Roo/form/HtmlEditor/ToolbarStandard.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404         var thread = null;
3405
3406
3407         var queue = [];
3408
3409
3410         var tweenCount = 0;
3411
3412
3413         this.fps = 1000;
3414
3415
3416         this.delay = 1;
3417
3418
3419         this.registerElement = function(tween) {
3420             queue[queue.length] = tween;
3421             tweenCount += 1;
3422             tween._onStart.fire();
3423             this.start();
3424         };
3425
3426
3427         this.unRegister = function(tween, index) {
3428             tween._onComplete.fire();
3429             index = index || getIndex(tween);
3430             if (index != -1) {
3431                 queue.splice(index, 1);
3432             }
3433
3434             tweenCount -= 1;
3435             if (tweenCount <= 0) {
3436                 this.stop();
3437             }
3438         };
3439
3440
3441         this.start = function() {
3442             if (thread === null) {
3443                 thread = setInterval(this.run, this.delay);
3444             }
3445         };
3446
3447
3448         this.stop = function(tween) {
3449             if (!tween) {
3450                 clearInterval(thread);
3451
3452                 for (var i = 0, len = queue.length; i < len; ++i) {
3453                     if (queue[0].isAnimated()) {
3454                         this.unRegister(queue[0], 0);
3455                     }
3456                 }
3457
3458                 queue = [];
3459                 thread = null;
3460                 tweenCount = 0;
3461             }
3462             else {
3463                 this.unRegister(tween);
3464             }
3465         };
3466
3467
3468         this.run = function() {
3469             for (var i = 0, len = queue.length; i < len; ++i) {
3470                 var tween = queue[i];
3471                 if (!tween || !tween.isAnimated()) {
3472                     continue;
3473                 }
3474
3475                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476                 {
3477                     tween.currentFrame += 1;
3478
3479                     if (tween.useSeconds) {
3480                         correctFrame(tween);
3481                     }
3482                     tween._onTween.fire();
3483                 }
3484                 else {
3485                     Roo.lib.AnimMgr.stop(tween, i);
3486                 }
3487             }
3488         };
3489
3490         var getIndex = function(anim) {
3491             for (var i = 0, len = queue.length; i < len; ++i) {
3492                 if (queue[i] == anim) {
3493                     return i;
3494                 }
3495             }
3496             return -1;
3497         };
3498
3499
3500         var correctFrame = function(tween) {
3501             var frames = tween.totalFrames;
3502             var frame = tween.currentFrame;
3503             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504             var elapsed = (new Date() - tween.getStartTime());
3505             var tweak = 0;
3506
3507             if (elapsed < tween.duration * 1000) {
3508                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509             } else {
3510                 tweak = frames - (frame + 1);
3511             }
3512             if (tweak > 0 && isFinite(tweak)) {
3513                 if (tween.currentFrame + tweak >= frames) {
3514                     tweak = frames - (frame + 1);
3515                 }
3516
3517                 tween.currentFrame += tweak;
3518             }
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 Roo.lib.Bezier = new function() {
3529
3530         this.getPosition = function(points, t) {
3531             var n = points.length;
3532             var tmp = [];
3533
3534             for (var i = 0; i < n; ++i) {
3535                 tmp[i] = [points[i][0], points[i][1]];
3536             }
3537
3538             for (var j = 1; j < n; ++j) {
3539                 for (i = 0; i < n - j; ++i) {
3540                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3541                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3542                 }
3543             }
3544
3545             return [ tmp[0][0], tmp[0][1] ];
3546
3547         };
3548     };/*
3549  * Portions of this file are based on pieces of Yahoo User Interface Library
3550  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3551  * YUI licensed under the BSD License:
3552  * http://developer.yahoo.net/yui/license.txt
3553  * <script type="text/javascript">
3554  *
3555  */
3556 (function() {
3557
3558     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3559         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3560     };
3561
3562     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3563
3564     var fly = Roo.lib.AnimBase.fly;
3565     var Y = Roo.lib;
3566     var superclass = Y.ColorAnim.superclass;
3567     var proto = Y.ColorAnim.prototype;
3568
3569     proto.toString = function() {
3570         var el = this.getEl();
3571         var id = el.id || el.tagName;
3572         return ("ColorAnim " + id);
3573     };
3574
3575     proto.patterns.color = /color$/i;
3576     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3577     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3578     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3579     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3580
3581
3582     proto.parseColor = function(s) {
3583         if (s.length == 3) {
3584             return s;
3585         }
3586
3587         var c = this.patterns.hex.exec(s);
3588         if (c && c.length == 4) {
3589             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3590         }
3591
3592         c = this.patterns.rgb.exec(s);
3593         if (c && c.length == 4) {
3594             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3595         }
3596
3597         c = this.patterns.hex3.exec(s);
3598         if (c && c.length == 4) {
3599             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3600         }
3601
3602         return null;
3603     };
3604     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3605     proto.getAttribute = function(attr) {
3606         var el = this.getEl();
3607         if (this.patterns.color.test(attr)) {
3608             var val = fly(el).getStyle(attr);
3609
3610             if (this.patterns.transparent.test(val)) {
3611                 var parent = el.parentNode;
3612                 val = fly(parent).getStyle(attr);
3613
3614                 while (parent && this.patterns.transparent.test(val)) {
3615                     parent = parent.parentNode;
3616                     val = fly(parent).getStyle(attr);
3617                     if (parent.tagName.toUpperCase() == 'HTML') {
3618                         val = '#fff';
3619                     }
3620                 }
3621             }
3622         } else {
3623             val = superclass.getAttribute.call(this, attr);
3624         }
3625
3626         return val;
3627     };
3628     proto.getAttribute = function(attr) {
3629         var el = this.getEl();
3630         if (this.patterns.color.test(attr)) {
3631             var val = fly(el).getStyle(attr);
3632
3633             if (this.patterns.transparent.test(val)) {
3634                 var parent = el.parentNode;
3635                 val = fly(parent).getStyle(attr);
3636
3637                 while (parent && this.patterns.transparent.test(val)) {
3638                     parent = parent.parentNode;
3639                     val = fly(parent).getStyle(attr);
3640                     if (parent.tagName.toUpperCase() == 'HTML') {
3641                         val = '#fff';
3642                     }
3643                 }
3644             }
3645         } else {
3646             val = superclass.getAttribute.call(this, attr);
3647         }
3648
3649         return val;
3650     };
3651
3652     proto.doMethod = function(attr, start, end) {
3653         var val;
3654
3655         if (this.patterns.color.test(attr)) {
3656             val = [];
3657             for (var i = 0, len = start.length; i < len; ++i) {
3658                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3659             }
3660
3661             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3662         }
3663         else {
3664             val = superclass.doMethod.call(this, attr, start, end);
3665         }
3666
3667         return val;
3668     };
3669
3670     proto.setRuntimeAttribute = function(attr) {
3671         superclass.setRuntimeAttribute.call(this, attr);
3672
3673         if (this.patterns.color.test(attr)) {
3674             var attributes = this.attributes;
3675             var start = this.parseColor(this.runtimeAttributes[attr].start);
3676             var end = this.parseColor(this.runtimeAttributes[attr].end);
3677
3678             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3679                 end = this.parseColor(attributes[attr].by);
3680
3681                 for (var i = 0, len = start.length; i < len; ++i) {
3682                     end[i] = start[i] + end[i];
3683                 }
3684             }
3685
3686             this.runtimeAttributes[attr].start = start;
3687             this.runtimeAttributes[attr].end = end;
3688         }
3689     };
3690 })();
3691
3692 /*
3693  * Portions of this file are based on pieces of Yahoo User Interface Library
3694  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3695  * YUI licensed under the BSD License:
3696  * http://developer.yahoo.net/yui/license.txt
3697  * <script type="text/javascript">
3698  *
3699  */
3700 Roo.lib.Easing = {
3701
3702
3703     easeNone: function (t, b, c, d) {
3704         return c * t / d + b;
3705     },
3706
3707
3708     easeIn: function (t, b, c, d) {
3709         return c * (t /= d) * t + b;
3710     },
3711
3712
3713     easeOut: function (t, b, c, d) {
3714         return -c * (t /= d) * (t - 2) + b;
3715     },
3716
3717
3718     easeBoth: function (t, b, c, d) {
3719         if ((t /= d / 2) < 1) {
3720             return c / 2 * t * t + b;
3721         }
3722
3723         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3724     },
3725
3726
3727     easeInStrong: function (t, b, c, d) {
3728         return c * (t /= d) * t * t * t + b;
3729     },
3730
3731
3732     easeOutStrong: function (t, b, c, d) {
3733         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3734     },
3735
3736
3737     easeBothStrong: function (t, b, c, d) {
3738         if ((t /= d / 2) < 1) {
3739             return c / 2 * t * t * t * t + b;
3740         }
3741
3742         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3743     },
3744
3745
3746
3747     elasticIn: function (t, b, c, d, a, p) {
3748         if (t == 0) {
3749             return b;
3750         }
3751         if ((t /= d) == 1) {
3752             return b + c;
3753         }
3754         if (!p) {
3755             p = d * .3;
3756         }
3757
3758         if (!a || a < Math.abs(c)) {
3759             a = c;
3760             var s = p / 4;
3761         }
3762         else {
3763             var s = p / (2 * Math.PI) * Math.asin(c / a);
3764         }
3765
3766         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3767     },
3768
3769
3770     elasticOut: function (t, b, c, d, a, p) {
3771         if (t == 0) {
3772             return b;
3773         }
3774         if ((t /= d) == 1) {
3775             return b + c;
3776         }
3777         if (!p) {
3778             p = d * .3;
3779         }
3780
3781         if (!a || a < Math.abs(c)) {
3782             a = c;
3783             var s = p / 4;
3784         }
3785         else {
3786             var s = p / (2 * Math.PI) * Math.asin(c / a);
3787         }
3788
3789         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3790     },
3791
3792
3793     elasticBoth: function (t, b, c, d, a, p) {
3794         if (t == 0) {
3795             return b;
3796         }
3797
3798         if ((t /= d / 2) == 2) {
3799             return b + c;
3800         }
3801
3802         if (!p) {
3803             p = d * (.3 * 1.5);
3804         }
3805
3806         if (!a || a < Math.abs(c)) {
3807             a = c;
3808             var s = p / 4;
3809         }
3810         else {
3811             var s = p / (2 * Math.PI) * Math.asin(c / a);
3812         }
3813
3814         if (t < 1) {
3815             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3816                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3817         }
3818         return a * Math.pow(2, -10 * (t -= 1)) *
3819                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3820     },
3821
3822
3823
3824     backIn: function (t, b, c, d, s) {
3825         if (typeof s == 'undefined') {
3826             s = 1.70158;
3827         }
3828         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3829     },
3830
3831
3832     backOut: function (t, b, c, d, s) {
3833         if (typeof s == 'undefined') {
3834             s = 1.70158;
3835         }
3836         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3837     },
3838
3839
3840     backBoth: function (t, b, c, d, s) {
3841         if (typeof s == 'undefined') {
3842             s = 1.70158;
3843         }
3844
3845         if ((t /= d / 2 ) < 1) {
3846             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3847         }
3848         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3849     },
3850
3851
3852     bounceIn: function (t, b, c, d) {
3853         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3854     },
3855
3856
3857     bounceOut: function (t, b, c, d) {
3858         if ((t /= d) < (1 / 2.75)) {
3859             return c * (7.5625 * t * t) + b;
3860         } else if (t < (2 / 2.75)) {
3861             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3862         } else if (t < (2.5 / 2.75)) {
3863             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3864         }
3865         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3866     },
3867
3868
3869     bounceBoth: function (t, b, c, d) {
3870         if (t < d / 2) {
3871             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3872         }
3873         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3874     }
3875 };/*
3876  * Portions of this file are based on pieces of Yahoo User Interface Library
3877  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3878  * YUI licensed under the BSD License:
3879  * http://developer.yahoo.net/yui/license.txt
3880  * <script type="text/javascript">
3881  *
3882  */
3883     (function() {
3884         Roo.lib.Motion = function(el, attributes, duration, method) {
3885             if (el) {
3886                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3887             }
3888         };
3889
3890         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3891
3892
3893         var Y = Roo.lib;
3894         var superclass = Y.Motion.superclass;
3895         var proto = Y.Motion.prototype;
3896
3897         proto.toString = function() {
3898             var el = this.getEl();
3899             var id = el.id || el.tagName;
3900             return ("Motion " + id);
3901         };
3902
3903         proto.patterns.points = /^points$/i;
3904
3905         proto.setAttribute = function(attr, val, unit) {
3906             if (this.patterns.points.test(attr)) {
3907                 unit = unit || 'px';
3908                 superclass.setAttribute.call(this, 'left', val[0], unit);
3909                 superclass.setAttribute.call(this, 'top', val[1], unit);
3910             } else {
3911                 superclass.setAttribute.call(this, attr, val, unit);
3912             }
3913         };
3914
3915         proto.getAttribute = function(attr) {
3916             if (this.patterns.points.test(attr)) {
3917                 var val = [
3918                         superclass.getAttribute.call(this, 'left'),
3919                         superclass.getAttribute.call(this, 'top')
3920                         ];
3921             } else {
3922                 val = superclass.getAttribute.call(this, attr);
3923             }
3924
3925             return val;
3926         };
3927
3928         proto.doMethod = function(attr, start, end) {
3929             var val = null;
3930
3931             if (this.patterns.points.test(attr)) {
3932                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3933                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3934             } else {
3935                 val = superclass.doMethod.call(this, attr, start, end);
3936             }
3937             return val;
3938         };
3939
3940         proto.setRuntimeAttribute = function(attr) {
3941             if (this.patterns.points.test(attr)) {
3942                 var el = this.getEl();
3943                 var attributes = this.attributes;
3944                 var start;
3945                 var control = attributes['points']['control'] || [];
3946                 var end;
3947                 var i, len;
3948
3949                 if (control.length > 0 && !(control[0] instanceof Array)) {
3950                     control = [control];
3951                 } else {
3952                     var tmp = [];
3953                     for (i = 0,len = control.length; i < len; ++i) {
3954                         tmp[i] = control[i];
3955                     }
3956                     control = tmp;
3957                 }
3958
3959                 Roo.fly(el).position();
3960
3961                 if (isset(attributes['points']['from'])) {
3962                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3963                 }
3964                 else {
3965                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3966                 }
3967
3968                 start = this.getAttribute('points');
3969
3970
3971                 if (isset(attributes['points']['to'])) {
3972                     end = translateValues.call(this, attributes['points']['to'], start);
3973
3974                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975                     for (i = 0,len = control.length; i < len; ++i) {
3976                         control[i] = translateValues.call(this, control[i], start);
3977                     }
3978
3979
3980                 } else if (isset(attributes['points']['by'])) {
3981                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3982
3983                     for (i = 0,len = control.length; i < len; ++i) {
3984                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3985                     }
3986                 }
3987
3988                 this.runtimeAttributes[attr] = [start];
3989
3990                 if (control.length > 0) {
3991                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3992                 }
3993
3994                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3995             }
3996             else {
3997                 superclass.setRuntimeAttribute.call(this, attr);
3998             }
3999         };
4000
4001         var translateValues = function(val, start) {
4002             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4003             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4004
4005             return val;
4006         };
4007
4008         var isset = function(prop) {
4009             return (typeof prop !== 'undefined');
4010         };
4011     })();
4012 /*
4013  * Portions of this file are based on pieces of Yahoo User Interface Library
4014  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4015  * YUI licensed under the BSD License:
4016  * http://developer.yahoo.net/yui/license.txt
4017  * <script type="text/javascript">
4018  *
4019  */
4020     (function() {
4021         Roo.lib.Scroll = function(el, attributes, duration, method) {
4022             if (el) {
4023                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4024             }
4025         };
4026
4027         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4028
4029
4030         var Y = Roo.lib;
4031         var superclass = Y.Scroll.superclass;
4032         var proto = Y.Scroll.prototype;
4033
4034         proto.toString = function() {
4035             var el = this.getEl();
4036             var id = el.id || el.tagName;
4037             return ("Scroll " + id);
4038         };
4039
4040         proto.doMethod = function(attr, start, end) {
4041             var val = null;
4042
4043             if (attr == 'scroll') {
4044                 val = [
4045                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4046                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4047                         ];
4048
4049             } else {
4050                 val = superclass.doMethod.call(this, attr, start, end);
4051             }
4052             return val;
4053         };
4054
4055         proto.getAttribute = function(attr) {
4056             var val = null;
4057             var el = this.getEl();
4058
4059             if (attr == 'scroll') {
4060                 val = [ el.scrollLeft, el.scrollTop ];
4061             } else {
4062                 val = superclass.getAttribute.call(this, attr);
4063             }
4064
4065             return val;
4066         };
4067
4068         proto.setAttribute = function(attr, val, unit) {
4069             var el = this.getEl();
4070
4071             if (attr == 'scroll') {
4072                 el.scrollLeft = val[0];
4073                 el.scrollTop = val[1];
4074             } else {
4075                 superclass.setAttribute.call(this, attr, val, unit);
4076             }
4077         };
4078     })();
4079 /*
4080  * Based on:
4081  * Ext JS Library 1.1.1
4082  * Copyright(c) 2006-2007, Ext JS, LLC.
4083  *
4084  * Originally Released Under LGPL - original licence link has changed is not relivant.
4085  *
4086  * Fork - LGPL
4087  * <script type="text/javascript">
4088  */
4089
4090
4091 // nasty IE9 hack - what a pile of crap that is..
4092
4093  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4094     Range.prototype.createContextualFragment = function (html) {
4095         var doc = window.document;
4096         var container = doc.createElement("div");
4097         container.innerHTML = html;
4098         var frag = doc.createDocumentFragment(), n;
4099         while ((n = container.firstChild)) {
4100             frag.appendChild(n);
4101         }
4102         return frag;
4103     };
4104 }
4105
4106 /**
4107  * @class Roo.DomHelper
4108  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4109  * 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>.
4110  * @singleton
4111  */
4112 Roo.DomHelper = function(){
4113     var tempTableEl = null;
4114     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4115     var tableRe = /^table|tbody|tr|td$/i;
4116     var xmlns = {};
4117     // build as innerHTML where available
4118     /** @ignore */
4119     var createHtml = function(o){
4120         if(typeof o == 'string'){
4121             return o;
4122         }
4123         var b = "";
4124         if(!o.tag){
4125             o.tag = "div";
4126         }
4127         b += "<" + o.tag;
4128         for(var attr in o){
4129             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4130             if(attr == "style"){
4131                 var s = o["style"];
4132                 if(typeof s == "function"){
4133                     s = s.call();
4134                 }
4135                 if(typeof s == "string"){
4136                     b += ' style="' + s + '"';
4137                 }else if(typeof s == "object"){
4138                     b += ' style="';
4139                     for(var key in s){
4140                         if(typeof s[key] != "function"){
4141                             b += key + ":" + s[key] + ";";
4142                         }
4143                     }
4144                     b += '"';
4145                 }
4146             }else{
4147                 if(attr == "cls"){
4148                     b += ' class="' + o["cls"] + '"';
4149                 }else if(attr == "htmlFor"){
4150                     b += ' for="' + o["htmlFor"] + '"';
4151                 }else{
4152                     b += " " + attr + '="' + o[attr] + '"';
4153                 }
4154             }
4155         }
4156         if(emptyTags.test(o.tag)){
4157             b += "/>";
4158         }else{
4159             b += ">";
4160             var cn = o.children || o.cn;
4161             if(cn){
4162                 //http://bugs.kde.org/show_bug.cgi?id=71506
4163                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4164                     for(var i = 0, len = cn.length; i < len; i++) {
4165                         b += createHtml(cn[i], b);
4166                     }
4167                 }else{
4168                     b += createHtml(cn, b);
4169                 }
4170             }
4171             if(o.html){
4172                 b += o.html;
4173             }
4174             b += "</" + o.tag + ">";
4175         }
4176         return b;
4177     };
4178
4179     // build as dom
4180     /** @ignore */
4181     var createDom = function(o, parentNode){
4182          
4183         // defininition craeted..
4184         var ns = false;
4185         if (o.ns && o.ns != 'html') {
4186                
4187             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4188                 xmlns[o.ns] = o.xmlns;
4189                 ns = o.xmlns;
4190             }
4191             if (typeof(xmlns[o.ns]) == 'undefined') {
4192                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4193             }
4194             ns = xmlns[o.ns];
4195         }
4196         
4197         
4198         if (typeof(o) == 'string') {
4199             return parentNode.appendChild(document.createTextNode(o));
4200         }
4201         o.tag = o.tag || div;
4202         if (o.ns && Roo.isIE) {
4203             ns = false;
4204             o.tag = o.ns + ':' + o.tag;
4205             
4206         }
4207         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4208         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4209         for(var attr in o){
4210             
4211             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4212                     attr == "style" || typeof o[attr] == "function") continue;
4213                     
4214             if(attr=="cls" && Roo.isIE){
4215                 el.className = o["cls"];
4216             }else{
4217                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4218                 else el[attr] = o[attr];
4219             }
4220         }
4221         Roo.DomHelper.applyStyles(el, o.style);
4222         var cn = o.children || o.cn;
4223         if(cn){
4224             //http://bugs.kde.org/show_bug.cgi?id=71506
4225              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4226                 for(var i = 0, len = cn.length; i < len; i++) {
4227                     createDom(cn[i], el);
4228                 }
4229             }else{
4230                 createDom(cn, el);
4231             }
4232         }
4233         if(o.html){
4234             el.innerHTML = o.html;
4235         }
4236         if(parentNode){
4237            parentNode.appendChild(el);
4238         }
4239         return el;
4240     };
4241
4242     var ieTable = function(depth, s, h, e){
4243         tempTableEl.innerHTML = [s, h, e].join('');
4244         var i = -1, el = tempTableEl;
4245         while(++i < depth){
4246             el = el.firstChild;
4247         }
4248         return el;
4249     };
4250
4251     // kill repeat to save bytes
4252     var ts = '<table>',
4253         te = '</table>',
4254         tbs = ts+'<tbody>',
4255         tbe = '</tbody>'+te,
4256         trs = tbs + '<tr>',
4257         tre = '</tr>'+tbe;
4258
4259     /**
4260      * @ignore
4261      * Nasty code for IE's broken table implementation
4262      */
4263     var insertIntoTable = function(tag, where, el, html){
4264         if(!tempTableEl){
4265             tempTableEl = document.createElement('div');
4266         }
4267         var node;
4268         var before = null;
4269         if(tag == 'td'){
4270             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4271                 return;
4272             }
4273             if(where == 'beforebegin'){
4274                 before = el;
4275                 el = el.parentNode;
4276             } else{
4277                 before = el.nextSibling;
4278                 el = el.parentNode;
4279             }
4280             node = ieTable(4, trs, html, tre);
4281         }
4282         else if(tag == 'tr'){
4283             if(where == 'beforebegin'){
4284                 before = el;
4285                 el = el.parentNode;
4286                 node = ieTable(3, tbs, html, tbe);
4287             } else if(where == 'afterend'){
4288                 before = el.nextSibling;
4289                 el = el.parentNode;
4290                 node = ieTable(3, tbs, html, tbe);
4291             } else{ // INTO a TR
4292                 if(where == 'afterbegin'){
4293                     before = el.firstChild;
4294                 }
4295                 node = ieTable(4, trs, html, tre);
4296             }
4297         } else if(tag == 'tbody'){
4298             if(where == 'beforebegin'){
4299                 before = el;
4300                 el = el.parentNode;
4301                 node = ieTable(2, ts, html, te);
4302             } else if(where == 'afterend'){
4303                 before = el.nextSibling;
4304                 el = el.parentNode;
4305                 node = ieTable(2, ts, html, te);
4306             } else{
4307                 if(where == 'afterbegin'){
4308                     before = el.firstChild;
4309                 }
4310                 node = ieTable(3, tbs, html, tbe);
4311             }
4312         } else{ // TABLE
4313             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4314                 return;
4315             }
4316             if(where == 'afterbegin'){
4317                 before = el.firstChild;
4318             }
4319             node = ieTable(2, ts, html, te);
4320         }
4321         el.insertBefore(node, before);
4322         return node;
4323     };
4324
4325     return {
4326     /** True to force the use of DOM instead of html fragments @type Boolean */
4327     useDom : false,
4328
4329     /**
4330      * Returns the markup for the passed Element(s) config
4331      * @param {Object} o The Dom object spec (and children)
4332      * @return {String}
4333      */
4334     markup : function(o){
4335         return createHtml(o);
4336     },
4337
4338     /**
4339      * Applies a style specification to an element
4340      * @param {String/HTMLElement} el The element to apply styles to
4341      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4342      * a function which returns such a specification.
4343      */
4344     applyStyles : function(el, styles){
4345         if(styles){
4346            el = Roo.fly(el);
4347            if(typeof styles == "string"){
4348                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4349                var matches;
4350                while ((matches = re.exec(styles)) != null){
4351                    el.setStyle(matches[1], matches[2]);
4352                }
4353            }else if (typeof styles == "object"){
4354                for (var style in styles){
4355                   el.setStyle(style, styles[style]);
4356                }
4357            }else if (typeof styles == "function"){
4358                 Roo.DomHelper.applyStyles(el, styles.call());
4359            }
4360         }
4361     },
4362
4363     /**
4364      * Inserts an HTML fragment into the Dom
4365      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4366      * @param {HTMLElement} el The context element
4367      * @param {String} html The HTML fragmenet
4368      * @return {HTMLElement} The new node
4369      */
4370     insertHtml : function(where, el, html){
4371         where = where.toLowerCase();
4372         if(el.insertAdjacentHTML){
4373             if(tableRe.test(el.tagName)){
4374                 var rs;
4375                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4376                     return rs;
4377                 }
4378             }
4379             switch(where){
4380                 case "beforebegin":
4381                     el.insertAdjacentHTML('BeforeBegin', html);
4382                     return el.previousSibling;
4383                 case "afterbegin":
4384                     el.insertAdjacentHTML('AfterBegin', html);
4385                     return el.firstChild;
4386                 case "beforeend":
4387                     el.insertAdjacentHTML('BeforeEnd', html);
4388                     return el.lastChild;
4389                 case "afterend":
4390                     el.insertAdjacentHTML('AfterEnd', html);
4391                     return el.nextSibling;
4392             }
4393             throw 'Illegal insertion point -> "' + where + '"';
4394         }
4395         var range = el.ownerDocument.createRange();
4396         var frag;
4397         switch(where){
4398              case "beforebegin":
4399                 range.setStartBefore(el);
4400                 frag = range.createContextualFragment(html);
4401                 el.parentNode.insertBefore(frag, el);
4402                 return el.previousSibling;
4403              case "afterbegin":
4404                 if(el.firstChild){
4405                     range.setStartBefore(el.firstChild);
4406                     frag = range.createContextualFragment(html);
4407                     el.insertBefore(frag, el.firstChild);
4408                     return el.firstChild;
4409                 }else{
4410                     el.innerHTML = html;
4411                     return el.firstChild;
4412                 }
4413             case "beforeend":
4414                 if(el.lastChild){
4415                     range.setStartAfter(el.lastChild);
4416                     frag = range.createContextualFragment(html);
4417                     el.appendChild(frag);
4418                     return el.lastChild;
4419                 }else{
4420                     el.innerHTML = html;
4421                     return el.lastChild;
4422                 }
4423             case "afterend":
4424                 range.setStartAfter(el);
4425                 frag = range.createContextualFragment(html);
4426                 el.parentNode.insertBefore(frag, el.nextSibling);
4427                 return el.nextSibling;
4428             }
4429             throw 'Illegal insertion point -> "' + where + '"';
4430     },
4431
4432     /**
4433      * Creates new Dom element(s) and inserts them before el
4434      * @param {String/HTMLElement/Element} el The context element
4435      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4436      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4437      * @return {HTMLElement/Roo.Element} The new node
4438      */
4439     insertBefore : function(el, o, returnElement){
4440         return this.doInsert(el, o, returnElement, "beforeBegin");
4441     },
4442
4443     /**
4444      * Creates new Dom element(s) and inserts them after el
4445      * @param {String/HTMLElement/Element} el The context element
4446      * @param {Object} o The Dom object spec (and children)
4447      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4448      * @return {HTMLElement/Roo.Element} The new node
4449      */
4450     insertAfter : function(el, o, returnElement){
4451         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4452     },
4453
4454     /**
4455      * Creates new Dom element(s) and inserts them as the first child of el
4456      * @param {String/HTMLElement/Element} el The context element
4457      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4458      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4459      * @return {HTMLElement/Roo.Element} The new node
4460      */
4461     insertFirst : function(el, o, returnElement){
4462         return this.doInsert(el, o, returnElement, "afterBegin");
4463     },
4464
4465     // private
4466     doInsert : function(el, o, returnElement, pos, sibling){
4467         el = Roo.getDom(el);
4468         var newNode;
4469         if(this.useDom || o.ns){
4470             newNode = createDom(o, null);
4471             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4472         }else{
4473             var html = createHtml(o);
4474             newNode = this.insertHtml(pos, el, html);
4475         }
4476         return returnElement ? Roo.get(newNode, true) : newNode;
4477     },
4478
4479     /**
4480      * Creates new Dom element(s) and appends them to el
4481      * @param {String/HTMLElement/Element} el The context element
4482      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4483      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4484      * @return {HTMLElement/Roo.Element} The new node
4485      */
4486     append : function(el, o, returnElement){
4487         el = Roo.getDom(el);
4488         var newNode;
4489         if(this.useDom || o.ns){
4490             newNode = createDom(o, null);
4491             el.appendChild(newNode);
4492         }else{
4493             var html = createHtml(o);
4494             newNode = this.insertHtml("beforeEnd", el, html);
4495         }
4496         return returnElement ? Roo.get(newNode, true) : newNode;
4497     },
4498
4499     /**
4500      * Creates new Dom element(s) and overwrites the contents of el with them
4501      * @param {String/HTMLElement/Element} el The context element
4502      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4503      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4504      * @return {HTMLElement/Roo.Element} The new node
4505      */
4506     overwrite : function(el, o, returnElement){
4507         el = Roo.getDom(el);
4508         if (o.ns) {
4509           
4510             while (el.childNodes.length) {
4511                 el.removeChild(el.firstChild);
4512             }
4513             createDom(o, el);
4514         } else {
4515             el.innerHTML = createHtml(o);   
4516         }
4517         
4518         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4519     },
4520
4521     /**
4522      * Creates a new Roo.DomHelper.Template from the Dom object spec
4523      * @param {Object} o The Dom object spec (and children)
4524      * @return {Roo.DomHelper.Template} The new template
4525      */
4526     createTemplate : function(o){
4527         var html = createHtml(o);
4528         return new Roo.Template(html);
4529     }
4530     };
4531 }();
4532 /*
4533  * Based on:
4534  * Ext JS Library 1.1.1
4535  * Copyright(c) 2006-2007, Ext JS, LLC.
4536  *
4537  * Originally Released Under LGPL - original licence link has changed is not relivant.
4538  *
4539  * Fork - LGPL
4540  * <script type="text/javascript">
4541  */
4542  
4543 /**
4544 * @class Roo.Template
4545 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4546 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4547 * Usage:
4548 <pre><code>
4549 var t = new Roo.Template({
4550     html :  '&lt;div name="{id}"&gt;' + 
4551         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4552         '&lt;/div&gt;',
4553     myformat: function (value, allValues) {
4554         return 'XX' + value;
4555     }
4556 });
4557 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4558 </code></pre>
4559 * For more information see this blog post with examples:
4560 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4561      - Create Elements using DOM, HTML fragments and Templates</a>. 
4562 * @constructor
4563 * @param {Object} cfg - Configuration object.
4564 */
4565 Roo.Template = function(cfg){
4566     // BC!
4567     if(cfg instanceof Array){
4568         cfg = cfg.join("");
4569     }else if(arguments.length > 1){
4570         cfg = Array.prototype.join.call(arguments, "");
4571     }
4572     
4573     
4574     if (typeof(cfg) == 'object') {
4575         Roo.apply(this,cfg)
4576     } else {
4577         // bc
4578         this.html = cfg;
4579     }
4580     if (this.url) {
4581         this.load();
4582     }
4583     
4584 };
4585 Roo.Template.prototype = {
4586     
4587     /**
4588      * @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..
4589      *                    it should be fixed so that template is observable...
4590      */
4591     url : false,
4592     /**
4593      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4594      */
4595     html : '',
4596     /**
4597      * Returns an HTML fragment of this template with the specified values applied.
4598      * @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'})
4599      * @return {String} The HTML fragment
4600      */
4601     applyTemplate : function(values){
4602         try {
4603            
4604             if(this.compiled){
4605                 return this.compiled(values);
4606             }
4607             var useF = this.disableFormats !== true;
4608             var fm = Roo.util.Format, tpl = this;
4609             var fn = function(m, name, format, args){
4610                 if(format && useF){
4611                     if(format.substr(0, 5) == "this."){
4612                         return tpl.call(format.substr(5), values[name], values);
4613                     }else{
4614                         if(args){
4615                             // quoted values are required for strings in compiled templates, 
4616                             // but for non compiled we need to strip them
4617                             // quoted reversed for jsmin
4618                             var re = /^\s*['"](.*)["']\s*$/;
4619                             args = args.split(',');
4620                             for(var i = 0, len = args.length; i < len; i++){
4621                                 args[i] = args[i].replace(re, "$1");
4622                             }
4623                             args = [values[name]].concat(args);
4624                         }else{
4625                             args = [values[name]];
4626                         }
4627                         return fm[format].apply(fm, args);
4628                     }
4629                 }else{
4630                     return values[name] !== undefined ? values[name] : "";
4631                 }
4632             };
4633             return this.html.replace(this.re, fn);
4634         } catch (e) {
4635             Roo.log(e);
4636             throw e;
4637         }
4638          
4639     },
4640     
4641     loading : false,
4642       
4643     load : function ()
4644     {
4645          
4646         if (this.loading) {
4647             return;
4648         }
4649         var _t = this;
4650         
4651         this.loading = true;
4652         this.compiled = false;
4653         
4654         var cx = new Roo.data.Connection();
4655         cx.request({
4656             url : this.url,
4657             method : 'GET',
4658             success : function (response) {
4659                 _t.loading = false;
4660                 _t.html = response.responseText;
4661                 _t.url = false;
4662                 _t.compile();
4663              },
4664             failure : function(response) {
4665                 Roo.log("Template failed to load from " + _t.url);
4666                 _t.loading = false;
4667             }
4668         });
4669     },
4670
4671     /**
4672      * Sets the HTML used as the template and optionally compiles it.
4673      * @param {String} html
4674      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4675      * @return {Roo.Template} this
4676      */
4677     set : function(html, compile){
4678         this.html = html;
4679         this.compiled = null;
4680         if(compile){
4681             this.compile();
4682         }
4683         return this;
4684     },
4685     
4686     /**
4687      * True to disable format functions (defaults to false)
4688      * @type Boolean
4689      */
4690     disableFormats : false,
4691     
4692     /**
4693     * The regular expression used to match template variables 
4694     * @type RegExp
4695     * @property 
4696     */
4697     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4698     
4699     /**
4700      * Compiles the template into an internal function, eliminating the RegEx overhead.
4701      * @return {Roo.Template} this
4702      */
4703     compile : function(){
4704         var fm = Roo.util.Format;
4705         var useF = this.disableFormats !== true;
4706         var sep = Roo.isGecko ? "+" : ",";
4707         var fn = function(m, name, format, args){
4708             if(format && useF){
4709                 args = args ? ',' + args : "";
4710                 if(format.substr(0, 5) != "this."){
4711                     format = "fm." + format + '(';
4712                 }else{
4713                     format = 'this.call("'+ format.substr(5) + '", ';
4714                     args = ", values";
4715                 }
4716             }else{
4717                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4718             }
4719             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4720         };
4721         var body;
4722         // branched to use + in gecko and [].join() in others
4723         if(Roo.isGecko){
4724             body = "this.compiled = function(values){ return '" +
4725                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4726                     "';};";
4727         }else{
4728             body = ["this.compiled = function(values){ return ['"];
4729             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4730             body.push("'].join('');};");
4731             body = body.join('');
4732         }
4733         /**
4734          * eval:var:values
4735          * eval:var:fm
4736          */
4737         eval(body);
4738         return this;
4739     },
4740     
4741     // private function used to call members
4742     call : function(fnName, value, allValues){
4743         return this[fnName](value, allValues);
4744     },
4745     
4746     /**
4747      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4748      * @param {String/HTMLElement/Roo.Element} el The context element
4749      * @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'})
4750      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4751      * @return {HTMLElement/Roo.Element} The new node or Element
4752      */
4753     insertFirst: function(el, values, returnElement){
4754         return this.doInsert('afterBegin', el, values, returnElement);
4755     },
4756
4757     /**
4758      * Applies the supplied values to the template and inserts the new node(s) before el.
4759      * @param {String/HTMLElement/Roo.Element} el The context element
4760      * @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'})
4761      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4762      * @return {HTMLElement/Roo.Element} The new node or Element
4763      */
4764     insertBefore: function(el, values, returnElement){
4765         return this.doInsert('beforeBegin', el, values, returnElement);
4766     },
4767
4768     /**
4769      * Applies the supplied values to the template and inserts the new node(s) after el.
4770      * @param {String/HTMLElement/Roo.Element} el The context element
4771      * @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'})
4772      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4773      * @return {HTMLElement/Roo.Element} The new node or Element
4774      */
4775     insertAfter : function(el, values, returnElement){
4776         return this.doInsert('afterEnd', el, values, returnElement);
4777     },
4778     
4779     /**
4780      * Applies the supplied values to the template and appends the new node(s) to el.
4781      * @param {String/HTMLElement/Roo.Element} el The context element
4782      * @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'})
4783      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4784      * @return {HTMLElement/Roo.Element} The new node or Element
4785      */
4786     append : function(el, values, returnElement){
4787         return this.doInsert('beforeEnd', el, values, returnElement);
4788     },
4789
4790     doInsert : function(where, el, values, returnEl){
4791         el = Roo.getDom(el);
4792         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4793         return returnEl ? Roo.get(newNode, true) : newNode;
4794     },
4795
4796     /**
4797      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4798      * @param {String/HTMLElement/Roo.Element} el The context element
4799      * @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'})
4800      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4801      * @return {HTMLElement/Roo.Element} The new node or Element
4802      */
4803     overwrite : function(el, values, returnElement){
4804         el = Roo.getDom(el);
4805         el.innerHTML = this.applyTemplate(values);
4806         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4807     }
4808 };
4809 /**
4810  * Alias for {@link #applyTemplate}
4811  * @method
4812  */
4813 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4814
4815 // backwards compat
4816 Roo.DomHelper.Template = Roo.Template;
4817
4818 /**
4819  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4820  * @param {String/HTMLElement} el A DOM element or its id
4821  * @returns {Roo.Template} The created template
4822  * @static
4823  */
4824 Roo.Template.from = function(el){
4825     el = Roo.getDom(el);
4826     return new Roo.Template(el.value || el.innerHTML);
4827 };/*
4828  * Based on:
4829  * Ext JS Library 1.1.1
4830  * Copyright(c) 2006-2007, Ext JS, LLC.
4831  *
4832  * Originally Released Under LGPL - original licence link has changed is not relivant.
4833  *
4834  * Fork - LGPL
4835  * <script type="text/javascript">
4836  */
4837  
4838
4839 /*
4840  * This is code is also distributed under MIT license for use
4841  * with jQuery and prototype JavaScript libraries.
4842  */
4843 /**
4844  * @class Roo.DomQuery
4845 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).
4846 <p>
4847 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>
4848
4849 <p>
4850 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.
4851 </p>
4852 <h4>Element Selectors:</h4>
4853 <ul class="list">
4854     <li> <b>*</b> any element</li>
4855     <li> <b>E</b> an element with the tag E</li>
4856     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4857     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4858     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4859     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4860 </ul>
4861 <h4>Attribute Selectors:</h4>
4862 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4863 <ul class="list">
4864     <li> <b>E[foo]</b> has an attribute "foo"</li>
4865     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4866     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4867     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4868     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4869     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4870     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4871 </ul>
4872 <h4>Pseudo Classes:</h4>
4873 <ul class="list">
4874     <li> <b>E:first-child</b> E is the first child of its parent</li>
4875     <li> <b>E:last-child</b> E is the last child of its parent</li>
4876     <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>
4877     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4878     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4879     <li> <b>E:only-child</b> E is the only child of its parent</li>
4880     <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>
4881     <li> <b>E:first</b> the first E in the resultset</li>
4882     <li> <b>E:last</b> the last E in the resultset</li>
4883     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4884     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4885     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4886     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4887     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4888     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4889     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4890     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4891     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4892 </ul>
4893 <h4>CSS Value Selectors:</h4>
4894 <ul class="list">
4895     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4896     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4897     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4898     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4899     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4900     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4901 </ul>
4902  * @singleton
4903  */
4904 Roo.DomQuery = function(){
4905     var cache = {}, simpleCache = {}, valueCache = {};
4906     var nonSpace = /\S/;
4907     var trimRe = /^\s+|\s+$/g;
4908     var tplRe = /\{(\d+)\}/g;
4909     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4910     var tagTokenRe = /^(#)?([\w-\*]+)/;
4911     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4912
4913     function child(p, index){
4914         var i = 0;
4915         var n = p.firstChild;
4916         while(n){
4917             if(n.nodeType == 1){
4918                if(++i == index){
4919                    return n;
4920                }
4921             }
4922             n = n.nextSibling;
4923         }
4924         return null;
4925     };
4926
4927     function next(n){
4928         while((n = n.nextSibling) && n.nodeType != 1);
4929         return n;
4930     };
4931
4932     function prev(n){
4933         while((n = n.previousSibling) && n.nodeType != 1);
4934         return n;
4935     };
4936
4937     function children(d){
4938         var n = d.firstChild, ni = -1;
4939             while(n){
4940                 var nx = n.nextSibling;
4941                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4942                     d.removeChild(n);
4943                 }else{
4944                     n.nodeIndex = ++ni;
4945                 }
4946                 n = nx;
4947             }
4948             return this;
4949         };
4950
4951     function byClassName(c, a, v){
4952         if(!v){
4953             return c;
4954         }
4955         var r = [], ri = -1, cn;
4956         for(var i = 0, ci; ci = c[i]; i++){
4957             if((' '+ci.className+' ').indexOf(v) != -1){
4958                 r[++ri] = ci;
4959             }
4960         }
4961         return r;
4962     };
4963
4964     function attrValue(n, attr){
4965         if(!n.tagName && typeof n.length != "undefined"){
4966             n = n[0];
4967         }
4968         if(!n){
4969             return null;
4970         }
4971         if(attr == "for"){
4972             return n.htmlFor;
4973         }
4974         if(attr == "class" || attr == "className"){
4975             return n.className;
4976         }
4977         return n.getAttribute(attr) || n[attr];
4978
4979     };
4980
4981     function getNodes(ns, mode, tagName){
4982         var result = [], ri = -1, cs;
4983         if(!ns){
4984             return result;
4985         }
4986         tagName = tagName || "*";
4987         if(typeof ns.getElementsByTagName != "undefined"){
4988             ns = [ns];
4989         }
4990         if(!mode){
4991             for(var i = 0, ni; ni = ns[i]; i++){
4992                 cs = ni.getElementsByTagName(tagName);
4993                 for(var j = 0, ci; ci = cs[j]; j++){
4994                     result[++ri] = ci;
4995                 }
4996             }
4997         }else if(mode == "/" || mode == ">"){
4998             var utag = tagName.toUpperCase();
4999             for(var i = 0, ni, cn; ni = ns[i]; i++){
5000                 cn = ni.children || ni.childNodes;
5001                 for(var j = 0, cj; cj = cn[j]; j++){
5002                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5003                         result[++ri] = cj;
5004                     }
5005                 }
5006             }
5007         }else if(mode == "+"){
5008             var utag = tagName.toUpperCase();
5009             for(var i = 0, n; n = ns[i]; i++){
5010                 while((n = n.nextSibling) && n.nodeType != 1);
5011                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5012                     result[++ri] = n;
5013                 }
5014             }
5015         }else if(mode == "~"){
5016             for(var i = 0, n; n = ns[i]; i++){
5017                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5018                 if(n){
5019                     result[++ri] = n;
5020                 }
5021             }
5022         }
5023         return result;
5024     };
5025
5026     function concat(a, b){
5027         if(b.slice){
5028             return a.concat(b);
5029         }
5030         for(var i = 0, l = b.length; i < l; i++){
5031             a[a.length] = b[i];
5032         }
5033         return a;
5034     }
5035
5036     function byTag(cs, tagName){
5037         if(cs.tagName || cs == document){
5038             cs = [cs];
5039         }
5040         if(!tagName){
5041             return cs;
5042         }
5043         var r = [], ri = -1;
5044         tagName = tagName.toLowerCase();
5045         for(var i = 0, ci; ci = cs[i]; i++){
5046             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5047                 r[++ri] = ci;
5048             }
5049         }
5050         return r;
5051     };
5052
5053     function byId(cs, attr, id){
5054         if(cs.tagName || cs == document){
5055             cs = [cs];
5056         }
5057         if(!id){
5058             return cs;
5059         }
5060         var r = [], ri = -1;
5061         for(var i = 0,ci; ci = cs[i]; i++){
5062             if(ci && ci.id == id){
5063                 r[++ri] = ci;
5064                 return r;
5065             }
5066         }
5067         return r;
5068     };
5069
5070     function byAttribute(cs, attr, value, op, custom){
5071         var r = [], ri = -1, st = custom=="{";
5072         var f = Roo.DomQuery.operators[op];
5073         for(var i = 0, ci; ci = cs[i]; i++){
5074             var a;
5075             if(st){
5076                 a = Roo.DomQuery.getStyle(ci, attr);
5077             }
5078             else if(attr == "class" || attr == "className"){
5079                 a = ci.className;
5080             }else if(attr == "for"){
5081                 a = ci.htmlFor;
5082             }else if(attr == "href"){
5083                 a = ci.getAttribute("href", 2);
5084             }else{
5085                 a = ci.getAttribute(attr);
5086             }
5087             if((f && f(a, value)) || (!f && a)){
5088                 r[++ri] = ci;
5089             }
5090         }
5091         return r;
5092     };
5093
5094     function byPseudo(cs, name, value){
5095         return Roo.DomQuery.pseudos[name](cs, value);
5096     };
5097
5098     // This is for IE MSXML which does not support expandos.
5099     // IE runs the same speed using setAttribute, however FF slows way down
5100     // and Safari completely fails so they need to continue to use expandos.
5101     var isIE = window.ActiveXObject ? true : false;
5102
5103     // this eval is stop the compressor from
5104     // renaming the variable to something shorter
5105     
5106     /** eval:var:batch */
5107     var batch = 30803; 
5108
5109     var key = 30803;
5110
5111     function nodupIEXml(cs){
5112         var d = ++key;
5113         cs[0].setAttribute("_nodup", d);
5114         var r = [cs[0]];
5115         for(var i = 1, len = cs.length; i < len; i++){
5116             var c = cs[i];
5117             if(!c.getAttribute("_nodup") != d){
5118                 c.setAttribute("_nodup", d);
5119                 r[r.length] = c;
5120             }
5121         }
5122         for(var i = 0, len = cs.length; i < len; i++){
5123             cs[i].removeAttribute("_nodup");
5124         }
5125         return r;
5126     }
5127
5128     function nodup(cs){
5129         if(!cs){
5130             return [];
5131         }
5132         var len = cs.length, c, i, r = cs, cj, ri = -1;
5133         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5134             return cs;
5135         }
5136         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5137             return nodupIEXml(cs);
5138         }
5139         var d = ++key;
5140         cs[0]._nodup = d;
5141         for(i = 1; c = cs[i]; i++){
5142             if(c._nodup != d){
5143                 c._nodup = d;
5144             }else{
5145                 r = [];
5146                 for(var j = 0; j < i; j++){
5147                     r[++ri] = cs[j];
5148                 }
5149                 for(j = i+1; cj = cs[j]; j++){
5150                     if(cj._nodup != d){
5151                         cj._nodup = d;
5152                         r[++ri] = cj;
5153                     }
5154                 }
5155                 return r;
5156             }
5157         }
5158         return r;
5159     }
5160
5161     function quickDiffIEXml(c1, c2){
5162         var d = ++key;
5163         for(var i = 0, len = c1.length; i < len; i++){
5164             c1[i].setAttribute("_qdiff", d);
5165         }
5166         var r = [];
5167         for(var i = 0, len = c2.length; i < len; i++){
5168             if(c2[i].getAttribute("_qdiff") != d){
5169                 r[r.length] = c2[i];
5170             }
5171         }
5172         for(var i = 0, len = c1.length; i < len; i++){
5173            c1[i].removeAttribute("_qdiff");
5174         }
5175         return r;
5176     }
5177
5178     function quickDiff(c1, c2){
5179         var len1 = c1.length;
5180         if(!len1){
5181             return c2;
5182         }
5183         if(isIE && c1[0].selectSingleNode){
5184             return quickDiffIEXml(c1, c2);
5185         }
5186         var d = ++key;
5187         for(var i = 0; i < len1; i++){
5188             c1[i]._qdiff = d;
5189         }
5190         var r = [];
5191         for(var i = 0, len = c2.length; i < len; i++){
5192             if(c2[i]._qdiff != d){
5193                 r[r.length] = c2[i];
5194             }
5195         }
5196         return r;
5197     }
5198
5199     function quickId(ns, mode, root, id){
5200         if(ns == root){
5201            var d = root.ownerDocument || root;
5202            return d.getElementById(id);
5203         }
5204         ns = getNodes(ns, mode, "*");
5205         return byId(ns, null, id);
5206     }
5207
5208     return {
5209         getStyle : function(el, name){
5210             return Roo.fly(el).getStyle(name);
5211         },
5212         /**
5213          * Compiles a selector/xpath query into a reusable function. The returned function
5214          * takes one parameter "root" (optional), which is the context node from where the query should start.
5215          * @param {String} selector The selector/xpath query
5216          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5217          * @return {Function}
5218          */
5219         compile : function(path, type){
5220             type = type || "select";
5221             
5222             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5223             var q = path, mode, lq;
5224             var tk = Roo.DomQuery.matchers;
5225             var tklen = tk.length;
5226             var mm;
5227
5228             // accept leading mode switch
5229             var lmode = q.match(modeRe);
5230             if(lmode && lmode[1]){
5231                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5232                 q = q.replace(lmode[1], "");
5233             }
5234             // strip leading slashes
5235             while(path.substr(0, 1)=="/"){
5236                 path = path.substr(1);
5237             }
5238
5239             while(q && lq != q){
5240                 lq = q;
5241                 var tm = q.match(tagTokenRe);
5242                 if(type == "select"){
5243                     if(tm){
5244                         if(tm[1] == "#"){
5245                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5246                         }else{
5247                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5248                         }
5249                         q = q.replace(tm[0], "");
5250                     }else if(q.substr(0, 1) != '@'){
5251                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5252                     }
5253                 }else{
5254                     if(tm){
5255                         if(tm[1] == "#"){
5256                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5257                         }else{
5258                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5259                         }
5260                         q = q.replace(tm[0], "");
5261                     }
5262                 }
5263                 while(!(mm = q.match(modeRe))){
5264                     var matched = false;
5265                     for(var j = 0; j < tklen; j++){
5266                         var t = tk[j];
5267                         var m = q.match(t.re);
5268                         if(m){
5269                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5270                                                     return m[i];
5271                                                 });
5272                             q = q.replace(m[0], "");
5273                             matched = true;
5274                             break;
5275                         }
5276                     }
5277                     // prevent infinite loop on bad selector
5278                     if(!matched){
5279                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5280                     }
5281                 }
5282                 if(mm[1]){
5283                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5284                     q = q.replace(mm[1], "");
5285                 }
5286             }
5287             fn[fn.length] = "return nodup(n);\n}";
5288             
5289              /** 
5290               * list of variables that need from compression as they are used by eval.
5291              *  eval:var:batch 
5292              *  eval:var:nodup
5293              *  eval:var:byTag
5294              *  eval:var:ById
5295              *  eval:var:getNodes
5296              *  eval:var:quickId
5297              *  eval:var:mode
5298              *  eval:var:root
5299              *  eval:var:n
5300              *  eval:var:byClassName
5301              *  eval:var:byPseudo
5302              *  eval:var:byAttribute
5303              *  eval:var:attrValue
5304              * 
5305              **/ 
5306             eval(fn.join(""));
5307             return f;
5308         },
5309
5310         /**
5311          * Selects a group of elements.
5312          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5313          * @param {Node} root (optional) The start of the query (defaults to document).
5314          * @return {Array}
5315          */
5316         select : function(path, root, type){
5317             if(!root || root == document){
5318                 root = document;
5319             }
5320             if(typeof root == "string"){
5321                 root = document.getElementById(root);
5322             }
5323             var paths = path.split(",");
5324             var results = [];
5325             for(var i = 0, len = paths.length; i < len; i++){
5326                 var p = paths[i].replace(trimRe, "");
5327                 if(!cache[p]){
5328                     cache[p] = Roo.DomQuery.compile(p);
5329                     if(!cache[p]){
5330                         throw p + " is not a valid selector";
5331                     }
5332                 }
5333                 var result = cache[p](root);
5334                 if(result && result != document){
5335                     results = results.concat(result);
5336                 }
5337             }
5338             if(paths.length > 1){
5339                 return nodup(results);
5340             }
5341             return results;
5342         },
5343
5344         /**
5345          * Selects a single element.
5346          * @param {String} selector The selector/xpath query
5347          * @param {Node} root (optional) The start of the query (defaults to document).
5348          * @return {Element}
5349          */
5350         selectNode : function(path, root){
5351             return Roo.DomQuery.select(path, root)[0];
5352         },
5353
5354         /**
5355          * Selects the value of a node, optionally replacing null with the defaultValue.
5356          * @param {String} selector The selector/xpath query
5357          * @param {Node} root (optional) The start of the query (defaults to document).
5358          * @param {String} defaultValue
5359          */
5360         selectValue : function(path, root, defaultValue){
5361             path = path.replace(trimRe, "");
5362             if(!valueCache[path]){
5363                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5364             }
5365             var n = valueCache[path](root);
5366             n = n[0] ? n[0] : n;
5367             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5368             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5369         },
5370
5371         /**
5372          * Selects the value of a node, parsing integers and floats.
5373          * @param {String} selector The selector/xpath query
5374          * @param {Node} root (optional) The start of the query (defaults to document).
5375          * @param {Number} defaultValue
5376          * @return {Number}
5377          */
5378         selectNumber : function(path, root, defaultValue){
5379             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5380             return parseFloat(v);
5381         },
5382
5383         /**
5384          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5385          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5386          * @param {String} selector The simple selector to test
5387          * @return {Boolean}
5388          */
5389         is : function(el, ss){
5390             if(typeof el == "string"){
5391                 el = document.getElementById(el);
5392             }
5393             var isArray = (el instanceof Array);
5394             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5395             return isArray ? (result.length == el.length) : (result.length > 0);
5396         },
5397
5398         /**
5399          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5400          * @param {Array} el An array of elements to filter
5401          * @param {String} selector The simple selector to test
5402          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5403          * the selector instead of the ones that match
5404          * @return {Array}
5405          */
5406         filter : function(els, ss, nonMatches){
5407             ss = ss.replace(trimRe, "");
5408             if(!simpleCache[ss]){
5409                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5410             }
5411             var result = simpleCache[ss](els);
5412             return nonMatches ? quickDiff(result, els) : result;
5413         },
5414
5415         /**
5416          * Collection of matching regular expressions and code snippets.
5417          */
5418         matchers : [{
5419                 re: /^\.([\w-]+)/,
5420                 select: 'n = byClassName(n, null, " {1} ");'
5421             }, {
5422                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5423                 select: 'n = byPseudo(n, "{1}", "{2}");'
5424             },{
5425                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5426                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5427             }, {
5428                 re: /^#([\w-]+)/,
5429                 select: 'n = byId(n, null, "{1}");'
5430             },{
5431                 re: /^@([\w-]+)/,
5432                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5433             }
5434         ],
5435
5436         /**
5437          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5438          * 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;.
5439          */
5440         operators : {
5441             "=" : function(a, v){
5442                 return a == v;
5443             },
5444             "!=" : function(a, v){
5445                 return a != v;
5446             },
5447             "^=" : function(a, v){
5448                 return a && a.substr(0, v.length) == v;
5449             },
5450             "$=" : function(a, v){
5451                 return a && a.substr(a.length-v.length) == v;
5452             },
5453             "*=" : function(a, v){
5454                 return a && a.indexOf(v) !== -1;
5455             },
5456             "%=" : function(a, v){
5457                 return (a % v) == 0;
5458             },
5459             "|=" : function(a, v){
5460                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5461             },
5462             "~=" : function(a, v){
5463                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5464             }
5465         },
5466
5467         /**
5468          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5469          * and the argument (if any) supplied in the selector.
5470          */
5471         pseudos : {
5472             "first-child" : function(c){
5473                 var r = [], ri = -1, n;
5474                 for(var i = 0, ci; ci = n = c[i]; i++){
5475                     while((n = n.previousSibling) && n.nodeType != 1);
5476                     if(!n){
5477                         r[++ri] = ci;
5478                     }
5479                 }
5480                 return r;
5481             },
5482
5483             "last-child" : function(c){
5484                 var r = [], ri = -1, n;
5485                 for(var i = 0, ci; ci = n = c[i]; i++){
5486                     while((n = n.nextSibling) && n.nodeType != 1);
5487                     if(!n){
5488                         r[++ri] = ci;
5489                     }
5490                 }
5491                 return r;
5492             },
5493
5494             "nth-child" : function(c, a) {
5495                 var r = [], ri = -1;
5496                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5497                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5498                 for(var i = 0, n; n = c[i]; i++){
5499                     var pn = n.parentNode;
5500                     if (batch != pn._batch) {
5501                         var j = 0;
5502                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5503                             if(cn.nodeType == 1){
5504                                cn.nodeIndex = ++j;
5505                             }
5506                         }
5507                         pn._batch = batch;
5508                     }
5509                     if (f == 1) {
5510                         if (l == 0 || n.nodeIndex == l){
5511                             r[++ri] = n;
5512                         }
5513                     } else if ((n.nodeIndex + l) % f == 0){
5514                         r[++ri] = n;
5515                     }
5516                 }
5517
5518                 return r;
5519             },
5520
5521             "only-child" : function(c){
5522                 var r = [], ri = -1;;
5523                 for(var i = 0, ci; ci = c[i]; i++){
5524                     if(!prev(ci) && !next(ci)){
5525                         r[++ri] = ci;
5526                     }
5527                 }
5528                 return r;
5529             },
5530
5531             "empty" : function(c){
5532                 var r = [], ri = -1;
5533                 for(var i = 0, ci; ci = c[i]; i++){
5534                     var cns = ci.childNodes, j = 0, cn, empty = true;
5535                     while(cn = cns[j]){
5536                         ++j;
5537                         if(cn.nodeType == 1 || cn.nodeType == 3){
5538                             empty = false;
5539                             break;
5540                         }
5541                     }
5542                     if(empty){
5543                         r[++ri] = ci;
5544                     }
5545                 }
5546                 return r;
5547             },
5548
5549             "contains" : function(c, v){
5550                 var r = [], ri = -1;
5551                 for(var i = 0, ci; ci = c[i]; i++){
5552                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "nodeValue" : function(c, v){
5560                 var r = [], ri = -1;
5561                 for(var i = 0, ci; ci = c[i]; i++){
5562                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5563                         r[++ri] = ci;
5564                     }
5565                 }
5566                 return r;
5567             },
5568
5569             "checked" : function(c){
5570                 var r = [], ri = -1;
5571                 for(var i = 0, ci; ci = c[i]; i++){
5572                     if(ci.checked == true){
5573                         r[++ri] = ci;
5574                     }
5575                 }
5576                 return r;
5577             },
5578
5579             "not" : function(c, ss){
5580                 return Roo.DomQuery.filter(c, ss, true);
5581             },
5582
5583             "odd" : function(c){
5584                 return this["nth-child"](c, "odd");
5585             },
5586
5587             "even" : function(c){
5588                 return this["nth-child"](c, "even");
5589             },
5590
5591             "nth" : function(c, a){
5592                 return c[a-1] || [];
5593             },
5594
5595             "first" : function(c){
5596                 return c[0] || [];
5597             },
5598
5599             "last" : function(c){
5600                 return c[c.length-1] || [];
5601             },
5602
5603             "has" : function(c, ss){
5604                 var s = Roo.DomQuery.select;
5605                 var r = [], ri = -1;
5606                 for(var i = 0, ci; ci = c[i]; i++){
5607                     if(s(ss, ci).length > 0){
5608                         r[++ri] = ci;
5609                     }
5610                 }
5611                 return r;
5612             },
5613
5614             "next" : function(c, ss){
5615                 var is = Roo.DomQuery.is;
5616                 var r = [], ri = -1;
5617                 for(var i = 0, ci; ci = c[i]; i++){
5618                     var n = next(ci);
5619                     if(n && is(n, ss)){
5620                         r[++ri] = ci;
5621                     }
5622                 }
5623                 return r;
5624             },
5625
5626             "prev" : function(c, ss){
5627                 var is = Roo.DomQuery.is;
5628                 var r = [], ri = -1;
5629                 for(var i = 0, ci; ci = c[i]; i++){
5630                     var n = prev(ci);
5631                     if(n && is(n, ss)){
5632                         r[++ri] = ci;
5633                     }
5634                 }
5635                 return r;
5636             }
5637         }
5638     };
5639 }();
5640
5641 /**
5642  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5643  * @param {String} path The selector/xpath query
5644  * @param {Node} root (optional) The start of the query (defaults to document).
5645  * @return {Array}
5646  * @member Roo
5647  * @method query
5648  */
5649 Roo.query = Roo.DomQuery.select;
5650 /*
5651  * Based on:
5652  * Ext JS Library 1.1.1
5653  * Copyright(c) 2006-2007, Ext JS, LLC.
5654  *
5655  * Originally Released Under LGPL - original licence link has changed is not relivant.
5656  *
5657  * Fork - LGPL
5658  * <script type="text/javascript">
5659  */
5660
5661 /**
5662  * @class Roo.util.Observable
5663  * Base class that provides a common interface for publishing events. Subclasses are expected to
5664  * to have a property "events" with all the events defined.<br>
5665  * For example:
5666  * <pre><code>
5667  Employee = function(name){
5668     this.name = name;
5669     this.addEvents({
5670         "fired" : true,
5671         "quit" : true
5672     });
5673  }
5674  Roo.extend(Employee, Roo.util.Observable);
5675 </code></pre>
5676  * @param {Object} config properties to use (incuding events / listeners)
5677  */
5678
5679 Roo.util.Observable = function(cfg){
5680     
5681     cfg = cfg|| {};
5682     this.addEvents(cfg.events || {});
5683     if (cfg.events) {
5684         delete cfg.events; // make sure
5685     }
5686      
5687     Roo.apply(this, cfg);
5688     
5689     if(this.listeners){
5690         this.on(this.listeners);
5691         delete this.listeners;
5692     }
5693 };
5694 Roo.util.Observable.prototype = {
5695     /** 
5696  * @cfg {Object} listeners  list of events and functions to call for this object, 
5697  * For example :
5698  * <pre><code>
5699     listeners :  { 
5700        'click' : function(e) {
5701            ..... 
5702         } ,
5703         .... 
5704     } 
5705   </code></pre>
5706  */
5707     
5708     
5709     /**
5710      * Fires the specified event with the passed parameters (minus the event name).
5711      * @param {String} eventName
5712      * @param {Object...} args Variable number of parameters are passed to handlers
5713      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5714      */
5715     fireEvent : function(){
5716         var ce = this.events[arguments[0].toLowerCase()];
5717         if(typeof ce == "object"){
5718             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5719         }else{
5720             return true;
5721         }
5722     },
5723
5724     // private
5725     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5726
5727     /**
5728      * Appends an event handler to this component
5729      * @param {String}   eventName The type of event to listen for
5730      * @param {Function} handler The method the event invokes
5731      * @param {Object}   scope (optional) The scope in which to execute the handler
5732      * function. The handler function's "this" context.
5733      * @param {Object}   options (optional) An object containing handler configuration
5734      * properties. This may contain any of the following properties:<ul>
5735      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5736      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5737      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5738      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5739      * by the specified number of milliseconds. If the event fires again within that time, the original
5740      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5741      * </ul><br>
5742      * <p>
5743      * <b>Combining Options</b><br>
5744      * Using the options argument, it is possible to combine different types of listeners:<br>
5745      * <br>
5746      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5747                 <pre><code>
5748                 el.on('click', this.onClick, this, {
5749                         single: true,
5750                 delay: 100,
5751                 forumId: 4
5752                 });
5753                 </code></pre>
5754      * <p>
5755      * <b>Attaching multiple handlers in 1 call</b><br>
5756      * The method also allows for a single argument to be passed which is a config object containing properties
5757      * which specify multiple handlers.
5758      * <pre><code>
5759                 el.on({
5760                         'click': {
5761                         fn: this.onClick,
5762                         scope: this,
5763                         delay: 100
5764                 }, 
5765                 'mouseover': {
5766                         fn: this.onMouseOver,
5767                         scope: this
5768                 },
5769                 'mouseout': {
5770                         fn: this.onMouseOut,
5771                         scope: this
5772                 }
5773                 });
5774                 </code></pre>
5775      * <p>
5776      * Or a shorthand syntax which passes the same scope object to all handlers:
5777         <pre><code>
5778                 el.on({
5779                         'click': this.onClick,
5780                 'mouseover': this.onMouseOver,
5781                 'mouseout': this.onMouseOut,
5782                 scope: this
5783                 });
5784                 </code></pre>
5785      */
5786     addListener : function(eventName, fn, scope, o){
5787         if(typeof eventName == "object"){
5788             o = eventName;
5789             for(var e in o){
5790                 if(this.filterOptRe.test(e)){
5791                     continue;
5792                 }
5793                 if(typeof o[e] == "function"){
5794                     // shared options
5795                     this.addListener(e, o[e], o.scope,  o);
5796                 }else{
5797                     // individual options
5798                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5799                 }
5800             }
5801             return;
5802         }
5803         o = (!o || typeof o == "boolean") ? {} : o;
5804         eventName = eventName.toLowerCase();
5805         var ce = this.events[eventName] || true;
5806         if(typeof ce == "boolean"){
5807             ce = new Roo.util.Event(this, eventName);
5808             this.events[eventName] = ce;
5809         }
5810         ce.addListener(fn, scope, o);
5811     },
5812
5813     /**
5814      * Removes a listener
5815      * @param {String}   eventName     The type of event to listen for
5816      * @param {Function} handler        The handler to remove
5817      * @param {Object}   scope  (optional) The scope (this object) for the handler
5818      */
5819     removeListener : function(eventName, fn, scope){
5820         var ce = this.events[eventName.toLowerCase()];
5821         if(typeof ce == "object"){
5822             ce.removeListener(fn, scope);
5823         }
5824     },
5825
5826     /**
5827      * Removes all listeners for this object
5828      */
5829     purgeListeners : function(){
5830         for(var evt in this.events){
5831             if(typeof this.events[evt] == "object"){
5832                  this.events[evt].clearListeners();
5833             }
5834         }
5835     },
5836
5837     relayEvents : function(o, events){
5838         var createHandler = function(ename){
5839             return function(){
5840                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5841             };
5842         };
5843         for(var i = 0, len = events.length; i < len; i++){
5844             var ename = events[i];
5845             if(!this.events[ename]){ this.events[ename] = true; };
5846             o.on(ename, createHandler(ename), this);
5847         }
5848     },
5849
5850     /**
5851      * Used to define events on this Observable
5852      * @param {Object} object The object with the events defined
5853      */
5854     addEvents : function(o){
5855         if(!this.events){
5856             this.events = {};
5857         }
5858         Roo.applyIf(this.events, o);
5859     },
5860
5861     /**
5862      * Checks to see if this object has any listeners for a specified event
5863      * @param {String} eventName The name of the event to check for
5864      * @return {Boolean} True if the event is being listened for, else false
5865      */
5866     hasListener : function(eventName){
5867         var e = this.events[eventName];
5868         return typeof e == "object" && e.listeners.length > 0;
5869     }
5870 };
5871 /**
5872  * Appends an event handler to this element (shorthand for addListener)
5873  * @param {String}   eventName     The type of event to listen for
5874  * @param {Function} handler        The method the event invokes
5875  * @param {Object}   scope (optional) The scope in which to execute the handler
5876  * function. The handler function's "this" context.
5877  * @param {Object}   options  (optional)
5878  * @method
5879  */
5880 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5881 /**
5882  * Removes a listener (shorthand for removeListener)
5883  * @param {String}   eventName     The type of event to listen for
5884  * @param {Function} handler        The handler to remove
5885  * @param {Object}   scope  (optional) The scope (this object) for the handler
5886  * @method
5887  */
5888 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5889
5890 /**
5891  * Starts capture on the specified Observable. All events will be passed
5892  * to the supplied function with the event name + standard signature of the event
5893  * <b>before</b> the event is fired. If the supplied function returns false,
5894  * the event will not fire.
5895  * @param {Observable} o The Observable to capture
5896  * @param {Function} fn The function to call
5897  * @param {Object} scope (optional) The scope (this object) for the fn
5898  * @static
5899  */
5900 Roo.util.Observable.capture = function(o, fn, scope){
5901     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5902 };
5903
5904 /**
5905  * Removes <b>all</b> added captures from the Observable.
5906  * @param {Observable} o The Observable to release
5907  * @static
5908  */
5909 Roo.util.Observable.releaseCapture = function(o){
5910     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5911 };
5912
5913 (function(){
5914
5915     var createBuffered = function(h, o, scope){
5916         var task = new Roo.util.DelayedTask();
5917         return function(){
5918             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5919         };
5920     };
5921
5922     var createSingle = function(h, e, fn, scope){
5923         return function(){
5924             e.removeListener(fn, scope);
5925             return h.apply(scope, arguments);
5926         };
5927     };
5928
5929     var createDelayed = function(h, o, scope){
5930         return function(){
5931             var args = Array.prototype.slice.call(arguments, 0);
5932             setTimeout(function(){
5933                 h.apply(scope, args);
5934             }, o.delay || 10);
5935         };
5936     };
5937
5938     Roo.util.Event = function(obj, name){
5939         this.name = name;
5940         this.obj = obj;
5941         this.listeners = [];
5942     };
5943
5944     Roo.util.Event.prototype = {
5945         addListener : function(fn, scope, options){
5946             var o = options || {};
5947             scope = scope || this.obj;
5948             if(!this.isListening(fn, scope)){
5949                 var l = {fn: fn, scope: scope, options: o};
5950                 var h = fn;
5951                 if(o.delay){
5952                     h = createDelayed(h, o, scope);
5953                 }
5954                 if(o.single){
5955                     h = createSingle(h, this, fn, scope);
5956                 }
5957                 if(o.buffer){
5958                     h = createBuffered(h, o, scope);
5959                 }
5960                 l.fireFn = h;
5961                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5962                     this.listeners.push(l);
5963                 }else{
5964                     this.listeners = this.listeners.slice(0);
5965                     this.listeners.push(l);
5966                 }
5967             }
5968         },
5969
5970         findListener : function(fn, scope){
5971             scope = scope || this.obj;
5972             var ls = this.listeners;
5973             for(var i = 0, len = ls.length; i < len; i++){
5974                 var l = ls[i];
5975                 if(l.fn == fn && l.scope == scope){
5976                     return i;
5977                 }
5978             }
5979             return -1;
5980         },
5981
5982         isListening : function(fn, scope){
5983             return this.findListener(fn, scope) != -1;
5984         },
5985
5986         removeListener : function(fn, scope){
5987             var index;
5988             if((index = this.findListener(fn, scope)) != -1){
5989                 if(!this.firing){
5990                     this.listeners.splice(index, 1);
5991                 }else{
5992                     this.listeners = this.listeners.slice(0);
5993                     this.listeners.splice(index, 1);
5994                 }
5995                 return true;
5996             }
5997             return false;
5998         },
5999
6000         clearListeners : function(){
6001             this.listeners = [];
6002         },
6003
6004         fire : function(){
6005             var ls = this.listeners, scope, len = ls.length;
6006             if(len > 0){
6007                 this.firing = true;
6008                 var args = Array.prototype.slice.call(arguments, 0);
6009                 for(var i = 0; i < len; i++){
6010                     var l = ls[i];
6011                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6012                         this.firing = false;
6013                         return false;
6014                     }
6015                 }
6016                 this.firing = false;
6017             }
6018             return true;
6019         }
6020     };
6021 })();/*
6022  * Based on:
6023  * Ext JS Library 1.1.1
6024  * Copyright(c) 2006-2007, Ext JS, LLC.
6025  *
6026  * Originally Released Under LGPL - original licence link has changed is not relivant.
6027  *
6028  * Fork - LGPL
6029  * <script type="text/javascript">
6030  */
6031
6032 /**
6033  * @class Roo.EventManager
6034  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6035  * several useful events directly.
6036  * See {@link Roo.EventObject} for more details on normalized event objects.
6037  * @singleton
6038  */
6039 Roo.EventManager = function(){
6040     var docReadyEvent, docReadyProcId, docReadyState = false;
6041     var resizeEvent, resizeTask, textEvent, textSize;
6042     var E = Roo.lib.Event;
6043     var D = Roo.lib.Dom;
6044
6045
6046     var fireDocReady = function(){
6047         if(!docReadyState){
6048             docReadyState = true;
6049             Roo.isReady = true;
6050             if(docReadyProcId){
6051                 clearInterval(docReadyProcId);
6052             }
6053             if(Roo.isGecko || Roo.isOpera) {
6054                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6055             }
6056             if(Roo.isIE){
6057                 var defer = document.getElementById("ie-deferred-loader");
6058                 if(defer){
6059                     defer.onreadystatechange = null;
6060                     defer.parentNode.removeChild(defer);
6061                 }
6062             }
6063             if(docReadyEvent){
6064                 docReadyEvent.fire();
6065                 docReadyEvent.clearListeners();
6066             }
6067         }
6068     };
6069     
6070     var initDocReady = function(){
6071         docReadyEvent = new Roo.util.Event();
6072         if(Roo.isGecko || Roo.isOpera) {
6073             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6074         }else if(Roo.isIE){
6075             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6076             var defer = document.getElementById("ie-deferred-loader");
6077             defer.onreadystatechange = function(){
6078                 if(this.readyState == "complete"){
6079                     fireDocReady();
6080                 }
6081             };
6082         }else if(Roo.isSafari){ 
6083             docReadyProcId = setInterval(function(){
6084                 var rs = document.readyState;
6085                 if(rs == "complete") {
6086                     fireDocReady();     
6087                  }
6088             }, 10);
6089         }
6090         // no matter what, make sure it fires on load
6091         E.on(window, "load", fireDocReady);
6092     };
6093
6094     var createBuffered = function(h, o){
6095         var task = new Roo.util.DelayedTask(h);
6096         return function(e){
6097             // create new event object impl so new events don't wipe out properties
6098             e = new Roo.EventObjectImpl(e);
6099             task.delay(o.buffer, h, null, [e]);
6100         };
6101     };
6102
6103     var createSingle = function(h, el, ename, fn){
6104         return function(e){
6105             Roo.EventManager.removeListener(el, ename, fn);
6106             h(e);
6107         };
6108     };
6109
6110     var createDelayed = function(h, o){
6111         return function(e){
6112             // create new event object impl so new events don't wipe out properties
6113             e = new Roo.EventObjectImpl(e);
6114             setTimeout(function(){
6115                 h(e);
6116             }, o.delay || 10);
6117         };
6118     };
6119
6120     var listen = function(element, ename, opt, fn, scope){
6121         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6122         fn = fn || o.fn; scope = scope || o.scope;
6123         var el = Roo.getDom(element);
6124         if(!el){
6125             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6126         }
6127         var h = function(e){
6128             e = Roo.EventObject.setEvent(e);
6129             var t;
6130             if(o.delegate){
6131                 t = e.getTarget(o.delegate, el);
6132                 if(!t){
6133                     return;
6134                 }
6135             }else{
6136                 t = e.target;
6137             }
6138             if(o.stopEvent === true){
6139                 e.stopEvent();
6140             }
6141             if(o.preventDefault === true){
6142                e.preventDefault();
6143             }
6144             if(o.stopPropagation === true){
6145                 e.stopPropagation();
6146             }
6147
6148             if(o.normalized === false){
6149                 e = e.browserEvent;
6150             }
6151
6152             fn.call(scope || el, e, t, o);
6153         };
6154         if(o.delay){
6155             h = createDelayed(h, o);
6156         }
6157         if(o.single){
6158             h = createSingle(h, el, ename, fn);
6159         }
6160         if(o.buffer){
6161             h = createBuffered(h, o);
6162         }
6163         fn._handlers = fn._handlers || [];
6164         fn._handlers.push([Roo.id(el), ename, h]);
6165
6166         E.on(el, ename, h);
6167         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6168             el.addEventListener("DOMMouseScroll", h, false);
6169             E.on(window, 'unload', function(){
6170                 el.removeEventListener("DOMMouseScroll", h, false);
6171             });
6172         }
6173         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6174             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6175         }
6176         return h;
6177     };
6178
6179     var stopListening = function(el, ename, fn){
6180         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6181         if(hds){
6182             for(var i = 0, len = hds.length; i < len; i++){
6183                 var h = hds[i];
6184                 if(h[0] == id && h[1] == ename){
6185                     hd = h[2];
6186                     hds.splice(i, 1);
6187                     break;
6188                 }
6189             }
6190         }
6191         E.un(el, ename, hd);
6192         el = Roo.getDom(el);
6193         if(ename == "mousewheel" && el.addEventListener){
6194             el.removeEventListener("DOMMouseScroll", hd, false);
6195         }
6196         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6197             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6198         }
6199     };
6200
6201     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6202     
6203     var pub = {
6204         
6205         
6206         /** 
6207          * Fix for doc tools
6208          * @scope Roo.EventManager
6209          */
6210         
6211         
6212         /** 
6213          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6214          * object with a Roo.EventObject
6215          * @param {Function} fn        The method the event invokes
6216          * @param {Object}   scope    An object that becomes the scope of the handler
6217          * @param {boolean}  override If true, the obj passed in becomes
6218          *                             the execution scope of the listener
6219          * @return {Function} The wrapped function
6220          * @deprecated
6221          */
6222         wrap : function(fn, scope, override){
6223             return function(e){
6224                 Roo.EventObject.setEvent(e);
6225                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6226             };
6227         },
6228         
6229         /**
6230      * Appends an event handler to an element (shorthand for addListener)
6231      * @param {String/HTMLElement}   element        The html element or id to assign the
6232      * @param {String}   eventName The type of event to listen for
6233      * @param {Function} handler The method the event invokes
6234      * @param {Object}   scope (optional) The scope in which to execute the handler
6235      * function. The handler function's "this" context.
6236      * @param {Object}   options (optional) An object containing handler configuration
6237      * properties. This may contain any of the following properties:<ul>
6238      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6239      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6240      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6241      * <li>preventDefault {Boolean} True to prevent the default action</li>
6242      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6243      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6244      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6245      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6246      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6247      * by the specified number of milliseconds. If the event fires again within that time, the original
6248      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6249      * </ul><br>
6250      * <p>
6251      * <b>Combining Options</b><br>
6252      * Using the options argument, it is possible to combine different types of listeners:<br>
6253      * <br>
6254      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6255      * Code:<pre><code>
6256 el.on('click', this.onClick, this, {
6257     single: true,
6258     delay: 100,
6259     stopEvent : true,
6260     forumId: 4
6261 });</code></pre>
6262      * <p>
6263      * <b>Attaching multiple handlers in 1 call</b><br>
6264       * The method also allows for a single argument to be passed which is a config object containing properties
6265      * which specify multiple handlers.
6266      * <p>
6267      * Code:<pre><code>
6268 el.on({
6269     'click' : {
6270         fn: this.onClick
6271         scope: this,
6272         delay: 100
6273     },
6274     'mouseover' : {
6275         fn: this.onMouseOver
6276         scope: this
6277     },
6278     'mouseout' : {
6279         fn: this.onMouseOut
6280         scope: this
6281     }
6282 });</code></pre>
6283      * <p>
6284      * Or a shorthand syntax:<br>
6285      * Code:<pre><code>
6286 el.on({
6287     'click' : this.onClick,
6288     'mouseover' : this.onMouseOver,
6289     'mouseout' : this.onMouseOut
6290     scope: this
6291 });</code></pre>
6292      */
6293         addListener : function(element, eventName, fn, scope, options){
6294             if(typeof eventName == "object"){
6295                 var o = eventName;
6296                 for(var e in o){
6297                     if(propRe.test(e)){
6298                         continue;
6299                     }
6300                     if(typeof o[e] == "function"){
6301                         // shared options
6302                         listen(element, e, o, o[e], o.scope);
6303                     }else{
6304                         // individual options
6305                         listen(element, e, o[e]);
6306                     }
6307                 }
6308                 return;
6309             }
6310             return listen(element, eventName, options, fn, scope);
6311         },
6312         
6313         /**
6314          * Removes an event handler
6315          *
6316          * @param {String/HTMLElement}   element        The id or html element to remove the 
6317          *                             event from
6318          * @param {String}   eventName     The type of event
6319          * @param {Function} fn
6320          * @return {Boolean} True if a listener was actually removed
6321          */
6322         removeListener : function(element, eventName, fn){
6323             return stopListening(element, eventName, fn);
6324         },
6325         
6326         /**
6327          * Fires when the document is ready (before onload and before images are loaded). Can be 
6328          * accessed shorthanded Roo.onReady().
6329          * @param {Function} fn        The method the event invokes
6330          * @param {Object}   scope    An  object that becomes the scope of the handler
6331          * @param {boolean}  options
6332          */
6333         onDocumentReady : function(fn, scope, options){
6334             if(docReadyState){ // if it already fired
6335                 docReadyEvent.addListener(fn, scope, options);
6336                 docReadyEvent.fire();
6337                 docReadyEvent.clearListeners();
6338                 return;
6339             }
6340             if(!docReadyEvent){
6341                 initDocReady();
6342             }
6343             docReadyEvent.addListener(fn, scope, options);
6344         },
6345         
6346         /**
6347          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6348          * @param {Function} fn        The method the event invokes
6349          * @param {Object}   scope    An object that becomes the scope of the handler
6350          * @param {boolean}  options
6351          */
6352         onWindowResize : function(fn, scope, options){
6353             if(!resizeEvent){
6354                 resizeEvent = new Roo.util.Event();
6355                 resizeTask = new Roo.util.DelayedTask(function(){
6356                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6357                 });
6358                 E.on(window, "resize", function(){
6359                     if(Roo.isIE){
6360                         resizeTask.delay(50);
6361                     }else{
6362                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6363                     }
6364                 });
6365             }
6366             resizeEvent.addListener(fn, scope, options);
6367         },
6368
6369         /**
6370          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6371          * @param {Function} fn        The method the event invokes
6372          * @param {Object}   scope    An object that becomes the scope of the handler
6373          * @param {boolean}  options
6374          */
6375         onTextResize : function(fn, scope, options){
6376             if(!textEvent){
6377                 textEvent = new Roo.util.Event();
6378                 var textEl = new Roo.Element(document.createElement('div'));
6379                 textEl.dom.className = 'x-text-resize';
6380                 textEl.dom.innerHTML = 'X';
6381                 textEl.appendTo(document.body);
6382                 textSize = textEl.dom.offsetHeight;
6383                 setInterval(function(){
6384                     if(textEl.dom.offsetHeight != textSize){
6385                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6386                     }
6387                 }, this.textResizeInterval);
6388             }
6389             textEvent.addListener(fn, scope, options);
6390         },
6391
6392         /**
6393          * Removes the passed window resize listener.
6394          * @param {Function} fn        The method the event invokes
6395          * @param {Object}   scope    The scope of handler
6396          */
6397         removeResizeListener : function(fn, scope){
6398             if(resizeEvent){
6399                 resizeEvent.removeListener(fn, scope);
6400             }
6401         },
6402
6403         // private
6404         fireResize : function(){
6405             if(resizeEvent){
6406                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6407             }   
6408         },
6409         /**
6410          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6411          */
6412         ieDeferSrc : false,
6413         /**
6414          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6415          */
6416         textResizeInterval : 50
6417     };
6418     
6419     /**
6420      * Fix for doc tools
6421      * @scopeAlias pub=Roo.EventManager
6422      */
6423     
6424      /**
6425      * Appends an event handler to an element (shorthand for addListener)
6426      * @param {String/HTMLElement}   element        The html element or id to assign the
6427      * @param {String}   eventName The type of event to listen for
6428      * @param {Function} handler The method the event invokes
6429      * @param {Object}   scope (optional) The scope in which to execute the handler
6430      * function. The handler function's "this" context.
6431      * @param {Object}   options (optional) An object containing handler configuration
6432      * properties. This may contain any of the following properties:<ul>
6433      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6434      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6435      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6436      * <li>preventDefault {Boolean} True to prevent the default action</li>
6437      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6438      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6439      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6440      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6441      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6442      * by the specified number of milliseconds. If the event fires again within that time, the original
6443      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6444      * </ul><br>
6445      * <p>
6446      * <b>Combining Options</b><br>
6447      * Using the options argument, it is possible to combine different types of listeners:<br>
6448      * <br>
6449      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6450      * Code:<pre><code>
6451 el.on('click', this.onClick, this, {
6452     single: true,
6453     delay: 100,
6454     stopEvent : true,
6455     forumId: 4
6456 });</code></pre>
6457      * <p>
6458      * <b>Attaching multiple handlers in 1 call</b><br>
6459       * The method also allows for a single argument to be passed which is a config object containing properties
6460      * which specify multiple handlers.
6461      * <p>
6462      * Code:<pre><code>
6463 el.on({
6464     'click' : {
6465         fn: this.onClick
6466         scope: this,
6467         delay: 100
6468     },
6469     'mouseover' : {
6470         fn: this.onMouseOver
6471         scope: this
6472     },
6473     'mouseout' : {
6474         fn: this.onMouseOut
6475         scope: this
6476     }
6477 });</code></pre>
6478      * <p>
6479      * Or a shorthand syntax:<br>
6480      * Code:<pre><code>
6481 el.on({
6482     'click' : this.onClick,
6483     'mouseover' : this.onMouseOver,
6484     'mouseout' : this.onMouseOut
6485     scope: this
6486 });</code></pre>
6487      */
6488     pub.on = pub.addListener;
6489     pub.un = pub.removeListener;
6490
6491     pub.stoppedMouseDownEvent = new Roo.util.Event();
6492     return pub;
6493 }();
6494 /**
6495   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6496   * @param {Function} fn        The method the event invokes
6497   * @param {Object}   scope    An  object that becomes the scope of the handler
6498   * @param {boolean}  override If true, the obj passed in becomes
6499   *                             the execution scope of the listener
6500   * @member Roo
6501   * @method onReady
6502  */
6503 Roo.onReady = Roo.EventManager.onDocumentReady;
6504
6505 Roo.onReady(function(){
6506     var bd = Roo.get(document.body);
6507     if(!bd){ return; }
6508
6509     var cls = [
6510             Roo.isIE ? "roo-ie"
6511             : Roo.isGecko ? "roo-gecko"
6512             : Roo.isOpera ? "roo-opera"
6513             : Roo.isSafari ? "roo-safari" : ""];
6514
6515     if(Roo.isMac){
6516         cls.push("roo-mac");
6517     }
6518     if(Roo.isLinux){
6519         cls.push("roo-linux");
6520     }
6521     if(Roo.isBorderBox){
6522         cls.push('roo-border-box');
6523     }
6524     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6525         var p = bd.dom.parentNode;
6526         if(p){
6527             p.className += ' roo-strict';
6528         }
6529     }
6530     bd.addClass(cls.join(' '));
6531 });
6532
6533 /**
6534  * @class Roo.EventObject
6535  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6536  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6537  * Example:
6538  * <pre><code>
6539  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6540     e.preventDefault();
6541     var target = e.getTarget();
6542     ...
6543  }
6544  var myDiv = Roo.get("myDiv");
6545  myDiv.on("click", handleClick);
6546  //or
6547  Roo.EventManager.on("myDiv", 'click', handleClick);
6548  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6549  </code></pre>
6550  * @singleton
6551  */
6552 Roo.EventObject = function(){
6553     
6554     var E = Roo.lib.Event;
6555     
6556     // safari keypress events for special keys return bad keycodes
6557     var safariKeys = {
6558         63234 : 37, // left
6559         63235 : 39, // right
6560         63232 : 38, // up
6561         63233 : 40, // down
6562         63276 : 33, // page up
6563         63277 : 34, // page down
6564         63272 : 46, // delete
6565         63273 : 36, // home
6566         63275 : 35  // end
6567     };
6568
6569     // normalize button clicks
6570     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6571                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6572
6573     Roo.EventObjectImpl = function(e){
6574         if(e){
6575             this.setEvent(e.browserEvent || e);
6576         }
6577     };
6578     Roo.EventObjectImpl.prototype = {
6579         /**
6580          * Used to fix doc tools.
6581          * @scope Roo.EventObject.prototype
6582          */
6583             
6584
6585         
6586         
6587         /** The normal browser event */
6588         browserEvent : null,
6589         /** The button pressed in a mouse event */
6590         button : -1,
6591         /** True if the shift key was down during the event */
6592         shiftKey : false,
6593         /** True if the control key was down during the event */
6594         ctrlKey : false,
6595         /** True if the alt key was down during the event */
6596         altKey : false,
6597
6598         /** Key constant 
6599         * @type Number */
6600         BACKSPACE : 8,
6601         /** Key constant 
6602         * @type Number */
6603         TAB : 9,
6604         /** Key constant 
6605         * @type Number */
6606         RETURN : 13,
6607         /** Key constant 
6608         * @type Number */
6609         ENTER : 13,
6610         /** Key constant 
6611         * @type Number */
6612         SHIFT : 16,
6613         /** Key constant 
6614         * @type Number */
6615         CONTROL : 17,
6616         /** Key constant 
6617         * @type Number */
6618         ESC : 27,
6619         /** Key constant 
6620         * @type Number */
6621         SPACE : 32,
6622         /** Key constant 
6623         * @type Number */
6624         PAGEUP : 33,
6625         /** Key constant 
6626         * @type Number */
6627         PAGEDOWN : 34,
6628         /** Key constant 
6629         * @type Number */
6630         END : 35,
6631         /** Key constant 
6632         * @type Number */
6633         HOME : 36,
6634         /** Key constant 
6635         * @type Number */
6636         LEFT : 37,
6637         /** Key constant 
6638         * @type Number */
6639         UP : 38,
6640         /** Key constant 
6641         * @type Number */
6642         RIGHT : 39,
6643         /** Key constant 
6644         * @type Number */
6645         DOWN : 40,
6646         /** Key constant 
6647         * @type Number */
6648         DELETE : 46,
6649         /** Key constant 
6650         * @type Number */
6651         F5 : 116,
6652
6653            /** @private */
6654         setEvent : function(e){
6655             if(e == this || (e && e.browserEvent)){ // already wrapped
6656                 return e;
6657             }
6658             this.browserEvent = e;
6659             if(e){
6660                 // normalize buttons
6661                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6662                 if(e.type == 'click' && this.button == -1){
6663                     this.button = 0;
6664                 }
6665                 this.type = e.type;
6666                 this.shiftKey = e.shiftKey;
6667                 // mac metaKey behaves like ctrlKey
6668                 this.ctrlKey = e.ctrlKey || e.metaKey;
6669                 this.altKey = e.altKey;
6670                 // in getKey these will be normalized for the mac
6671                 this.keyCode = e.keyCode;
6672                 // keyup warnings on firefox.
6673                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6674                 // cache the target for the delayed and or buffered events
6675                 this.target = E.getTarget(e);
6676                 // same for XY
6677                 this.xy = E.getXY(e);
6678             }else{
6679                 this.button = -1;
6680                 this.shiftKey = false;
6681                 this.ctrlKey = false;
6682                 this.altKey = false;
6683                 this.keyCode = 0;
6684                 this.charCode =0;
6685                 this.target = null;
6686                 this.xy = [0, 0];
6687             }
6688             return this;
6689         },
6690
6691         /**
6692          * Stop the event (preventDefault and stopPropagation)
6693          */
6694         stopEvent : function(){
6695             if(this.browserEvent){
6696                 if(this.browserEvent.type == 'mousedown'){
6697                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6698                 }
6699                 E.stopEvent(this.browserEvent);
6700             }
6701         },
6702
6703         /**
6704          * Prevents the browsers default handling of the event.
6705          */
6706         preventDefault : function(){
6707             if(this.browserEvent){
6708                 E.preventDefault(this.browserEvent);
6709             }
6710         },
6711
6712         /** @private */
6713         isNavKeyPress : function(){
6714             var k = this.keyCode;
6715             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6716             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6717         },
6718
6719         isSpecialKey : function(){
6720             var k = this.keyCode;
6721             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6722             (k == 16) || (k == 17) ||
6723             (k >= 18 && k <= 20) ||
6724             (k >= 33 && k <= 35) ||
6725             (k >= 36 && k <= 39) ||
6726             (k >= 44 && k <= 45);
6727         },
6728         /**
6729          * Cancels bubbling of the event.
6730          */
6731         stopPropagation : function(){
6732             if(this.browserEvent){
6733                 if(this.type == 'mousedown'){
6734                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6735                 }
6736                 E.stopPropagation(this.browserEvent);
6737             }
6738         },
6739
6740         /**
6741          * Gets the key code for the event.
6742          * @return {Number}
6743          */
6744         getCharCode : function(){
6745             return this.charCode || this.keyCode;
6746         },
6747
6748         /**
6749          * Returns a normalized keyCode for the event.
6750          * @return {Number} The key code
6751          */
6752         getKey : function(){
6753             var k = this.keyCode || this.charCode;
6754             return Roo.isSafari ? (safariKeys[k] || k) : k;
6755         },
6756
6757         /**
6758          * Gets the x coordinate of the event.
6759          * @return {Number}
6760          */
6761         getPageX : function(){
6762             return this.xy[0];
6763         },
6764
6765         /**
6766          * Gets the y coordinate of the event.
6767          * @return {Number}
6768          */
6769         getPageY : function(){
6770             return this.xy[1];
6771         },
6772
6773         /**
6774          * Gets the time of the event.
6775          * @return {Number}
6776          */
6777         getTime : function(){
6778             if(this.browserEvent){
6779                 return E.getTime(this.browserEvent);
6780             }
6781             return null;
6782         },
6783
6784         /**
6785          * Gets the page coordinates of the event.
6786          * @return {Array} The xy values like [x, y]
6787          */
6788         getXY : function(){
6789             return this.xy;
6790         },
6791
6792         /**
6793          * Gets the target for the event.
6794          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6795          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6796                 search as a number or element (defaults to 10 || document.body)
6797          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6798          * @return {HTMLelement}
6799          */
6800         getTarget : function(selector, maxDepth, returnEl){
6801             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6802         },
6803         /**
6804          * Gets the related target.
6805          * @return {HTMLElement}
6806          */
6807         getRelatedTarget : function(){
6808             if(this.browserEvent){
6809                 return E.getRelatedTarget(this.browserEvent);
6810             }
6811             return null;
6812         },
6813
6814         /**
6815          * Normalizes mouse wheel delta across browsers
6816          * @return {Number} The delta
6817          */
6818         getWheelDelta : function(){
6819             var e = this.browserEvent;
6820             var delta = 0;
6821             if(e.wheelDelta){ /* IE/Opera. */
6822                 delta = e.wheelDelta/120;
6823             }else if(e.detail){ /* Mozilla case. */
6824                 delta = -e.detail/3;
6825             }
6826             return delta;
6827         },
6828
6829         /**
6830          * Returns true if the control, meta, shift or alt key was pressed during this event.
6831          * @return {Boolean}
6832          */
6833         hasModifier : function(){
6834             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6835         },
6836
6837         /**
6838          * Returns true if the target of this event equals el or is a child of el
6839          * @param {String/HTMLElement/Element} el
6840          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6841          * @return {Boolean}
6842          */
6843         within : function(el, related){
6844             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6845             return t && Roo.fly(el).contains(t);
6846         },
6847
6848         getPoint : function(){
6849             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6850         }
6851     };
6852
6853     return new Roo.EventObjectImpl();
6854 }();
6855             
6856     /*
6857  * Based on:
6858  * Ext JS Library 1.1.1
6859  * Copyright(c) 2006-2007, Ext JS, LLC.
6860  *
6861  * Originally Released Under LGPL - original licence link has changed is not relivant.
6862  *
6863  * Fork - LGPL
6864  * <script type="text/javascript">
6865  */
6866
6867  
6868 // was in Composite Element!??!?!
6869  
6870 (function(){
6871     var D = Roo.lib.Dom;
6872     var E = Roo.lib.Event;
6873     var A = Roo.lib.Anim;
6874
6875     // local style camelizing for speed
6876     var propCache = {};
6877     var camelRe = /(-[a-z])/gi;
6878     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6879     var view = document.defaultView;
6880
6881 /**
6882  * @class Roo.Element
6883  * Represents an Element in the DOM.<br><br>
6884  * Usage:<br>
6885 <pre><code>
6886 var el = Roo.get("my-div");
6887
6888 // or with getEl
6889 var el = getEl("my-div");
6890
6891 // or with a DOM element
6892 var el = Roo.get(myDivElement);
6893 </code></pre>
6894  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6895  * each call instead of constructing a new one.<br><br>
6896  * <b>Animations</b><br />
6897  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6898  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6899 <pre>
6900 Option    Default   Description
6901 --------- --------  ---------------------------------------------
6902 duration  .35       The duration of the animation in seconds
6903 easing    easeOut   The YUI easing method
6904 callback  none      A function to execute when the anim completes
6905 scope     this      The scope (this) of the callback function
6906 </pre>
6907 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6908 * manipulate the animation. Here's an example:
6909 <pre><code>
6910 var el = Roo.get("my-div");
6911
6912 // no animation
6913 el.setWidth(100);
6914
6915 // default animation
6916 el.setWidth(100, true);
6917
6918 // animation with some options set
6919 el.setWidth(100, {
6920     duration: 1,
6921     callback: this.foo,
6922     scope: this
6923 });
6924
6925 // using the "anim" property to get the Anim object
6926 var opt = {
6927     duration: 1,
6928     callback: this.foo,
6929     scope: this
6930 };
6931 el.setWidth(100, opt);
6932 ...
6933 if(opt.anim.isAnimated()){
6934     opt.anim.stop();
6935 }
6936 </code></pre>
6937 * <b> Composite (Collections of) Elements</b><br />
6938  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6939  * @constructor Create a new Element directly.
6940  * @param {String/HTMLElement} element
6941  * @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).
6942  */
6943     Roo.Element = function(element, forceNew){
6944         var dom = typeof element == "string" ?
6945                 document.getElementById(element) : element;
6946         if(!dom){ // invalid id/element
6947             return null;
6948         }
6949         var id = dom.id;
6950         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6951             return Roo.Element.cache[id];
6952         }
6953
6954         /**
6955          * The DOM element
6956          * @type HTMLElement
6957          */
6958         this.dom = dom;
6959
6960         /**
6961          * The DOM element ID
6962          * @type String
6963          */
6964         this.id = id || Roo.id(dom);
6965     };
6966
6967     var El = Roo.Element;
6968
6969     El.prototype = {
6970         /**
6971          * The element's default display mode  (defaults to "")
6972          * @type String
6973          */
6974         originalDisplay : "",
6975
6976         visibilityMode : 1,
6977         /**
6978          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6979          * @type String
6980          */
6981         defaultUnit : "px",
6982         /**
6983          * Sets the element's visibility mode. When setVisible() is called it
6984          * will use this to determine whether to set the visibility or the display property.
6985          * @param visMode Element.VISIBILITY or Element.DISPLAY
6986          * @return {Roo.Element} this
6987          */
6988         setVisibilityMode : function(visMode){
6989             this.visibilityMode = visMode;
6990             return this;
6991         },
6992         /**
6993          * Convenience method for setVisibilityMode(Element.DISPLAY)
6994          * @param {String} display (optional) What to set display to when visible
6995          * @return {Roo.Element} this
6996          */
6997         enableDisplayMode : function(display){
6998             this.setVisibilityMode(El.DISPLAY);
6999             if(typeof display != "undefined") this.originalDisplay = display;
7000             return this;
7001         },
7002
7003         /**
7004          * 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)
7005          * @param {String} selector The simple selector to test
7006          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7007                 search as a number or element (defaults to 10 || document.body)
7008          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7009          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7010          */
7011         findParent : function(simpleSelector, maxDepth, returnEl){
7012             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7013             maxDepth = maxDepth || 50;
7014             if(typeof maxDepth != "number"){
7015                 stopEl = Roo.getDom(maxDepth);
7016                 maxDepth = 10;
7017             }
7018             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7019                 if(dq.is(p, simpleSelector)){
7020                     return returnEl ? Roo.get(p) : p;
7021                 }
7022                 depth++;
7023                 p = p.parentNode;
7024             }
7025             return null;
7026         },
7027
7028
7029         /**
7030          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7031          * @param {String} selector The simple selector to test
7032          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7033                 search as a number or element (defaults to 10 || document.body)
7034          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7035          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7036          */
7037         findParentNode : function(simpleSelector, maxDepth, returnEl){
7038             var p = Roo.fly(this.dom.parentNode, '_internal');
7039             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7040         },
7041
7042         /**
7043          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7044          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7045          * @param {String} selector The simple selector to test
7046          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7047                 search as a number or element (defaults to 10 || document.body)
7048          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7049          */
7050         up : function(simpleSelector, maxDepth){
7051             return this.findParentNode(simpleSelector, maxDepth, true);
7052         },
7053
7054
7055
7056         /**
7057          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7058          * @param {String} selector The simple selector to test
7059          * @return {Boolean} True if this element matches the selector, else false
7060          */
7061         is : function(simpleSelector){
7062             return Roo.DomQuery.is(this.dom, simpleSelector);
7063         },
7064
7065         /**
7066          * Perform animation on this element.
7067          * @param {Object} args The YUI animation control args
7068          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7069          * @param {Function} onComplete (optional) Function to call when animation completes
7070          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7071          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7072          * @return {Roo.Element} this
7073          */
7074         animate : function(args, duration, onComplete, easing, animType){
7075             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7076             return this;
7077         },
7078
7079         /*
7080          * @private Internal animation call
7081          */
7082         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7083             animType = animType || 'run';
7084             opt = opt || {};
7085             var anim = Roo.lib.Anim[animType](
7086                 this.dom, args,
7087                 (opt.duration || defaultDur) || .35,
7088                 (opt.easing || defaultEase) || 'easeOut',
7089                 function(){
7090                     Roo.callback(cb, this);
7091                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7092                 },
7093                 this
7094             );
7095             opt.anim = anim;
7096             return anim;
7097         },
7098
7099         // private legacy anim prep
7100         preanim : function(a, i){
7101             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7102         },
7103
7104         /**
7105          * Removes worthless text nodes
7106          * @param {Boolean} forceReclean (optional) By default the element
7107          * keeps track if it has been cleaned already so
7108          * you can call this over and over. However, if you update the element and
7109          * need to force a reclean, you can pass true.
7110          */
7111         clean : function(forceReclean){
7112             if(this.isCleaned && forceReclean !== true){
7113                 return this;
7114             }
7115             var ns = /\S/;
7116             var d = this.dom, n = d.firstChild, ni = -1;
7117             while(n){
7118                 var nx = n.nextSibling;
7119                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7120                     d.removeChild(n);
7121                 }else{
7122                     n.nodeIndex = ++ni;
7123                 }
7124                 n = nx;
7125             }
7126             this.isCleaned = true;
7127             return this;
7128         },
7129
7130         // private
7131         calcOffsetsTo : function(el){
7132             el = Roo.get(el);
7133             var d = el.dom;
7134             var restorePos = false;
7135             if(el.getStyle('position') == 'static'){
7136                 el.position('relative');
7137                 restorePos = true;
7138             }
7139             var x = 0, y =0;
7140             var op = this.dom;
7141             while(op && op != d && op.tagName != 'HTML'){
7142                 x+= op.offsetLeft;
7143                 y+= op.offsetTop;
7144                 op = op.offsetParent;
7145             }
7146             if(restorePos){
7147                 el.position('static');
7148             }
7149             return [x, y];
7150         },
7151
7152         /**
7153          * Scrolls this element into view within the passed container.
7154          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7155          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7156          * @return {Roo.Element} this
7157          */
7158         scrollIntoView : function(container, hscroll){
7159             var c = Roo.getDom(container) || document.body;
7160             var el = this.dom;
7161
7162             var o = this.calcOffsetsTo(c),
7163                 l = o[0],
7164                 t = o[1],
7165                 b = t+el.offsetHeight,
7166                 r = l+el.offsetWidth;
7167
7168             var ch = c.clientHeight;
7169             var ct = parseInt(c.scrollTop, 10);
7170             var cl = parseInt(c.scrollLeft, 10);
7171             var cb = ct + ch;
7172             var cr = cl + c.clientWidth;
7173
7174             if(t < ct){
7175                 c.scrollTop = t;
7176             }else if(b > cb){
7177                 c.scrollTop = b-ch;
7178             }
7179
7180             if(hscroll !== false){
7181                 if(l < cl){
7182                     c.scrollLeft = l;
7183                 }else if(r > cr){
7184                     c.scrollLeft = r-c.clientWidth;
7185                 }
7186             }
7187             return this;
7188         },
7189
7190         // private
7191         scrollChildIntoView : function(child, hscroll){
7192             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7193         },
7194
7195         /**
7196          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7197          * the new height may not be available immediately.
7198          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7199          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7200          * @param {Function} onComplete (optional) Function to call when animation completes
7201          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7202          * @return {Roo.Element} this
7203          */
7204         autoHeight : function(animate, duration, onComplete, easing){
7205             var oldHeight = this.getHeight();
7206             this.clip();
7207             this.setHeight(1); // force clipping
7208             setTimeout(function(){
7209                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7210                 if(!animate){
7211                     this.setHeight(height);
7212                     this.unclip();
7213                     if(typeof onComplete == "function"){
7214                         onComplete();
7215                     }
7216                 }else{
7217                     this.setHeight(oldHeight); // restore original height
7218                     this.setHeight(height, animate, duration, function(){
7219                         this.unclip();
7220                         if(typeof onComplete == "function") onComplete();
7221                     }.createDelegate(this), easing);
7222                 }
7223             }.createDelegate(this), 0);
7224             return this;
7225         },
7226
7227         /**
7228          * Returns true if this element is an ancestor of the passed element
7229          * @param {HTMLElement/String} el The element to check
7230          * @return {Boolean} True if this element is an ancestor of el, else false
7231          */
7232         contains : function(el){
7233             if(!el){return false;}
7234             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7235         },
7236
7237         /**
7238          * Checks whether the element is currently visible using both visibility and display properties.
7239          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7240          * @return {Boolean} True if the element is currently visible, else false
7241          */
7242         isVisible : function(deep) {
7243             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7244             if(deep !== true || !vis){
7245                 return vis;
7246             }
7247             var p = this.dom.parentNode;
7248             while(p && p.tagName.toLowerCase() != "body"){
7249                 if(!Roo.fly(p, '_isVisible').isVisible()){
7250                     return false;
7251                 }
7252                 p = p.parentNode;
7253             }
7254             return true;
7255         },
7256
7257         /**
7258          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7259          * @param {String} selector The CSS selector
7260          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7261          * @return {CompositeElement/CompositeElementLite} The composite element
7262          */
7263         select : function(selector, unique){
7264             return El.select(selector, unique, this.dom);
7265         },
7266
7267         /**
7268          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7269          * @param {String} selector The CSS selector
7270          * @return {Array} An array of the matched nodes
7271          */
7272         query : function(selector, unique){
7273             return Roo.DomQuery.select(selector, this.dom);
7274         },
7275
7276         /**
7277          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7278          * @param {String} selector The CSS selector
7279          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7280          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7281          */
7282         child : function(selector, returnDom){
7283             var n = Roo.DomQuery.selectNode(selector, this.dom);
7284             return returnDom ? n : Roo.get(n);
7285         },
7286
7287         /**
7288          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7289          * @param {String} selector The CSS selector
7290          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7291          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7292          */
7293         down : function(selector, returnDom){
7294             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7295             return returnDom ? n : Roo.get(n);
7296         },
7297
7298         /**
7299          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7300          * @param {String} group The group the DD object is member of
7301          * @param {Object} config The DD config object
7302          * @param {Object} overrides An object containing methods to override/implement on the DD object
7303          * @return {Roo.dd.DD} The DD object
7304          */
7305         initDD : function(group, config, overrides){
7306             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7307             return Roo.apply(dd, overrides);
7308         },
7309
7310         /**
7311          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7312          * @param {String} group The group the DDProxy object is member of
7313          * @param {Object} config The DDProxy config object
7314          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7315          * @return {Roo.dd.DDProxy} The DDProxy object
7316          */
7317         initDDProxy : function(group, config, overrides){
7318             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7319             return Roo.apply(dd, overrides);
7320         },
7321
7322         /**
7323          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7324          * @param {String} group The group the DDTarget object is member of
7325          * @param {Object} config The DDTarget config object
7326          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7327          * @return {Roo.dd.DDTarget} The DDTarget object
7328          */
7329         initDDTarget : function(group, config, overrides){
7330             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7331             return Roo.apply(dd, overrides);
7332         },
7333
7334         /**
7335          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7336          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7337          * @param {Boolean} visible Whether the element is visible
7338          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7339          * @return {Roo.Element} this
7340          */
7341          setVisible : function(visible, animate){
7342             if(!animate || !A){
7343                 if(this.visibilityMode == El.DISPLAY){
7344                     this.setDisplayed(visible);
7345                 }else{
7346                     this.fixDisplay();
7347                     this.dom.style.visibility = visible ? "visible" : "hidden";
7348                 }
7349             }else{
7350                 // closure for composites
7351                 var dom = this.dom;
7352                 var visMode = this.visibilityMode;
7353                 if(visible){
7354                     this.setOpacity(.01);
7355                     this.setVisible(true);
7356                 }
7357                 this.anim({opacity: { to: (visible?1:0) }},
7358                       this.preanim(arguments, 1),
7359                       null, .35, 'easeIn', function(){
7360                          if(!visible){
7361                              if(visMode == El.DISPLAY){
7362                                  dom.style.display = "none";
7363                              }else{
7364                                  dom.style.visibility = "hidden";
7365                              }
7366                              Roo.get(dom).setOpacity(1);
7367                          }
7368                      });
7369             }
7370             return this;
7371         },
7372
7373         /**
7374          * Returns true if display is not "none"
7375          * @return {Boolean}
7376          */
7377         isDisplayed : function() {
7378             return this.getStyle("display") != "none";
7379         },
7380
7381         /**
7382          * Toggles the element's visibility or display, depending on visibility mode.
7383          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7384          * @return {Roo.Element} this
7385          */
7386         toggle : function(animate){
7387             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7388             return this;
7389         },
7390
7391         /**
7392          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7393          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7394          * @return {Roo.Element} this
7395          */
7396         setDisplayed : function(value) {
7397             if(typeof value == "boolean"){
7398                value = value ? this.originalDisplay : "none";
7399             }
7400             this.setStyle("display", value);
7401             return this;
7402         },
7403
7404         /**
7405          * Tries to focus the element. Any exceptions are caught and ignored.
7406          * @return {Roo.Element} this
7407          */
7408         focus : function() {
7409             try{
7410                 this.dom.focus();
7411             }catch(e){}
7412             return this;
7413         },
7414
7415         /**
7416          * Tries to blur the element. Any exceptions are caught and ignored.
7417          * @return {Roo.Element} this
7418          */
7419         blur : function() {
7420             try{
7421                 this.dom.blur();
7422             }catch(e){}
7423             return this;
7424         },
7425
7426         /**
7427          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7428          * @param {String/Array} className The CSS class to add, or an array of classes
7429          * @return {Roo.Element} this
7430          */
7431         addClass : function(className){
7432             if(className instanceof Array){
7433                 for(var i = 0, len = className.length; i < len; i++) {
7434                     this.addClass(className[i]);
7435                 }
7436             }else{
7437                 if(className && !this.hasClass(className)){
7438                     this.dom.className = this.dom.className + " " + className;
7439                 }
7440             }
7441             return this;
7442         },
7443
7444         /**
7445          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7446          * @param {String/Array} className The CSS class to add, or an array of classes
7447          * @return {Roo.Element} this
7448          */
7449         radioClass : function(className){
7450             var siblings = this.dom.parentNode.childNodes;
7451             for(var i = 0; i < siblings.length; i++) {
7452                 var s = siblings[i];
7453                 if(s.nodeType == 1){
7454                     Roo.get(s).removeClass(className);
7455                 }
7456             }
7457             this.addClass(className);
7458             return this;
7459         },
7460
7461         /**
7462          * Removes one or more CSS classes from the element.
7463          * @param {String/Array} className The CSS class to remove, or an array of classes
7464          * @return {Roo.Element} this
7465          */
7466         removeClass : function(className){
7467             if(!className || !this.dom.className){
7468                 return this;
7469             }
7470             if(className instanceof Array){
7471                 for(var i = 0, len = className.length; i < len; i++) {
7472                     this.removeClass(className[i]);
7473                 }
7474             }else{
7475                 if(this.hasClass(className)){
7476                     var re = this.classReCache[className];
7477                     if (!re) {
7478                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7479                        this.classReCache[className] = re;
7480                     }
7481                     this.dom.className =
7482                         this.dom.className.replace(re, " ");
7483                 }
7484             }
7485             return this;
7486         },
7487
7488         // private
7489         classReCache: {},
7490
7491         /**
7492          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7493          * @param {String} className The CSS class to toggle
7494          * @return {Roo.Element} this
7495          */
7496         toggleClass : function(className){
7497             if(this.hasClass(className)){
7498                 this.removeClass(className);
7499             }else{
7500                 this.addClass(className);
7501             }
7502             return this;
7503         },
7504
7505         /**
7506          * Checks if the specified CSS class exists on this element's DOM node.
7507          * @param {String} className The CSS class to check for
7508          * @return {Boolean} True if the class exists, else false
7509          */
7510         hasClass : function(className){
7511             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7512         },
7513
7514         /**
7515          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7516          * @param {String} oldClassName The CSS class to replace
7517          * @param {String} newClassName The replacement CSS class
7518          * @return {Roo.Element} this
7519          */
7520         replaceClass : function(oldClassName, newClassName){
7521             this.removeClass(oldClassName);
7522             this.addClass(newClassName);
7523             return this;
7524         },
7525
7526         /**
7527          * Returns an object with properties matching the styles requested.
7528          * For example, el.getStyles('color', 'font-size', 'width') might return
7529          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7530          * @param {String} style1 A style name
7531          * @param {String} style2 A style name
7532          * @param {String} etc.
7533          * @return {Object} The style object
7534          */
7535         getStyles : function(){
7536             var a = arguments, len = a.length, r = {};
7537             for(var i = 0; i < len; i++){
7538                 r[a[i]] = this.getStyle(a[i]);
7539             }
7540             return r;
7541         },
7542
7543         /**
7544          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7545          * @param {String} property The style property whose value is returned.
7546          * @return {String} The current value of the style property for this element.
7547          */
7548         getStyle : function(){
7549             return view && view.getComputedStyle ?
7550                 function(prop){
7551                     var el = this.dom, v, cs, camel;
7552                     if(prop == 'float'){
7553                         prop = "cssFloat";
7554                     }
7555                     if(el.style && (v = el.style[prop])){
7556                         return v;
7557                     }
7558                     if(cs = view.getComputedStyle(el, "")){
7559                         if(!(camel = propCache[prop])){
7560                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7561                         }
7562                         return cs[camel];
7563                     }
7564                     return null;
7565                 } :
7566                 function(prop){
7567                     var el = this.dom, v, cs, camel;
7568                     if(prop == 'opacity'){
7569                         if(typeof el.style.filter == 'string'){
7570                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7571                             if(m){
7572                                 var fv = parseFloat(m[1]);
7573                                 if(!isNaN(fv)){
7574                                     return fv ? fv / 100 : 0;
7575                                 }
7576                             }
7577                         }
7578                         return 1;
7579                     }else if(prop == 'float'){
7580                         prop = "styleFloat";
7581                     }
7582                     if(!(camel = propCache[prop])){
7583                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7584                     }
7585                     if(v = el.style[camel]){
7586                         return v;
7587                     }
7588                     if(cs = el.currentStyle){
7589                         return cs[camel];
7590                     }
7591                     return null;
7592                 };
7593         }(),
7594
7595         /**
7596          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7597          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7598          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7599          * @return {Roo.Element} this
7600          */
7601         setStyle : function(prop, value){
7602             if(typeof prop == "string"){
7603                 
7604                 if (prop == 'float') {
7605                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7606                     return this;
7607                 }
7608                 
7609                 var camel;
7610                 if(!(camel = propCache[prop])){
7611                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7612                 }
7613                 
7614                 if(camel == 'opacity') {
7615                     this.setOpacity(value);
7616                 }else{
7617                     this.dom.style[camel] = value;
7618                 }
7619             }else{
7620                 for(var style in prop){
7621                     if(typeof prop[style] != "function"){
7622                        this.setStyle(style, prop[style]);
7623                     }
7624                 }
7625             }
7626             return this;
7627         },
7628
7629         /**
7630          * More flexible version of {@link #setStyle} for setting style properties.
7631          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7632          * a function which returns such a specification.
7633          * @return {Roo.Element} this
7634          */
7635         applyStyles : function(style){
7636             Roo.DomHelper.applyStyles(this.dom, style);
7637             return this;
7638         },
7639
7640         /**
7641           * 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).
7642           * @return {Number} The X position of the element
7643           */
7644         getX : function(){
7645             return D.getX(this.dom);
7646         },
7647
7648         /**
7649           * 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).
7650           * @return {Number} The Y position of the element
7651           */
7652         getY : function(){
7653             return D.getY(this.dom);
7654         },
7655
7656         /**
7657           * 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).
7658           * @return {Array} The XY position of the element
7659           */
7660         getXY : function(){
7661             return D.getXY(this.dom);
7662         },
7663
7664         /**
7665          * 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).
7666          * @param {Number} The X position of the element
7667          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7668          * @return {Roo.Element} this
7669          */
7670         setX : function(x, animate){
7671             if(!animate || !A){
7672                 D.setX(this.dom, x);
7673             }else{
7674                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7675             }
7676             return this;
7677         },
7678
7679         /**
7680          * 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).
7681          * @param {Number} The Y position of the element
7682          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7683          * @return {Roo.Element} this
7684          */
7685         setY : function(y, animate){
7686             if(!animate || !A){
7687                 D.setY(this.dom, y);
7688             }else{
7689                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7690             }
7691             return this;
7692         },
7693
7694         /**
7695          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7696          * @param {String} left The left CSS property value
7697          * @return {Roo.Element} this
7698          */
7699         setLeft : function(left){
7700             this.setStyle("left", this.addUnits(left));
7701             return this;
7702         },
7703
7704         /**
7705          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7706          * @param {String} top The top CSS property value
7707          * @return {Roo.Element} this
7708          */
7709         setTop : function(top){
7710             this.setStyle("top", this.addUnits(top));
7711             return this;
7712         },
7713
7714         /**
7715          * Sets the element's CSS right style.
7716          * @param {String} right The right CSS property value
7717          * @return {Roo.Element} this
7718          */
7719         setRight : function(right){
7720             this.setStyle("right", this.addUnits(right));
7721             return this;
7722         },
7723
7724         /**
7725          * Sets the element's CSS bottom style.
7726          * @param {String} bottom The bottom CSS property value
7727          * @return {Roo.Element} this
7728          */
7729         setBottom : function(bottom){
7730             this.setStyle("bottom", this.addUnits(bottom));
7731             return this;
7732         },
7733
7734         /**
7735          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7736          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7737          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7738          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7739          * @return {Roo.Element} this
7740          */
7741         setXY : function(pos, animate){
7742             if(!animate || !A){
7743                 D.setXY(this.dom, pos);
7744             }else{
7745                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7746             }
7747             return this;
7748         },
7749
7750         /**
7751          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7752          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7753          * @param {Number} x X value for new position (coordinates are page-based)
7754          * @param {Number} y Y value for new position (coordinates are page-based)
7755          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7756          * @return {Roo.Element} this
7757          */
7758         setLocation : function(x, y, animate){
7759             this.setXY([x, y], this.preanim(arguments, 2));
7760             return this;
7761         },
7762
7763         /**
7764          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7765          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7766          * @param {Number} x X value for new position (coordinates are page-based)
7767          * @param {Number} y Y value for new position (coordinates are page-based)
7768          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7769          * @return {Roo.Element} this
7770          */
7771         moveTo : function(x, y, animate){
7772             this.setXY([x, y], this.preanim(arguments, 2));
7773             return this;
7774         },
7775
7776         /**
7777          * Returns the region of the given element.
7778          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7779          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7780          */
7781         getRegion : function(){
7782             return D.getRegion(this.dom);
7783         },
7784
7785         /**
7786          * Returns the offset height of the element
7787          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7788          * @return {Number} The element's height
7789          */
7790         getHeight : function(contentHeight){
7791             var h = this.dom.offsetHeight || 0;
7792             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7793         },
7794
7795         /**
7796          * Returns the offset width of the element
7797          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7798          * @return {Number} The element's width
7799          */
7800         getWidth : function(contentWidth){
7801             var w = this.dom.offsetWidth || 0;
7802             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7803         },
7804
7805         /**
7806          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7807          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7808          * if a height has not been set using CSS.
7809          * @return {Number}
7810          */
7811         getComputedHeight : function(){
7812             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7813             if(!h){
7814                 h = parseInt(this.getStyle('height'), 10) || 0;
7815                 if(!this.isBorderBox()){
7816                     h += this.getFrameWidth('tb');
7817                 }
7818             }
7819             return h;
7820         },
7821
7822         /**
7823          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7824          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7825          * if a width has not been set using CSS.
7826          * @return {Number}
7827          */
7828         getComputedWidth : function(){
7829             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7830             if(!w){
7831                 w = parseInt(this.getStyle('width'), 10) || 0;
7832                 if(!this.isBorderBox()){
7833                     w += this.getFrameWidth('lr');
7834                 }
7835             }
7836             return w;
7837         },
7838
7839         /**
7840          * Returns the size of the element.
7841          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7842          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7843          */
7844         getSize : function(contentSize){
7845             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7846         },
7847
7848         /**
7849          * Returns the width and height of the viewport.
7850          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7851          */
7852         getViewSize : function(){
7853             var d = this.dom, doc = document, aw = 0, ah = 0;
7854             if(d == doc || d == doc.body){
7855                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7856             }else{
7857                 return {
7858                     width : d.clientWidth,
7859                     height: d.clientHeight
7860                 };
7861             }
7862         },
7863
7864         /**
7865          * Returns the value of the "value" attribute
7866          * @param {Boolean} asNumber true to parse the value as a number
7867          * @return {String/Number}
7868          */
7869         getValue : function(asNumber){
7870             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7871         },
7872
7873         // private
7874         adjustWidth : function(width){
7875             if(typeof width == "number"){
7876                 if(this.autoBoxAdjust && !this.isBorderBox()){
7877                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7878                 }
7879                 if(width < 0){
7880                     width = 0;
7881                 }
7882             }
7883             return width;
7884         },
7885
7886         // private
7887         adjustHeight : function(height){
7888             if(typeof height == "number"){
7889                if(this.autoBoxAdjust && !this.isBorderBox()){
7890                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7891                }
7892                if(height < 0){
7893                    height = 0;
7894                }
7895             }
7896             return height;
7897         },
7898
7899         /**
7900          * Set the width of the element
7901          * @param {Number} width The new width
7902          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7903          * @return {Roo.Element} this
7904          */
7905         setWidth : function(width, animate){
7906             width = this.adjustWidth(width);
7907             if(!animate || !A){
7908                 this.dom.style.width = this.addUnits(width);
7909             }else{
7910                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7911             }
7912             return this;
7913         },
7914
7915         /**
7916          * Set the height of the element
7917          * @param {Number} height The new height
7918          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7919          * @return {Roo.Element} this
7920          */
7921          setHeight : function(height, animate){
7922             height = this.adjustHeight(height);
7923             if(!animate || !A){
7924                 this.dom.style.height = this.addUnits(height);
7925             }else{
7926                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7927             }
7928             return this;
7929         },
7930
7931         /**
7932          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7933          * @param {Number} width The new width
7934          * @param {Number} height The new height
7935          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7936          * @return {Roo.Element} this
7937          */
7938          setSize : function(width, height, animate){
7939             if(typeof width == "object"){ // in case of object from getSize()
7940                 height = width.height; width = width.width;
7941             }
7942             width = this.adjustWidth(width); height = this.adjustHeight(height);
7943             if(!animate || !A){
7944                 this.dom.style.width = this.addUnits(width);
7945                 this.dom.style.height = this.addUnits(height);
7946             }else{
7947                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7948             }
7949             return this;
7950         },
7951
7952         /**
7953          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7954          * @param {Number} x X value for new position (coordinates are page-based)
7955          * @param {Number} y Y value for new position (coordinates are page-based)
7956          * @param {Number} width The new width
7957          * @param {Number} height The new height
7958          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7959          * @return {Roo.Element} this
7960          */
7961         setBounds : function(x, y, width, height, animate){
7962             if(!animate || !A){
7963                 this.setSize(width, height);
7964                 this.setLocation(x, y);
7965             }else{
7966                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7967                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7968                               this.preanim(arguments, 4), 'motion');
7969             }
7970             return this;
7971         },
7972
7973         /**
7974          * 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.
7975          * @param {Roo.lib.Region} region The region to fill
7976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7977          * @return {Roo.Element} this
7978          */
7979         setRegion : function(region, animate){
7980             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7981             return this;
7982         },
7983
7984         /**
7985          * Appends an event handler
7986          *
7987          * @param {String}   eventName     The type of event to append
7988          * @param {Function} fn        The method the event invokes
7989          * @param {Object} scope       (optional) The scope (this object) of the fn
7990          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7991          */
7992         addListener : function(eventName, fn, scope, options){
7993             if (this.dom) {
7994                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7995             }
7996         },
7997
7998         /**
7999          * Removes an event handler from this element
8000          * @param {String} eventName the type of event to remove
8001          * @param {Function} fn the method the event invokes
8002          * @return {Roo.Element} this
8003          */
8004         removeListener : function(eventName, fn){
8005             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8006             return this;
8007         },
8008
8009         /**
8010          * Removes all previous added listeners from this element
8011          * @return {Roo.Element} this
8012          */
8013         removeAllListeners : function(){
8014             E.purgeElement(this.dom);
8015             return this;
8016         },
8017
8018         relayEvent : function(eventName, observable){
8019             this.on(eventName, function(e){
8020                 observable.fireEvent(eventName, e);
8021             });
8022         },
8023
8024         /**
8025          * Set the opacity of the element
8026          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8027          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8028          * @return {Roo.Element} this
8029          */
8030          setOpacity : function(opacity, animate){
8031             if(!animate || !A){
8032                 var s = this.dom.style;
8033                 if(Roo.isIE){
8034                     s.zoom = 1;
8035                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8036                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8037                 }else{
8038                     s.opacity = opacity;
8039                 }
8040             }else{
8041                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8042             }
8043             return this;
8044         },
8045
8046         /**
8047          * Gets the left X coordinate
8048          * @param {Boolean} local True to get the local css position instead of page coordinate
8049          * @return {Number}
8050          */
8051         getLeft : function(local){
8052             if(!local){
8053                 return this.getX();
8054             }else{
8055                 return parseInt(this.getStyle("left"), 10) || 0;
8056             }
8057         },
8058
8059         /**
8060          * Gets the right X coordinate of the element (element X position + element width)
8061          * @param {Boolean} local True to get the local css position instead of page coordinate
8062          * @return {Number}
8063          */
8064         getRight : function(local){
8065             if(!local){
8066                 return this.getX() + this.getWidth();
8067             }else{
8068                 return (this.getLeft(true) + this.getWidth()) || 0;
8069             }
8070         },
8071
8072         /**
8073          * Gets the top Y coordinate
8074          * @param {Boolean} local True to get the local css position instead of page coordinate
8075          * @return {Number}
8076          */
8077         getTop : function(local) {
8078             if(!local){
8079                 return this.getY();
8080             }else{
8081                 return parseInt(this.getStyle("top"), 10) || 0;
8082             }
8083         },
8084
8085         /**
8086          * Gets the bottom Y coordinate of the element (element Y position + element height)
8087          * @param {Boolean} local True to get the local css position instead of page coordinate
8088          * @return {Number}
8089          */
8090         getBottom : function(local){
8091             if(!local){
8092                 return this.getY() + this.getHeight();
8093             }else{
8094                 return (this.getTop(true) + this.getHeight()) || 0;
8095             }
8096         },
8097
8098         /**
8099         * Initializes positioning on this element. If a desired position is not passed, it will make the
8100         * the element positioned relative IF it is not already positioned.
8101         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8102         * @param {Number} zIndex (optional) The zIndex to apply
8103         * @param {Number} x (optional) Set the page X position
8104         * @param {Number} y (optional) Set the page Y position
8105         */
8106         position : function(pos, zIndex, x, y){
8107             if(!pos){
8108                if(this.getStyle('position') == 'static'){
8109                    this.setStyle('position', 'relative');
8110                }
8111             }else{
8112                 this.setStyle("position", pos);
8113             }
8114             if(zIndex){
8115                 this.setStyle("z-index", zIndex);
8116             }
8117             if(x !== undefined && y !== undefined){
8118                 this.setXY([x, y]);
8119             }else if(x !== undefined){
8120                 this.setX(x);
8121             }else if(y !== undefined){
8122                 this.setY(y);
8123             }
8124         },
8125
8126         /**
8127         * Clear positioning back to the default when the document was loaded
8128         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8129         * @return {Roo.Element} this
8130          */
8131         clearPositioning : function(value){
8132             value = value ||'';
8133             this.setStyle({
8134                 "left": value,
8135                 "right": value,
8136                 "top": value,
8137                 "bottom": value,
8138                 "z-index": "",
8139                 "position" : "static"
8140             });
8141             return this;
8142         },
8143
8144         /**
8145         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8146         * snapshot before performing an update and then restoring the element.
8147         * @return {Object}
8148         */
8149         getPositioning : function(){
8150             var l = this.getStyle("left");
8151             var t = this.getStyle("top");
8152             return {
8153                 "position" : this.getStyle("position"),
8154                 "left" : l,
8155                 "right" : l ? "" : this.getStyle("right"),
8156                 "top" : t,
8157                 "bottom" : t ? "" : this.getStyle("bottom"),
8158                 "z-index" : this.getStyle("z-index")
8159             };
8160         },
8161
8162         /**
8163          * Gets the width of the border(s) for the specified side(s)
8164          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8165          * passing lr would get the border (l)eft width + the border (r)ight width.
8166          * @return {Number} The width of the sides passed added together
8167          */
8168         getBorderWidth : function(side){
8169             return this.addStyles(side, El.borders);
8170         },
8171
8172         /**
8173          * Gets the width of the padding(s) for the specified side(s)
8174          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8175          * passing lr would get the padding (l)eft + the padding (r)ight.
8176          * @return {Number} The padding of the sides passed added together
8177          */
8178         getPadding : function(side){
8179             return this.addStyles(side, El.paddings);
8180         },
8181
8182         /**
8183         * Set positioning with an object returned by getPositioning().
8184         * @param {Object} posCfg
8185         * @return {Roo.Element} this
8186          */
8187         setPositioning : function(pc){
8188             this.applyStyles(pc);
8189             if(pc.right == "auto"){
8190                 this.dom.style.right = "";
8191             }
8192             if(pc.bottom == "auto"){
8193                 this.dom.style.bottom = "";
8194             }
8195             return this;
8196         },
8197
8198         // private
8199         fixDisplay : function(){
8200             if(this.getStyle("display") == "none"){
8201                 this.setStyle("visibility", "hidden");
8202                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8203                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8204                     this.setStyle("display", "block");
8205                 }
8206             }
8207         },
8208
8209         /**
8210          * Quick set left and top adding default units
8211          * @param {String} left The left CSS property value
8212          * @param {String} top The top CSS property value
8213          * @return {Roo.Element} this
8214          */
8215          setLeftTop : function(left, top){
8216             this.dom.style.left = this.addUnits(left);
8217             this.dom.style.top = this.addUnits(top);
8218             return this;
8219         },
8220
8221         /**
8222          * Move this element relative to its current position.
8223          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8224          * @param {Number} distance How far to move the element in pixels
8225          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8226          * @return {Roo.Element} this
8227          */
8228          move : function(direction, distance, animate){
8229             var xy = this.getXY();
8230             direction = direction.toLowerCase();
8231             switch(direction){
8232                 case "l":
8233                 case "left":
8234                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8235                     break;
8236                case "r":
8237                case "right":
8238                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8239                     break;
8240                case "t":
8241                case "top":
8242                case "up":
8243                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8244                     break;
8245                case "b":
8246                case "bottom":
8247                case "down":
8248                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8249                     break;
8250             }
8251             return this;
8252         },
8253
8254         /**
8255          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8256          * @return {Roo.Element} this
8257          */
8258         clip : function(){
8259             if(!this.isClipped){
8260                this.isClipped = true;
8261                this.originalClip = {
8262                    "o": this.getStyle("overflow"),
8263                    "x": this.getStyle("overflow-x"),
8264                    "y": this.getStyle("overflow-y")
8265                };
8266                this.setStyle("overflow", "hidden");
8267                this.setStyle("overflow-x", "hidden");
8268                this.setStyle("overflow-y", "hidden");
8269             }
8270             return this;
8271         },
8272
8273         /**
8274          *  Return clipping (overflow) to original clipping before clip() was called
8275          * @return {Roo.Element} this
8276          */
8277         unclip : function(){
8278             if(this.isClipped){
8279                 this.isClipped = false;
8280                 var o = this.originalClip;
8281                 if(o.o){this.setStyle("overflow", o.o);}
8282                 if(o.x){this.setStyle("overflow-x", o.x);}
8283                 if(o.y){this.setStyle("overflow-y", o.y);}
8284             }
8285             return this;
8286         },
8287
8288
8289         /**
8290          * Gets the x,y coordinates specified by the anchor position on the element.
8291          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8292          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8293          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8294          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8295          * @return {Array} [x, y] An array containing the element's x and y coordinates
8296          */
8297         getAnchorXY : function(anchor, local, s){
8298             //Passing a different size is useful for pre-calculating anchors,
8299             //especially for anchored animations that change the el size.
8300
8301             var w, h, vp = false;
8302             if(!s){
8303                 var d = this.dom;
8304                 if(d == document.body || d == document){
8305                     vp = true;
8306                     w = D.getViewWidth(); h = D.getViewHeight();
8307                 }else{
8308                     w = this.getWidth(); h = this.getHeight();
8309                 }
8310             }else{
8311                 w = s.width;  h = s.height;
8312             }
8313             var x = 0, y = 0, r = Math.round;
8314             switch((anchor || "tl").toLowerCase()){
8315                 case "c":
8316                     x = r(w*.5);
8317                     y = r(h*.5);
8318                 break;
8319                 case "t":
8320                     x = r(w*.5);
8321                     y = 0;
8322                 break;
8323                 case "l":
8324                     x = 0;
8325                     y = r(h*.5);
8326                 break;
8327                 case "r":
8328                     x = w;
8329                     y = r(h*.5);
8330                 break;
8331                 case "b":
8332                     x = r(w*.5);
8333                     y = h;
8334                 break;
8335                 case "tl":
8336                     x = 0;
8337                     y = 0;
8338                 break;
8339                 case "bl":
8340                     x = 0;
8341                     y = h;
8342                 break;
8343                 case "br":
8344                     x = w;
8345                     y = h;
8346                 break;
8347                 case "tr":
8348                     x = w;
8349                     y = 0;
8350                 break;
8351             }
8352             if(local === true){
8353                 return [x, y];
8354             }
8355             if(vp){
8356                 var sc = this.getScroll();
8357                 return [x + sc.left, y + sc.top];
8358             }
8359             //Add the element's offset xy
8360             var o = this.getXY();
8361             return [x+o[0], y+o[1]];
8362         },
8363
8364         /**
8365          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8366          * supported position values.
8367          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8368          * @param {String} position The position to align to.
8369          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8370          * @return {Array} [x, y]
8371          */
8372         getAlignToXY : function(el, p, o){
8373             el = Roo.get(el);
8374             var d = this.dom;
8375             if(!el.dom){
8376                 throw "Element.alignTo with an element that doesn't exist";
8377             }
8378             var c = false; //constrain to viewport
8379             var p1 = "", p2 = "";
8380             o = o || [0,0];
8381
8382             if(!p){
8383                 p = "tl-bl";
8384             }else if(p == "?"){
8385                 p = "tl-bl?";
8386             }else if(p.indexOf("-") == -1){
8387                 p = "tl-" + p;
8388             }
8389             p = p.toLowerCase();
8390             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8391             if(!m){
8392                throw "Element.alignTo with an invalid alignment " + p;
8393             }
8394             p1 = m[1]; p2 = m[2]; c = !!m[3];
8395
8396             //Subtract the aligned el's internal xy from the target's offset xy
8397             //plus custom offset to get the aligned el's new offset xy
8398             var a1 = this.getAnchorXY(p1, true);
8399             var a2 = el.getAnchorXY(p2, false);
8400             var x = a2[0] - a1[0] + o[0];
8401             var y = a2[1] - a1[1] + o[1];
8402             if(c){
8403                 //constrain the aligned el to viewport if necessary
8404                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8405                 // 5px of margin for ie
8406                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8407
8408                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8409                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8410                 //otherwise swap the aligned el to the opposite border of the target.
8411                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8412                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8413                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8414                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8415
8416                var doc = document;
8417                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8418                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8419
8420                if((x+w) > dw + scrollX){
8421                     x = swapX ? r.left-w : dw+scrollX-w;
8422                 }
8423                if(x < scrollX){
8424                    x = swapX ? r.right : scrollX;
8425                }
8426                if((y+h) > dh + scrollY){
8427                     y = swapY ? r.top-h : dh+scrollY-h;
8428                 }
8429                if (y < scrollY){
8430                    y = swapY ? r.bottom : scrollY;
8431                }
8432             }
8433             return [x,y];
8434         },
8435
8436         // private
8437         getConstrainToXY : function(){
8438             var os = {top:0, left:0, bottom:0, right: 0};
8439
8440             return function(el, local, offsets, proposedXY){
8441                 el = Roo.get(el);
8442                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8443
8444                 var vw, vh, vx = 0, vy = 0;
8445                 if(el.dom == document.body || el.dom == document){
8446                     vw = Roo.lib.Dom.getViewWidth();
8447                     vh = Roo.lib.Dom.getViewHeight();
8448                 }else{
8449                     vw = el.dom.clientWidth;
8450                     vh = el.dom.clientHeight;
8451                     if(!local){
8452                         var vxy = el.getXY();
8453                         vx = vxy[0];
8454                         vy = vxy[1];
8455                     }
8456                 }
8457
8458                 var s = el.getScroll();
8459
8460                 vx += offsets.left + s.left;
8461                 vy += offsets.top + s.top;
8462
8463                 vw -= offsets.right;
8464                 vh -= offsets.bottom;
8465
8466                 var vr = vx+vw;
8467                 var vb = vy+vh;
8468
8469                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8470                 var x = xy[0], y = xy[1];
8471                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8472
8473                 // only move it if it needs it
8474                 var moved = false;
8475
8476                 // first validate right/bottom
8477                 if((x + w) > vr){
8478                     x = vr - w;
8479                     moved = true;
8480                 }
8481                 if((y + h) > vb){
8482                     y = vb - h;
8483                     moved = true;
8484                 }
8485                 // then make sure top/left isn't negative
8486                 if(x < vx){
8487                     x = vx;
8488                     moved = true;
8489                 }
8490                 if(y < vy){
8491                     y = vy;
8492                     moved = true;
8493                 }
8494                 return moved ? [x, y] : false;
8495             };
8496         }(),
8497
8498         // private
8499         adjustForConstraints : function(xy, parent, offsets){
8500             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8501         },
8502
8503         /**
8504          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8505          * document it aligns it to the viewport.
8506          * The position parameter is optional, and can be specified in any one of the following formats:
8507          * <ul>
8508          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8509          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8510          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8511          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8512          *   <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
8513          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8514          * </ul>
8515          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8516          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8517          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8518          * that specified in order to enforce the viewport constraints.
8519          * Following are all of the supported anchor positions:
8520     <pre>
8521     Value  Description
8522     -----  -----------------------------
8523     tl     The top left corner (default)
8524     t      The center of the top edge
8525     tr     The top right corner
8526     l      The center of the left edge
8527     c      In the center of the element
8528     r      The center of the right edge
8529     bl     The bottom left corner
8530     b      The center of the bottom edge
8531     br     The bottom right corner
8532     </pre>
8533     Example Usage:
8534     <pre><code>
8535     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8536     el.alignTo("other-el");
8537
8538     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8539     el.alignTo("other-el", "tr?");
8540
8541     // align the bottom right corner of el with the center left edge of other-el
8542     el.alignTo("other-el", "br-l?");
8543
8544     // align the center of el with the bottom left corner of other-el and
8545     // adjust the x position by -6 pixels (and the y position by 0)
8546     el.alignTo("other-el", "c-bl", [-6, 0]);
8547     </code></pre>
8548          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8549          * @param {String} position The position to align to.
8550          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8551          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8552          * @return {Roo.Element} this
8553          */
8554         alignTo : function(element, position, offsets, animate){
8555             var xy = this.getAlignToXY(element, position, offsets);
8556             this.setXY(xy, this.preanim(arguments, 3));
8557             return this;
8558         },
8559
8560         /**
8561          * Anchors an element to another element and realigns it when the window is resized.
8562          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8563          * @param {String} position The position to align to.
8564          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8565          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8566          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8567          * is a number, it is used as the buffer delay (defaults to 50ms).
8568          * @param {Function} callback The function to call after the animation finishes
8569          * @return {Roo.Element} this
8570          */
8571         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8572             var action = function(){
8573                 this.alignTo(el, alignment, offsets, animate);
8574                 Roo.callback(callback, this);
8575             };
8576             Roo.EventManager.onWindowResize(action, this);
8577             var tm = typeof monitorScroll;
8578             if(tm != 'undefined'){
8579                 Roo.EventManager.on(window, 'scroll', action, this,
8580                     {buffer: tm == 'number' ? monitorScroll : 50});
8581             }
8582             action.call(this); // align immediately
8583             return this;
8584         },
8585         /**
8586          * Clears any opacity settings from this element. Required in some cases for IE.
8587          * @return {Roo.Element} this
8588          */
8589         clearOpacity : function(){
8590             if (window.ActiveXObject) {
8591                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8592                     this.dom.style.filter = "";
8593                 }
8594             } else {
8595                 this.dom.style.opacity = "";
8596                 this.dom.style["-moz-opacity"] = "";
8597                 this.dom.style["-khtml-opacity"] = "";
8598             }
8599             return this;
8600         },
8601
8602         /**
8603          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8604          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8605          * @return {Roo.Element} this
8606          */
8607         hide : function(animate){
8608             this.setVisible(false, this.preanim(arguments, 0));
8609             return this;
8610         },
8611
8612         /**
8613         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8614         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8615          * @return {Roo.Element} this
8616          */
8617         show : function(animate){
8618             this.setVisible(true, this.preanim(arguments, 0));
8619             return this;
8620         },
8621
8622         /**
8623          * @private Test if size has a unit, otherwise appends the default
8624          */
8625         addUnits : function(size){
8626             return Roo.Element.addUnits(size, this.defaultUnit);
8627         },
8628
8629         /**
8630          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8631          * @return {Roo.Element} this
8632          */
8633         beginMeasure : function(){
8634             var el = this.dom;
8635             if(el.offsetWidth || el.offsetHeight){
8636                 return this; // offsets work already
8637             }
8638             var changed = [];
8639             var p = this.dom, b = document.body; // start with this element
8640             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8641                 var pe = Roo.get(p);
8642                 if(pe.getStyle('display') == 'none'){
8643                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8644                     p.style.visibility = "hidden";
8645                     p.style.display = "block";
8646                 }
8647                 p = p.parentNode;
8648             }
8649             this._measureChanged = changed;
8650             return this;
8651
8652         },
8653
8654         /**
8655          * Restores displays to before beginMeasure was called
8656          * @return {Roo.Element} this
8657          */
8658         endMeasure : function(){
8659             var changed = this._measureChanged;
8660             if(changed){
8661                 for(var i = 0, len = changed.length; i < len; i++) {
8662                     var r = changed[i];
8663                     r.el.style.visibility = r.visibility;
8664                     r.el.style.display = "none";
8665                 }
8666                 this._measureChanged = null;
8667             }
8668             return this;
8669         },
8670
8671         /**
8672         * Update the innerHTML of this element, optionally searching for and processing scripts
8673         * @param {String} html The new HTML
8674         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8675         * @param {Function} callback For async script loading you can be noticed when the update completes
8676         * @return {Roo.Element} this
8677          */
8678         update : function(html, loadScripts, callback){
8679             if(typeof html == "undefined"){
8680                 html = "";
8681             }
8682             if(loadScripts !== true){
8683                 this.dom.innerHTML = html;
8684                 if(typeof callback == "function"){
8685                     callback();
8686                 }
8687                 return this;
8688             }
8689             var id = Roo.id();
8690             var dom = this.dom;
8691
8692             html += '<span id="' + id + '"></span>';
8693
8694             E.onAvailable(id, function(){
8695                 var hd = document.getElementsByTagName("head")[0];
8696                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8697                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8698                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8699
8700                 var match;
8701                 while(match = re.exec(html)){
8702                     var attrs = match[1];
8703                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8704                     if(srcMatch && srcMatch[2]){
8705                        var s = document.createElement("script");
8706                        s.src = srcMatch[2];
8707                        var typeMatch = attrs.match(typeRe);
8708                        if(typeMatch && typeMatch[2]){
8709                            s.type = typeMatch[2];
8710                        }
8711                        hd.appendChild(s);
8712                     }else if(match[2] && match[2].length > 0){
8713                         if(window.execScript) {
8714                            window.execScript(match[2]);
8715                         } else {
8716                             /**
8717                              * eval:var:id
8718                              * eval:var:dom
8719                              * eval:var:html
8720                              * 
8721                              */
8722                            window.eval(match[2]);
8723                         }
8724                     }
8725                 }
8726                 var el = document.getElementById(id);
8727                 if(el){el.parentNode.removeChild(el);}
8728                 if(typeof callback == "function"){
8729                     callback();
8730                 }
8731             });
8732             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8733             return this;
8734         },
8735
8736         /**
8737          * Direct access to the UpdateManager update() method (takes the same parameters).
8738          * @param {String/Function} url The url for this request or a function to call to get the url
8739          * @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}
8740          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8741          * @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.
8742          * @return {Roo.Element} this
8743          */
8744         load : function(){
8745             var um = this.getUpdateManager();
8746             um.update.apply(um, arguments);
8747             return this;
8748         },
8749
8750         /**
8751         * Gets this element's UpdateManager
8752         * @return {Roo.UpdateManager} The UpdateManager
8753         */
8754         getUpdateManager : function(){
8755             if(!this.updateManager){
8756                 this.updateManager = new Roo.UpdateManager(this);
8757             }
8758             return this.updateManager;
8759         },
8760
8761         /**
8762          * Disables text selection for this element (normalized across browsers)
8763          * @return {Roo.Element} this
8764          */
8765         unselectable : function(){
8766             this.dom.unselectable = "on";
8767             this.swallowEvent("selectstart", true);
8768             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8769             this.addClass("x-unselectable");
8770             return this;
8771         },
8772
8773         /**
8774         * Calculates the x, y to center this element on the screen
8775         * @return {Array} The x, y values [x, y]
8776         */
8777         getCenterXY : function(){
8778             return this.getAlignToXY(document, 'c-c');
8779         },
8780
8781         /**
8782         * Centers the Element in either the viewport, or another Element.
8783         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8784         */
8785         center : function(centerIn){
8786             this.alignTo(centerIn || document, 'c-c');
8787             return this;
8788         },
8789
8790         /**
8791          * Tests various css rules/browsers to determine if this element uses a border box
8792          * @return {Boolean}
8793          */
8794         isBorderBox : function(){
8795             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8796         },
8797
8798         /**
8799          * Return a box {x, y, width, height} that can be used to set another elements
8800          * size/location to match this element.
8801          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8802          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8803          * @return {Object} box An object in the format {x, y, width, height}
8804          */
8805         getBox : function(contentBox, local){
8806             var xy;
8807             if(!local){
8808                 xy = this.getXY();
8809             }else{
8810                 var left = parseInt(this.getStyle("left"), 10) || 0;
8811                 var top = parseInt(this.getStyle("top"), 10) || 0;
8812                 xy = [left, top];
8813             }
8814             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8815             if(!contentBox){
8816                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8817             }else{
8818                 var l = this.getBorderWidth("l")+this.getPadding("l");
8819                 var r = this.getBorderWidth("r")+this.getPadding("r");
8820                 var t = this.getBorderWidth("t")+this.getPadding("t");
8821                 var b = this.getBorderWidth("b")+this.getPadding("b");
8822                 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)};
8823             }
8824             bx.right = bx.x + bx.width;
8825             bx.bottom = bx.y + bx.height;
8826             return bx;
8827         },
8828
8829         /**
8830          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8831          for more information about the sides.
8832          * @param {String} sides
8833          * @return {Number}
8834          */
8835         getFrameWidth : function(sides, onlyContentBox){
8836             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8837         },
8838
8839         /**
8840          * 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.
8841          * @param {Object} box The box to fill {x, y, width, height}
8842          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8843          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8844          * @return {Roo.Element} this
8845          */
8846         setBox : function(box, adjust, animate){
8847             var w = box.width, h = box.height;
8848             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8849                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8850                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8851             }
8852             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8853             return this;
8854         },
8855
8856         /**
8857          * Forces the browser to repaint this element
8858          * @return {Roo.Element} this
8859          */
8860          repaint : function(){
8861             var dom = this.dom;
8862             this.addClass("x-repaint");
8863             setTimeout(function(){
8864                 Roo.get(dom).removeClass("x-repaint");
8865             }, 1);
8866             return this;
8867         },
8868
8869         /**
8870          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8871          * then it returns the calculated width of the sides (see getPadding)
8872          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8873          * @return {Object/Number}
8874          */
8875         getMargins : function(side){
8876             if(!side){
8877                 return {
8878                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8879                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8880                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8881                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8882                 };
8883             }else{
8884                 return this.addStyles(side, El.margins);
8885              }
8886         },
8887
8888         // private
8889         addStyles : function(sides, styles){
8890             var val = 0, v, w;
8891             for(var i = 0, len = sides.length; i < len; i++){
8892                 v = this.getStyle(styles[sides.charAt(i)]);
8893                 if(v){
8894                      w = parseInt(v, 10);
8895                      if(w){ val += w; }
8896                 }
8897             }
8898             return val;
8899         },
8900
8901         /**
8902          * Creates a proxy element of this element
8903          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8904          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8905          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8906          * @return {Roo.Element} The new proxy element
8907          */
8908         createProxy : function(config, renderTo, matchBox){
8909             if(renderTo){
8910                 renderTo = Roo.getDom(renderTo);
8911             }else{
8912                 renderTo = document.body;
8913             }
8914             config = typeof config == "object" ?
8915                 config : {tag : "div", cls: config};
8916             var proxy = Roo.DomHelper.append(renderTo, config, true);
8917             if(matchBox){
8918                proxy.setBox(this.getBox());
8919             }
8920             return proxy;
8921         },
8922
8923         /**
8924          * Puts a mask over this element to disable user interaction. Requires core.css.
8925          * This method can only be applied to elements which accept child nodes.
8926          * @param {String} msg (optional) A message to display in the mask
8927          * @param {String} msgCls (optional) A css class to apply to the msg element
8928          * @return {Element} The mask  element
8929          */
8930         mask : function(msg, msgCls)
8931         {
8932             if(this.getStyle("position") == "static"){
8933                 this.setStyle("position", "relative");
8934             }
8935             if(!this._mask){
8936                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8937             }
8938             this.addClass("x-masked");
8939             this._mask.setDisplayed(true);
8940             
8941             // we wander
8942             var z = 0;
8943             var dom = this.dom
8944             while (dom && dom.style) {
8945                 if (!isNaN(parseInt(dom.style.zIndex))) {
8946                     z = Math.max(z, parseInt(dom.style.zIndex));
8947                 }
8948                 dom = dom.parentNode;
8949             }
8950             // if we are masking the body - then it hides everything..
8951             if (this.dom == document.body) {
8952                 z = 1000000;
8953                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8954                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8955             }
8956            
8957             if(typeof msg == 'string'){
8958                 if(!this._maskMsg){
8959                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8960                 }
8961                 var mm = this._maskMsg;
8962                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8963                 mm.dom.firstChild.innerHTML = msg;
8964                 mm.setDisplayed(true);
8965                 mm.center(this);
8966                 mm.setStyle('z-index', z + 102);
8967             }
8968             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8969                 this._mask.setHeight(this.getHeight());
8970             }
8971             this._mask.setStyle('z-index', z + 100);
8972             
8973             return this._mask;
8974         },
8975
8976         /**
8977          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8978          * it is cached for reuse.
8979          */
8980         unmask : function(removeEl){
8981             if(this._mask){
8982                 if(removeEl === true){
8983                     this._mask.remove();
8984                     delete this._mask;
8985                     if(this._maskMsg){
8986                         this._maskMsg.remove();
8987                         delete this._maskMsg;
8988                     }
8989                 }else{
8990                     this._mask.setDisplayed(false);
8991                     if(this._maskMsg){
8992                         this._maskMsg.setDisplayed(false);
8993                     }
8994                 }
8995             }
8996             this.removeClass("x-masked");
8997         },
8998
8999         /**
9000          * Returns true if this element is masked
9001          * @return {Boolean}
9002          */
9003         isMasked : function(){
9004             return this._mask && this._mask.isVisible();
9005         },
9006
9007         /**
9008          * Creates an iframe shim for this element to keep selects and other windowed objects from
9009          * showing through.
9010          * @return {Roo.Element} The new shim element
9011          */
9012         createShim : function(){
9013             var el = document.createElement('iframe');
9014             el.frameBorder = 'no';
9015             el.className = 'roo-shim';
9016             if(Roo.isIE && Roo.isSecure){
9017                 el.src = Roo.SSL_SECURE_URL;
9018             }
9019             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9020             shim.autoBoxAdjust = false;
9021             return shim;
9022         },
9023
9024         /**
9025          * Removes this element from the DOM and deletes it from the cache
9026          */
9027         remove : function(){
9028             if(this.dom.parentNode){
9029                 this.dom.parentNode.removeChild(this.dom);
9030             }
9031             delete El.cache[this.dom.id];
9032         },
9033
9034         /**
9035          * Sets up event handlers to add and remove a css class when the mouse is over this element
9036          * @param {String} className
9037          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9038          * mouseout events for children elements
9039          * @return {Roo.Element} this
9040          */
9041         addClassOnOver : function(className, preventFlicker){
9042             this.on("mouseover", function(){
9043                 Roo.fly(this, '_internal').addClass(className);
9044             }, this.dom);
9045             var removeFn = function(e){
9046                 if(preventFlicker !== true || !e.within(this, true)){
9047                     Roo.fly(this, '_internal').removeClass(className);
9048                 }
9049             };
9050             this.on("mouseout", removeFn, this.dom);
9051             return this;
9052         },
9053
9054         /**
9055          * Sets up event handlers to add and remove a css class when this element has the focus
9056          * @param {String} className
9057          * @return {Roo.Element} this
9058          */
9059         addClassOnFocus : function(className){
9060             this.on("focus", function(){
9061                 Roo.fly(this, '_internal').addClass(className);
9062             }, this.dom);
9063             this.on("blur", function(){
9064                 Roo.fly(this, '_internal').removeClass(className);
9065             }, this.dom);
9066             return this;
9067         },
9068         /**
9069          * 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)
9070          * @param {String} className
9071          * @return {Roo.Element} this
9072          */
9073         addClassOnClick : function(className){
9074             var dom = this.dom;
9075             this.on("mousedown", function(){
9076                 Roo.fly(dom, '_internal').addClass(className);
9077                 var d = Roo.get(document);
9078                 var fn = function(){
9079                     Roo.fly(dom, '_internal').removeClass(className);
9080                     d.removeListener("mouseup", fn);
9081                 };
9082                 d.on("mouseup", fn);
9083             });
9084             return this;
9085         },
9086
9087         /**
9088          * Stops the specified event from bubbling and optionally prevents the default action
9089          * @param {String} eventName
9090          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9091          * @return {Roo.Element} this
9092          */
9093         swallowEvent : function(eventName, preventDefault){
9094             var fn = function(e){
9095                 e.stopPropagation();
9096                 if(preventDefault){
9097                     e.preventDefault();
9098                 }
9099             };
9100             if(eventName instanceof Array){
9101                 for(var i = 0, len = eventName.length; i < len; i++){
9102                      this.on(eventName[i], fn);
9103                 }
9104                 return this;
9105             }
9106             this.on(eventName, fn);
9107             return this;
9108         },
9109
9110         /**
9111          * @private
9112          */
9113       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9114
9115         /**
9116          * Sizes this element to its parent element's dimensions performing
9117          * neccessary box adjustments.
9118          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9119          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9120          * @return {Roo.Element} this
9121          */
9122         fitToParent : function(monitorResize, targetParent) {
9123           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9124           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9125           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9126             return;
9127           }
9128           var p = Roo.get(targetParent || this.dom.parentNode);
9129           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9130           if (monitorResize === true) {
9131             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9132             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9133           }
9134           return this;
9135         },
9136
9137         /**
9138          * Gets the next sibling, skipping text nodes
9139          * @return {HTMLElement} The next sibling or null
9140          */
9141         getNextSibling : function(){
9142             var n = this.dom.nextSibling;
9143             while(n && n.nodeType != 1){
9144                 n = n.nextSibling;
9145             }
9146             return n;
9147         },
9148
9149         /**
9150          * Gets the previous sibling, skipping text nodes
9151          * @return {HTMLElement} The previous sibling or null
9152          */
9153         getPrevSibling : function(){
9154             var n = this.dom.previousSibling;
9155             while(n && n.nodeType != 1){
9156                 n = n.previousSibling;
9157             }
9158             return n;
9159         },
9160
9161
9162         /**
9163          * Appends the passed element(s) to this element
9164          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9165          * @return {Roo.Element} this
9166          */
9167         appendChild: function(el){
9168             el = Roo.get(el);
9169             el.appendTo(this);
9170             return this;
9171         },
9172
9173         /**
9174          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9175          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9176          * automatically generated with the specified attributes.
9177          * @param {HTMLElement} insertBefore (optional) a child element of this element
9178          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9179          * @return {Roo.Element} The new child element
9180          */
9181         createChild: function(config, insertBefore, returnDom){
9182             config = config || {tag:'div'};
9183             if(insertBefore){
9184                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9185             }
9186             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9187         },
9188
9189         /**
9190          * Appends this element to the passed element
9191          * @param {String/HTMLElement/Element} el The new parent element
9192          * @return {Roo.Element} this
9193          */
9194         appendTo: function(el){
9195             el = Roo.getDom(el);
9196             el.appendChild(this.dom);
9197             return this;
9198         },
9199
9200         /**
9201          * Inserts this element before the passed element in the DOM
9202          * @param {String/HTMLElement/Element} el The element to insert before
9203          * @return {Roo.Element} this
9204          */
9205         insertBefore: function(el){
9206             el = Roo.getDom(el);
9207             el.parentNode.insertBefore(this.dom, el);
9208             return this;
9209         },
9210
9211         /**
9212          * Inserts this element after the passed element in the DOM
9213          * @param {String/HTMLElement/Element} el The element to insert after
9214          * @return {Roo.Element} this
9215          */
9216         insertAfter: function(el){
9217             el = Roo.getDom(el);
9218             el.parentNode.insertBefore(this.dom, el.nextSibling);
9219             return this;
9220         },
9221
9222         /**
9223          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9224          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9225          * @return {Roo.Element} The new child
9226          */
9227         insertFirst: function(el, returnDom){
9228             el = el || {};
9229             if(typeof el == 'object' && !el.nodeType){ // dh config
9230                 return this.createChild(el, this.dom.firstChild, returnDom);
9231             }else{
9232                 el = Roo.getDom(el);
9233                 this.dom.insertBefore(el, this.dom.firstChild);
9234                 return !returnDom ? Roo.get(el) : el;
9235             }
9236         },
9237
9238         /**
9239          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9240          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9241          * @param {String} where (optional) 'before' or 'after' defaults to before
9242          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9243          * @return {Roo.Element} the inserted Element
9244          */
9245         insertSibling: function(el, where, returnDom){
9246             where = where ? where.toLowerCase() : 'before';
9247             el = el || {};
9248             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9249
9250             if(typeof el == 'object' && !el.nodeType){ // dh config
9251                 if(where == 'after' && !this.dom.nextSibling){
9252                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9253                 }else{
9254                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9255                 }
9256
9257             }else{
9258                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9259                             where == 'before' ? this.dom : this.dom.nextSibling);
9260                 if(!returnDom){
9261                     rt = Roo.get(rt);
9262                 }
9263             }
9264             return rt;
9265         },
9266
9267         /**
9268          * Creates and wraps this element with another element
9269          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9270          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9271          * @return {HTMLElement/Element} The newly created wrapper element
9272          */
9273         wrap: function(config, returnDom){
9274             if(!config){
9275                 config = {tag: "div"};
9276             }
9277             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9278             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9279             return newEl;
9280         },
9281
9282         /**
9283          * Replaces the passed element with this element
9284          * @param {String/HTMLElement/Element} el The element to replace
9285          * @return {Roo.Element} this
9286          */
9287         replace: function(el){
9288             el = Roo.get(el);
9289             this.insertBefore(el);
9290             el.remove();
9291             return this;
9292         },
9293
9294         /**
9295          * Inserts an html fragment into this element
9296          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9297          * @param {String} html The HTML fragment
9298          * @param {Boolean} returnEl True to return an Roo.Element
9299          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9300          */
9301         insertHtml : function(where, html, returnEl){
9302             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9303             return returnEl ? Roo.get(el) : el;
9304         },
9305
9306         /**
9307          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9308          * @param {Object} o The object with the attributes
9309          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9310          * @return {Roo.Element} this
9311          */
9312         set : function(o, useSet){
9313             var el = this.dom;
9314             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9315             for(var attr in o){
9316                 if(attr == "style" || typeof o[attr] == "function") continue;
9317                 if(attr=="cls"){
9318                     el.className = o["cls"];
9319                 }else{
9320                     if(useSet) el.setAttribute(attr, o[attr]);
9321                     else el[attr] = o[attr];
9322                 }
9323             }
9324             if(o.style){
9325                 Roo.DomHelper.applyStyles(el, o.style);
9326             }
9327             return this;
9328         },
9329
9330         /**
9331          * Convenience method for constructing a KeyMap
9332          * @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:
9333          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9334          * @param {Function} fn The function to call
9335          * @param {Object} scope (optional) The scope of the function
9336          * @return {Roo.KeyMap} The KeyMap created
9337          */
9338         addKeyListener : function(key, fn, scope){
9339             var config;
9340             if(typeof key != "object" || key instanceof Array){
9341                 config = {
9342                     key: key,
9343                     fn: fn,
9344                     scope: scope
9345                 };
9346             }else{
9347                 config = {
9348                     key : key.key,
9349                     shift : key.shift,
9350                     ctrl : key.ctrl,
9351                     alt : key.alt,
9352                     fn: fn,
9353                     scope: scope
9354                 };
9355             }
9356             return new Roo.KeyMap(this, config);
9357         },
9358
9359         /**
9360          * Creates a KeyMap for this element
9361          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9362          * @return {Roo.KeyMap} The KeyMap created
9363          */
9364         addKeyMap : function(config){
9365             return new Roo.KeyMap(this, config);
9366         },
9367
9368         /**
9369          * Returns true if this element is scrollable.
9370          * @return {Boolean}
9371          */
9372          isScrollable : function(){
9373             var dom = this.dom;
9374             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9375         },
9376
9377         /**
9378          * 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().
9379          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9380          * @param {Number} value The new scroll value
9381          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9382          * @return {Element} this
9383          */
9384
9385         scrollTo : function(side, value, animate){
9386             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9387             if(!animate || !A){
9388                 this.dom[prop] = value;
9389             }else{
9390                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9391                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9392             }
9393             return this;
9394         },
9395
9396         /**
9397          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9398          * within this element's scrollable range.
9399          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9400          * @param {Number} distance How far to scroll the element in pixels
9401          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9402          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9403          * was scrolled as far as it could go.
9404          */
9405          scroll : function(direction, distance, animate){
9406              if(!this.isScrollable()){
9407                  return;
9408              }
9409              var el = this.dom;
9410              var l = el.scrollLeft, t = el.scrollTop;
9411              var w = el.scrollWidth, h = el.scrollHeight;
9412              var cw = el.clientWidth, ch = el.clientHeight;
9413              direction = direction.toLowerCase();
9414              var scrolled = false;
9415              var a = this.preanim(arguments, 2);
9416              switch(direction){
9417                  case "l":
9418                  case "left":
9419                      if(w - l > cw){
9420                          var v = Math.min(l + distance, w-cw);
9421                          this.scrollTo("left", v, a);
9422                          scrolled = true;
9423                      }
9424                      break;
9425                 case "r":
9426                 case "right":
9427                      if(l > 0){
9428                          var v = Math.max(l - distance, 0);
9429                          this.scrollTo("left", v, a);
9430                          scrolled = true;
9431                      }
9432                      break;
9433                 case "t":
9434                 case "top":
9435                 case "up":
9436                      if(t > 0){
9437                          var v = Math.max(t - distance, 0);
9438                          this.scrollTo("top", v, a);
9439                          scrolled = true;
9440                      }
9441                      break;
9442                 case "b":
9443                 case "bottom":
9444                 case "down":
9445                      if(h - t > ch){
9446                          var v = Math.min(t + distance, h-ch);
9447                          this.scrollTo("top", v, a);
9448                          scrolled = true;
9449                      }
9450                      break;
9451              }
9452              return scrolled;
9453         },
9454
9455         /**
9456          * Translates the passed page coordinates into left/top css values for this element
9457          * @param {Number/Array} x The page x or an array containing [x, y]
9458          * @param {Number} y The page y
9459          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9460          */
9461         translatePoints : function(x, y){
9462             if(typeof x == 'object' || x instanceof Array){
9463                 y = x[1]; x = x[0];
9464             }
9465             var p = this.getStyle('position');
9466             var o = this.getXY();
9467
9468             var l = parseInt(this.getStyle('left'), 10);
9469             var t = parseInt(this.getStyle('top'), 10);
9470
9471             if(isNaN(l)){
9472                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9473             }
9474             if(isNaN(t)){
9475                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9476             }
9477
9478             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9479         },
9480
9481         /**
9482          * Returns the current scroll position of the element.
9483          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9484          */
9485         getScroll : function(){
9486             var d = this.dom, doc = document;
9487             if(d == doc || d == doc.body){
9488                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9489                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9490                 return {left: l, top: t};
9491             }else{
9492                 return {left: d.scrollLeft, top: d.scrollTop};
9493             }
9494         },
9495
9496         /**
9497          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9498          * are convert to standard 6 digit hex color.
9499          * @param {String} attr The css attribute
9500          * @param {String} defaultValue The default value to use when a valid color isn't found
9501          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9502          * YUI color anims.
9503          */
9504         getColor : function(attr, defaultValue, prefix){
9505             var v = this.getStyle(attr);
9506             if(!v || v == "transparent" || v == "inherit") {
9507                 return defaultValue;
9508             }
9509             var color = typeof prefix == "undefined" ? "#" : prefix;
9510             if(v.substr(0, 4) == "rgb("){
9511                 var rvs = v.slice(4, v.length -1).split(",");
9512                 for(var i = 0; i < 3; i++){
9513                     var h = parseInt(rvs[i]).toString(16);
9514                     if(h < 16){
9515                         h = "0" + h;
9516                     }
9517                     color += h;
9518                 }
9519             } else {
9520                 if(v.substr(0, 1) == "#"){
9521                     if(v.length == 4) {
9522                         for(var i = 1; i < 4; i++){
9523                             var c = v.charAt(i);
9524                             color +=  c + c;
9525                         }
9526                     }else if(v.length == 7){
9527                         color += v.substr(1);
9528                     }
9529                 }
9530             }
9531             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9532         },
9533
9534         /**
9535          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9536          * gradient background, rounded corners and a 4-way shadow.
9537          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9538          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9539          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9540          * @return {Roo.Element} this
9541          */
9542         boxWrap : function(cls){
9543             cls = cls || 'x-box';
9544             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9545             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9546             return el;
9547         },
9548
9549         /**
9550          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9551          * @param {String} namespace The namespace in which to look for the attribute
9552          * @param {String} name The attribute name
9553          * @return {String} The attribute value
9554          */
9555         getAttributeNS : Roo.isIE ? function(ns, name){
9556             var d = this.dom;
9557             var type = typeof d[ns+":"+name];
9558             if(type != 'undefined' && type != 'unknown'){
9559                 return d[ns+":"+name];
9560             }
9561             return d[name];
9562         } : function(ns, name){
9563             var d = this.dom;
9564             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9565         },
9566         
9567         
9568         /**
9569          * Sets or Returns the value the dom attribute value
9570          * @param {String} name The attribute name
9571          * @param {String} value (optional) The value to set the attribute to
9572          * @return {String} The attribute value
9573          */
9574         attr : function(name){
9575             if (arguments.length > 1) {
9576                 this.dom.setAttribute(name, arguments[1]);
9577                 return arguments[1];
9578             }
9579             if (!this.dom.hasAttribute(name)) {
9580                 return undefined;
9581             }
9582             return this.dom.getAttribute(name);
9583         }
9584         
9585         
9586         
9587     };
9588
9589     var ep = El.prototype;
9590
9591     /**
9592      * Appends an event handler (Shorthand for addListener)
9593      * @param {String}   eventName     The type of event to append
9594      * @param {Function} fn        The method the event invokes
9595      * @param {Object} scope       (optional) The scope (this object) of the fn
9596      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9597      * @method
9598      */
9599     ep.on = ep.addListener;
9600         // backwards compat
9601     ep.mon = ep.addListener;
9602
9603     /**
9604      * Removes an event handler from this element (shorthand for removeListener)
9605      * @param {String} eventName the type of event to remove
9606      * @param {Function} fn the method the event invokes
9607      * @return {Roo.Element} this
9608      * @method
9609      */
9610     ep.un = ep.removeListener;
9611
9612     /**
9613      * true to automatically adjust width and height settings for box-model issues (default to true)
9614      */
9615     ep.autoBoxAdjust = true;
9616
9617     // private
9618     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9619
9620     // private
9621     El.addUnits = function(v, defaultUnit){
9622         if(v === "" || v == "auto"){
9623             return v;
9624         }
9625         if(v === undefined){
9626             return '';
9627         }
9628         if(typeof v == "number" || !El.unitPattern.test(v)){
9629             return v + (defaultUnit || 'px');
9630         }
9631         return v;
9632     };
9633
9634     // special markup used throughout Roo when box wrapping elements
9635     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>';
9636     /**
9637      * Visibility mode constant - Use visibility to hide element
9638      * @static
9639      * @type Number
9640      */
9641     El.VISIBILITY = 1;
9642     /**
9643      * Visibility mode constant - Use display to hide element
9644      * @static
9645      * @type Number
9646      */
9647     El.DISPLAY = 2;
9648
9649     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9650     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9651     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9652
9653
9654
9655     /**
9656      * @private
9657      */
9658     El.cache = {};
9659
9660     var docEl;
9661
9662     /**
9663      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9664      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9665      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9666      * @return {Element} The Element object
9667      * @static
9668      */
9669     El.get = function(el){
9670         var ex, elm, id;
9671         if(!el){ return null; }
9672         if(typeof el == "string"){ // element id
9673             if(!(elm = document.getElementById(el))){
9674                 return null;
9675             }
9676             if(ex = El.cache[el]){
9677                 ex.dom = elm;
9678             }else{
9679                 ex = El.cache[el] = new El(elm);
9680             }
9681             return ex;
9682         }else if(el.tagName){ // dom element
9683             if(!(id = el.id)){
9684                 id = Roo.id(el);
9685             }
9686             if(ex = El.cache[id]){
9687                 ex.dom = el;
9688             }else{
9689                 ex = El.cache[id] = new El(el);
9690             }
9691             return ex;
9692         }else if(el instanceof El){
9693             if(el != docEl){
9694                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9695                                                               // catch case where it hasn't been appended
9696                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9697             }
9698             return el;
9699         }else if(el.isComposite){
9700             return el;
9701         }else if(el instanceof Array){
9702             return El.select(el);
9703         }else if(el == document){
9704             // create a bogus element object representing the document object
9705             if(!docEl){
9706                 var f = function(){};
9707                 f.prototype = El.prototype;
9708                 docEl = new f();
9709                 docEl.dom = document;
9710             }
9711             return docEl;
9712         }
9713         return null;
9714     };
9715
9716     // private
9717     El.uncache = function(el){
9718         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9719             if(a[i]){
9720                 delete El.cache[a[i].id || a[i]];
9721             }
9722         }
9723     };
9724
9725     // private
9726     // Garbage collection - uncache elements/purge listeners on orphaned elements
9727     // so we don't hold a reference and cause the browser to retain them
9728     El.garbageCollect = function(){
9729         if(!Roo.enableGarbageCollector){
9730             clearInterval(El.collectorThread);
9731             return;
9732         }
9733         for(var eid in El.cache){
9734             var el = El.cache[eid], d = el.dom;
9735             // -------------------------------------------------------
9736             // Determining what is garbage:
9737             // -------------------------------------------------------
9738             // !d
9739             // dom node is null, definitely garbage
9740             // -------------------------------------------------------
9741             // !d.parentNode
9742             // no parentNode == direct orphan, definitely garbage
9743             // -------------------------------------------------------
9744             // !d.offsetParent && !document.getElementById(eid)
9745             // display none elements have no offsetParent so we will
9746             // also try to look it up by it's id. However, check
9747             // offsetParent first so we don't do unneeded lookups.
9748             // This enables collection of elements that are not orphans
9749             // directly, but somewhere up the line they have an orphan
9750             // parent.
9751             // -------------------------------------------------------
9752             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9753                 delete El.cache[eid];
9754                 if(d && Roo.enableListenerCollection){
9755                     E.purgeElement(d);
9756                 }
9757             }
9758         }
9759     }
9760     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9761
9762
9763     // dom is optional
9764     El.Flyweight = function(dom){
9765         this.dom = dom;
9766     };
9767     El.Flyweight.prototype = El.prototype;
9768
9769     El._flyweights = {};
9770     /**
9771      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9772      * the dom node can be overwritten by other code.
9773      * @param {String/HTMLElement} el The dom node or id
9774      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9775      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9776      * @static
9777      * @return {Element} The shared Element object
9778      */
9779     El.fly = function(el, named){
9780         named = named || '_global';
9781         el = Roo.getDom(el);
9782         if(!el){
9783             return null;
9784         }
9785         if(!El._flyweights[named]){
9786             El._flyweights[named] = new El.Flyweight();
9787         }
9788         El._flyweights[named].dom = el;
9789         return El._flyweights[named];
9790     };
9791
9792     /**
9793      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9794      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9795      * Shorthand of {@link Roo.Element#get}
9796      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9797      * @return {Element} The Element object
9798      * @member Roo
9799      * @method get
9800      */
9801     Roo.get = El.get;
9802     /**
9803      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9804      * the dom node can be overwritten by other code.
9805      * Shorthand of {@link Roo.Element#fly}
9806      * @param {String/HTMLElement} el The dom node or id
9807      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9808      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9809      * @static
9810      * @return {Element} The shared Element object
9811      * @member Roo
9812      * @method fly
9813      */
9814     Roo.fly = El.fly;
9815
9816     // speedy lookup for elements never to box adjust
9817     var noBoxAdjust = Roo.isStrict ? {
9818         select:1
9819     } : {
9820         input:1, select:1, textarea:1
9821     };
9822     if(Roo.isIE || Roo.isGecko){
9823         noBoxAdjust['button'] = 1;
9824     }
9825
9826
9827     Roo.EventManager.on(window, 'unload', function(){
9828         delete El.cache;
9829         delete El._flyweights;
9830     });
9831 })();
9832
9833
9834
9835
9836 if(Roo.DomQuery){
9837     Roo.Element.selectorFunction = Roo.DomQuery.select;
9838 }
9839
9840 Roo.Element.select = function(selector, unique, root){
9841     var els;
9842     if(typeof selector == "string"){
9843         els = Roo.Element.selectorFunction(selector, root);
9844     }else if(selector.length !== undefined){
9845         els = selector;
9846     }else{
9847         throw "Invalid selector";
9848     }
9849     if(unique === true){
9850         return new Roo.CompositeElement(els);
9851     }else{
9852         return new Roo.CompositeElementLite(els);
9853     }
9854 };
9855 /**
9856  * Selects elements based on the passed CSS selector to enable working on them as 1.
9857  * @param {String/Array} selector The CSS selector or an array of elements
9858  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9859  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9860  * @return {CompositeElementLite/CompositeElement}
9861  * @member Roo
9862  * @method select
9863  */
9864 Roo.select = Roo.Element.select;
9865
9866
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879 /*
9880  * Based on:
9881  * Ext JS Library 1.1.1
9882  * Copyright(c) 2006-2007, Ext JS, LLC.
9883  *
9884  * Originally Released Under LGPL - original licence link has changed is not relivant.
9885  *
9886  * Fork - LGPL
9887  * <script type="text/javascript">
9888  */
9889
9890
9891
9892 //Notifies Element that fx methods are available
9893 Roo.enableFx = true;
9894
9895 /**
9896  * @class Roo.Fx
9897  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9898  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9899  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9900  * Element effects to work.</p><br/>
9901  *
9902  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9903  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9904  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9905  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9906  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9907  * expected results and should be done with care.</p><br/>
9908  *
9909  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9910  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9911 <pre>
9912 Value  Description
9913 -----  -----------------------------
9914 tl     The top left corner
9915 t      The center of the top edge
9916 tr     The top right corner
9917 l      The center of the left edge
9918 r      The center of the right edge
9919 bl     The bottom left corner
9920 b      The center of the bottom edge
9921 br     The bottom right corner
9922 </pre>
9923  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9924  * below are common options that can be passed to any Fx method.</b>
9925  * @cfg {Function} callback A function called when the effect is finished
9926  * @cfg {Object} scope The scope of the effect function
9927  * @cfg {String} easing A valid Easing value for the effect
9928  * @cfg {String} afterCls A css class to apply after the effect
9929  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9930  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9931  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9932  * effects that end with the element being visually hidden, ignored otherwise)
9933  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9934  * a function which returns such a specification that will be applied to the Element after the effect finishes
9935  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9936  * @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
9937  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9938  */
9939 Roo.Fx = {
9940         /**
9941          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9942          * origin for the slide effect.  This function automatically handles wrapping the element with
9943          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9944          * Usage:
9945          *<pre><code>
9946 // default: slide the element in from the top
9947 el.slideIn();
9948
9949 // custom: slide the element in from the right with a 2-second duration
9950 el.slideIn('r', { duration: 2 });
9951
9952 // common config options shown with default values
9953 el.slideIn('t', {
9954     easing: 'easeOut',
9955     duration: .5
9956 });
9957 </code></pre>
9958          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9959          * @param {Object} options (optional) Object literal with any of the Fx config options
9960          * @return {Roo.Element} The Element
9961          */
9962     slideIn : function(anchor, o){
9963         var el = this.getFxEl();
9964         o = o || {};
9965
9966         el.queueFx(o, function(){
9967
9968             anchor = anchor || "t";
9969
9970             // fix display to visibility
9971             this.fixDisplay();
9972
9973             // restore values after effect
9974             var r = this.getFxRestore();
9975             var b = this.getBox();
9976             // fixed size for slide
9977             this.setSize(b);
9978
9979             // wrap if needed
9980             var wrap = this.fxWrap(r.pos, o, "hidden");
9981
9982             var st = this.dom.style;
9983             st.visibility = "visible";
9984             st.position = "absolute";
9985
9986             // clear out temp styles after slide and unwrap
9987             var after = function(){
9988                 el.fxUnwrap(wrap, r.pos, o);
9989                 st.width = r.width;
9990                 st.height = r.height;
9991                 el.afterFx(o);
9992             };
9993             // time to calc the positions
9994             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9995
9996             switch(anchor.toLowerCase()){
9997                 case "t":
9998                     wrap.setSize(b.width, 0);
9999                     st.left = st.bottom = "0";
10000                     a = {height: bh};
10001                 break;
10002                 case "l":
10003                     wrap.setSize(0, b.height);
10004                     st.right = st.top = "0";
10005                     a = {width: bw};
10006                 break;
10007                 case "r":
10008                     wrap.setSize(0, b.height);
10009                     wrap.setX(b.right);
10010                     st.left = st.top = "0";
10011                     a = {width: bw, points: pt};
10012                 break;
10013                 case "b":
10014                     wrap.setSize(b.width, 0);
10015                     wrap.setY(b.bottom);
10016                     st.left = st.top = "0";
10017                     a = {height: bh, points: pt};
10018                 break;
10019                 case "tl":
10020                     wrap.setSize(0, 0);
10021                     st.right = st.bottom = "0";
10022                     a = {width: bw, height: bh};
10023                 break;
10024                 case "bl":
10025                     wrap.setSize(0, 0);
10026                     wrap.setY(b.y+b.height);
10027                     st.right = st.top = "0";
10028                     a = {width: bw, height: bh, points: pt};
10029                 break;
10030                 case "br":
10031                     wrap.setSize(0, 0);
10032                     wrap.setXY([b.right, b.bottom]);
10033                     st.left = st.top = "0";
10034                     a = {width: bw, height: bh, points: pt};
10035                 break;
10036                 case "tr":
10037                     wrap.setSize(0, 0);
10038                     wrap.setX(b.x+b.width);
10039                     st.left = st.bottom = "0";
10040                     a = {width: bw, height: bh, points: pt};
10041                 break;
10042             }
10043             this.dom.style.visibility = "visible";
10044             wrap.show();
10045
10046             arguments.callee.anim = wrap.fxanim(a,
10047                 o,
10048                 'motion',
10049                 .5,
10050                 'easeOut', after);
10051         });
10052         return this;
10053     },
10054     
10055         /**
10056          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10057          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10058          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10059          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10060          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10061          * Usage:
10062          *<pre><code>
10063 // default: slide the element out to the top
10064 el.slideOut();
10065
10066 // custom: slide the element out to the right with a 2-second duration
10067 el.slideOut('r', { duration: 2 });
10068
10069 // common config options shown with default values
10070 el.slideOut('t', {
10071     easing: 'easeOut',
10072     duration: .5,
10073     remove: false,
10074     useDisplay: false
10075 });
10076 </code></pre>
10077          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10078          * @param {Object} options (optional) Object literal with any of the Fx config options
10079          * @return {Roo.Element} The Element
10080          */
10081     slideOut : function(anchor, o){
10082         var el = this.getFxEl();
10083         o = o || {};
10084
10085         el.queueFx(o, function(){
10086
10087             anchor = anchor || "t";
10088
10089             // restore values after effect
10090             var r = this.getFxRestore();
10091             
10092             var b = this.getBox();
10093             // fixed size for slide
10094             this.setSize(b);
10095
10096             // wrap if needed
10097             var wrap = this.fxWrap(r.pos, o, "visible");
10098
10099             var st = this.dom.style;
10100             st.visibility = "visible";
10101             st.position = "absolute";
10102
10103             wrap.setSize(b);
10104
10105             var after = function(){
10106                 if(o.useDisplay){
10107                     el.setDisplayed(false);
10108                 }else{
10109                     el.hide();
10110                 }
10111
10112                 el.fxUnwrap(wrap, r.pos, o);
10113
10114                 st.width = r.width;
10115                 st.height = r.height;
10116
10117                 el.afterFx(o);
10118             };
10119
10120             var a, zero = {to: 0};
10121             switch(anchor.toLowerCase()){
10122                 case "t":
10123                     st.left = st.bottom = "0";
10124                     a = {height: zero};
10125                 break;
10126                 case "l":
10127                     st.right = st.top = "0";
10128                     a = {width: zero};
10129                 break;
10130                 case "r":
10131                     st.left = st.top = "0";
10132                     a = {width: zero, points: {to:[b.right, b.y]}};
10133                 break;
10134                 case "b":
10135                     st.left = st.top = "0";
10136                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10137                 break;
10138                 case "tl":
10139                     st.right = st.bottom = "0";
10140                     a = {width: zero, height: zero};
10141                 break;
10142                 case "bl":
10143                     st.right = st.top = "0";
10144                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10145                 break;
10146                 case "br":
10147                     st.left = st.top = "0";
10148                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10149                 break;
10150                 case "tr":
10151                     st.left = st.bottom = "0";
10152                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10153                 break;
10154             }
10155
10156             arguments.callee.anim = wrap.fxanim(a,
10157                 o,
10158                 'motion',
10159                 .5,
10160                 "easeOut", after);
10161         });
10162         return this;
10163     },
10164
10165         /**
10166          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10167          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10168          * The element must be removed from the DOM using the 'remove' config option if desired.
10169          * Usage:
10170          *<pre><code>
10171 // default
10172 el.puff();
10173
10174 // common config options shown with default values
10175 el.puff({
10176     easing: 'easeOut',
10177     duration: .5,
10178     remove: false,
10179     useDisplay: false
10180 });
10181 </code></pre>
10182          * @param {Object} options (optional) Object literal with any of the Fx config options
10183          * @return {Roo.Element} The Element
10184          */
10185     puff : function(o){
10186         var el = this.getFxEl();
10187         o = o || {};
10188
10189         el.queueFx(o, function(){
10190             this.clearOpacity();
10191             this.show();
10192
10193             // restore values after effect
10194             var r = this.getFxRestore();
10195             var st = this.dom.style;
10196
10197             var after = function(){
10198                 if(o.useDisplay){
10199                     el.setDisplayed(false);
10200                 }else{
10201                     el.hide();
10202                 }
10203
10204                 el.clearOpacity();
10205
10206                 el.setPositioning(r.pos);
10207                 st.width = r.width;
10208                 st.height = r.height;
10209                 st.fontSize = '';
10210                 el.afterFx(o);
10211             };
10212
10213             var width = this.getWidth();
10214             var height = this.getHeight();
10215
10216             arguments.callee.anim = this.fxanim({
10217                     width : {to: this.adjustWidth(width * 2)},
10218                     height : {to: this.adjustHeight(height * 2)},
10219                     points : {by: [-(width * .5), -(height * .5)]},
10220                     opacity : {to: 0},
10221                     fontSize: {to:200, unit: "%"}
10222                 },
10223                 o,
10224                 'motion',
10225                 .5,
10226                 "easeOut", after);
10227         });
10228         return this;
10229     },
10230
10231         /**
10232          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10233          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10234          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10235          * Usage:
10236          *<pre><code>
10237 // default
10238 el.switchOff();
10239
10240 // all config options shown with default values
10241 el.switchOff({
10242     easing: 'easeIn',
10243     duration: .3,
10244     remove: false,
10245     useDisplay: false
10246 });
10247 </code></pre>
10248          * @param {Object} options (optional) Object literal with any of the Fx config options
10249          * @return {Roo.Element} The Element
10250          */
10251     switchOff : function(o){
10252         var el = this.getFxEl();
10253         o = o || {};
10254
10255         el.queueFx(o, function(){
10256             this.clearOpacity();
10257             this.clip();
10258
10259             // restore values after effect
10260             var r = this.getFxRestore();
10261             var st = this.dom.style;
10262
10263             var after = function(){
10264                 if(o.useDisplay){
10265                     el.setDisplayed(false);
10266                 }else{
10267                     el.hide();
10268                 }
10269
10270                 el.clearOpacity();
10271                 el.setPositioning(r.pos);
10272                 st.width = r.width;
10273                 st.height = r.height;
10274
10275                 el.afterFx(o);
10276             };
10277
10278             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10279                 this.clearOpacity();
10280                 (function(){
10281                     this.fxanim({
10282                         height:{to:1},
10283                         points:{by:[0, this.getHeight() * .5]}
10284                     }, o, 'motion', 0.3, 'easeIn', after);
10285                 }).defer(100, this);
10286             });
10287         });
10288         return this;
10289     },
10290
10291     /**
10292      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10293      * changed using the "attr" config option) and then fading back to the original color. If no original
10294      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10295      * Usage:
10296 <pre><code>
10297 // default: highlight background to yellow
10298 el.highlight();
10299
10300 // custom: highlight foreground text to blue for 2 seconds
10301 el.highlight("0000ff", { attr: 'color', duration: 2 });
10302
10303 // common config options shown with default values
10304 el.highlight("ffff9c", {
10305     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10306     endColor: (current color) or "ffffff",
10307     easing: 'easeIn',
10308     duration: 1
10309 });
10310 </code></pre>
10311      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10312      * @param {Object} options (optional) Object literal with any of the Fx config options
10313      * @return {Roo.Element} The Element
10314      */ 
10315     highlight : function(color, o){
10316         var el = this.getFxEl();
10317         o = o || {};
10318
10319         el.queueFx(o, function(){
10320             color = color || "ffff9c";
10321             attr = o.attr || "backgroundColor";
10322
10323             this.clearOpacity();
10324             this.show();
10325
10326             var origColor = this.getColor(attr);
10327             var restoreColor = this.dom.style[attr];
10328             endColor = (o.endColor || origColor) || "ffffff";
10329
10330             var after = function(){
10331                 el.dom.style[attr] = restoreColor;
10332                 el.afterFx(o);
10333             };
10334
10335             var a = {};
10336             a[attr] = {from: color, to: endColor};
10337             arguments.callee.anim = this.fxanim(a,
10338                 o,
10339                 'color',
10340                 1,
10341                 'easeIn', after);
10342         });
10343         return this;
10344     },
10345
10346    /**
10347     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10348     * Usage:
10349 <pre><code>
10350 // default: a single light blue ripple
10351 el.frame();
10352
10353 // custom: 3 red ripples lasting 3 seconds total
10354 el.frame("ff0000", 3, { duration: 3 });
10355
10356 // common config options shown with default values
10357 el.frame("C3DAF9", 1, {
10358     duration: 1 //duration of entire animation (not each individual ripple)
10359     // Note: Easing is not configurable and will be ignored if included
10360 });
10361 </code></pre>
10362     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10363     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10364     * @param {Object} options (optional) Object literal with any of the Fx config options
10365     * @return {Roo.Element} The Element
10366     */
10367     frame : function(color, count, o){
10368         var el = this.getFxEl();
10369         o = o || {};
10370
10371         el.queueFx(o, function(){
10372             color = color || "#C3DAF9";
10373             if(color.length == 6){
10374                 color = "#" + color;
10375             }
10376             count = count || 1;
10377             duration = o.duration || 1;
10378             this.show();
10379
10380             var b = this.getBox();
10381             var animFn = function(){
10382                 var proxy = this.createProxy({
10383
10384                      style:{
10385                         visbility:"hidden",
10386                         position:"absolute",
10387                         "z-index":"35000", // yee haw
10388                         border:"0px solid " + color
10389                      }
10390                   });
10391                 var scale = Roo.isBorderBox ? 2 : 1;
10392                 proxy.animate({
10393                     top:{from:b.y, to:b.y - 20},
10394                     left:{from:b.x, to:b.x - 20},
10395                     borderWidth:{from:0, to:10},
10396                     opacity:{from:1, to:0},
10397                     height:{from:b.height, to:(b.height + (20*scale))},
10398                     width:{from:b.width, to:(b.width + (20*scale))}
10399                 }, duration, function(){
10400                     proxy.remove();
10401                 });
10402                 if(--count > 0){
10403                      animFn.defer((duration/2)*1000, this);
10404                 }else{
10405                     el.afterFx(o);
10406                 }
10407             };
10408             animFn.call(this);
10409         });
10410         return this;
10411     },
10412
10413    /**
10414     * Creates a pause before any subsequent queued effects begin.  If there are
10415     * no effects queued after the pause it will have no effect.
10416     * Usage:
10417 <pre><code>
10418 el.pause(1);
10419 </code></pre>
10420     * @param {Number} seconds The length of time to pause (in seconds)
10421     * @return {Roo.Element} The Element
10422     */
10423     pause : function(seconds){
10424         var el = this.getFxEl();
10425         var o = {};
10426
10427         el.queueFx(o, function(){
10428             setTimeout(function(){
10429                 el.afterFx(o);
10430             }, seconds * 1000);
10431         });
10432         return this;
10433     },
10434
10435    /**
10436     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10437     * using the "endOpacity" config option.
10438     * Usage:
10439 <pre><code>
10440 // default: fade in from opacity 0 to 100%
10441 el.fadeIn();
10442
10443 // custom: fade in from opacity 0 to 75% over 2 seconds
10444 el.fadeIn({ endOpacity: .75, duration: 2});
10445
10446 // common config options shown with default values
10447 el.fadeIn({
10448     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10449     easing: 'easeOut',
10450     duration: .5
10451 });
10452 </code></pre>
10453     * @param {Object} options (optional) Object literal with any of the Fx config options
10454     * @return {Roo.Element} The Element
10455     */
10456     fadeIn : function(o){
10457         var el = this.getFxEl();
10458         o = o || {};
10459         el.queueFx(o, function(){
10460             this.setOpacity(0);
10461             this.fixDisplay();
10462             this.dom.style.visibility = 'visible';
10463             var to = o.endOpacity || 1;
10464             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10465                 o, null, .5, "easeOut", function(){
10466                 if(to == 1){
10467                     this.clearOpacity();
10468                 }
10469                 el.afterFx(o);
10470             });
10471         });
10472         return this;
10473     },
10474
10475    /**
10476     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10477     * using the "endOpacity" config option.
10478     * Usage:
10479 <pre><code>
10480 // default: fade out from the element's current opacity to 0
10481 el.fadeOut();
10482
10483 // custom: fade out from the element's current opacity to 25% over 2 seconds
10484 el.fadeOut({ endOpacity: .25, duration: 2});
10485
10486 // common config options shown with default values
10487 el.fadeOut({
10488     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10489     easing: 'easeOut',
10490     duration: .5
10491     remove: false,
10492     useDisplay: false
10493 });
10494 </code></pre>
10495     * @param {Object} options (optional) Object literal with any of the Fx config options
10496     * @return {Roo.Element} The Element
10497     */
10498     fadeOut : function(o){
10499         var el = this.getFxEl();
10500         o = o || {};
10501         el.queueFx(o, function(){
10502             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10503                 o, null, .5, "easeOut", function(){
10504                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10505                      this.dom.style.display = "none";
10506                 }else{
10507                      this.dom.style.visibility = "hidden";
10508                 }
10509                 this.clearOpacity();
10510                 el.afterFx(o);
10511             });
10512         });
10513         return this;
10514     },
10515
10516    /**
10517     * Animates the transition of an element's dimensions from a starting height/width
10518     * to an ending height/width.
10519     * Usage:
10520 <pre><code>
10521 // change height and width to 100x100 pixels
10522 el.scale(100, 100);
10523
10524 // common config options shown with default values.  The height and width will default to
10525 // the element's existing values if passed as null.
10526 el.scale(
10527     [element's width],
10528     [element's height], {
10529     easing: 'easeOut',
10530     duration: .35
10531 });
10532 </code></pre>
10533     * @param {Number} width  The new width (pass undefined to keep the original width)
10534     * @param {Number} height  The new height (pass undefined to keep the original height)
10535     * @param {Object} options (optional) Object literal with any of the Fx config options
10536     * @return {Roo.Element} The Element
10537     */
10538     scale : function(w, h, o){
10539         this.shift(Roo.apply({}, o, {
10540             width: w,
10541             height: h
10542         }));
10543         return this;
10544     },
10545
10546    /**
10547     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10548     * Any of these properties not specified in the config object will not be changed.  This effect 
10549     * requires that at least one new dimension, position or opacity setting must be passed in on
10550     * the config object in order for the function to have any effect.
10551     * Usage:
10552 <pre><code>
10553 // slide the element horizontally to x position 200 while changing the height and opacity
10554 el.shift({ x: 200, height: 50, opacity: .8 });
10555
10556 // common config options shown with default values.
10557 el.shift({
10558     width: [element's width],
10559     height: [element's height],
10560     x: [element's x position],
10561     y: [element's y position],
10562     opacity: [element's opacity],
10563     easing: 'easeOut',
10564     duration: .35
10565 });
10566 </code></pre>
10567     * @param {Object} options  Object literal with any of the Fx config options
10568     * @return {Roo.Element} The Element
10569     */
10570     shift : function(o){
10571         var el = this.getFxEl();
10572         o = o || {};
10573         el.queueFx(o, function(){
10574             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10575             if(w !== undefined){
10576                 a.width = {to: this.adjustWidth(w)};
10577             }
10578             if(h !== undefined){
10579                 a.height = {to: this.adjustHeight(h)};
10580             }
10581             if(x !== undefined || y !== undefined){
10582                 a.points = {to: [
10583                     x !== undefined ? x : this.getX(),
10584                     y !== undefined ? y : this.getY()
10585                 ]};
10586             }
10587             if(op !== undefined){
10588                 a.opacity = {to: op};
10589             }
10590             if(o.xy !== undefined){
10591                 a.points = {to: o.xy};
10592             }
10593             arguments.callee.anim = this.fxanim(a,
10594                 o, 'motion', .35, "easeOut", function(){
10595                 el.afterFx(o);
10596             });
10597         });
10598         return this;
10599     },
10600
10601         /**
10602          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10603          * ending point of the effect.
10604          * Usage:
10605          *<pre><code>
10606 // default: slide the element downward while fading out
10607 el.ghost();
10608
10609 // custom: slide the element out to the right with a 2-second duration
10610 el.ghost('r', { duration: 2 });
10611
10612 // common config options shown with default values
10613 el.ghost('b', {
10614     easing: 'easeOut',
10615     duration: .5
10616     remove: false,
10617     useDisplay: false
10618 });
10619 </code></pre>
10620          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10621          * @param {Object} options (optional) Object literal with any of the Fx config options
10622          * @return {Roo.Element} The Element
10623          */
10624     ghost : function(anchor, o){
10625         var el = this.getFxEl();
10626         o = o || {};
10627
10628         el.queueFx(o, function(){
10629             anchor = anchor || "b";
10630
10631             // restore values after effect
10632             var r = this.getFxRestore();
10633             var w = this.getWidth(),
10634                 h = this.getHeight();
10635
10636             var st = this.dom.style;
10637
10638             var after = function(){
10639                 if(o.useDisplay){
10640                     el.setDisplayed(false);
10641                 }else{
10642                     el.hide();
10643                 }
10644
10645                 el.clearOpacity();
10646                 el.setPositioning(r.pos);
10647                 st.width = r.width;
10648                 st.height = r.height;
10649
10650                 el.afterFx(o);
10651             };
10652
10653             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10654             switch(anchor.toLowerCase()){
10655                 case "t":
10656                     pt.by = [0, -h];
10657                 break;
10658                 case "l":
10659                     pt.by = [-w, 0];
10660                 break;
10661                 case "r":
10662                     pt.by = [w, 0];
10663                 break;
10664                 case "b":
10665                     pt.by = [0, h];
10666                 break;
10667                 case "tl":
10668                     pt.by = [-w, -h];
10669                 break;
10670                 case "bl":
10671                     pt.by = [-w, h];
10672                 break;
10673                 case "br":
10674                     pt.by = [w, h];
10675                 break;
10676                 case "tr":
10677                     pt.by = [w, -h];
10678                 break;
10679             }
10680
10681             arguments.callee.anim = this.fxanim(a,
10682                 o,
10683                 'motion',
10684                 .5,
10685                 "easeOut", after);
10686         });
10687         return this;
10688     },
10689
10690         /**
10691          * Ensures that all effects queued after syncFx is called on the element are
10692          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10693          * @return {Roo.Element} The Element
10694          */
10695     syncFx : function(){
10696         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10697             block : false,
10698             concurrent : true,
10699             stopFx : false
10700         });
10701         return this;
10702     },
10703
10704         /**
10705          * Ensures that all effects queued after sequenceFx is called on the element are
10706          * run in sequence.  This is the opposite of {@link #syncFx}.
10707          * @return {Roo.Element} The Element
10708          */
10709     sequenceFx : function(){
10710         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10711             block : false,
10712             concurrent : false,
10713             stopFx : false
10714         });
10715         return this;
10716     },
10717
10718         /* @private */
10719     nextFx : function(){
10720         var ef = this.fxQueue[0];
10721         if(ef){
10722             ef.call(this);
10723         }
10724     },
10725
10726         /**
10727          * Returns true if the element has any effects actively running or queued, else returns false.
10728          * @return {Boolean} True if element has active effects, else false
10729          */
10730     hasActiveFx : function(){
10731         return this.fxQueue && this.fxQueue[0];
10732     },
10733
10734         /**
10735          * Stops any running effects and clears the element's internal effects queue if it contains
10736          * any additional effects that haven't started yet.
10737          * @return {Roo.Element} The Element
10738          */
10739     stopFx : function(){
10740         if(this.hasActiveFx()){
10741             var cur = this.fxQueue[0];
10742             if(cur && cur.anim && cur.anim.isAnimated()){
10743                 this.fxQueue = [cur]; // clear out others
10744                 cur.anim.stop(true);
10745             }
10746         }
10747         return this;
10748     },
10749
10750         /* @private */
10751     beforeFx : function(o){
10752         if(this.hasActiveFx() && !o.concurrent){
10753            if(o.stopFx){
10754                this.stopFx();
10755                return true;
10756            }
10757            return false;
10758         }
10759         return true;
10760     },
10761
10762         /**
10763          * Returns true if the element is currently blocking so that no other effect can be queued
10764          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10765          * used to ensure that an effect initiated by a user action runs to completion prior to the
10766          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10767          * @return {Boolean} True if blocking, else false
10768          */
10769     hasFxBlock : function(){
10770         var q = this.fxQueue;
10771         return q && q[0] && q[0].block;
10772     },
10773
10774         /* @private */
10775     queueFx : function(o, fn){
10776         if(!this.fxQueue){
10777             this.fxQueue = [];
10778         }
10779         if(!this.hasFxBlock()){
10780             Roo.applyIf(o, this.fxDefaults);
10781             if(!o.concurrent){
10782                 var run = this.beforeFx(o);
10783                 fn.block = o.block;
10784                 this.fxQueue.push(fn);
10785                 if(run){
10786                     this.nextFx();
10787                 }
10788             }else{
10789                 fn.call(this);
10790             }
10791         }
10792         return this;
10793     },
10794
10795         /* @private */
10796     fxWrap : function(pos, o, vis){
10797         var wrap;
10798         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10799             var wrapXY;
10800             if(o.fixPosition){
10801                 wrapXY = this.getXY();
10802             }
10803             var div = document.createElement("div");
10804             div.style.visibility = vis;
10805             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10806             wrap.setPositioning(pos);
10807             if(wrap.getStyle("position") == "static"){
10808                 wrap.position("relative");
10809             }
10810             this.clearPositioning('auto');
10811             wrap.clip();
10812             wrap.dom.appendChild(this.dom);
10813             if(wrapXY){
10814                 wrap.setXY(wrapXY);
10815             }
10816         }
10817         return wrap;
10818     },
10819
10820         /* @private */
10821     fxUnwrap : function(wrap, pos, o){
10822         this.clearPositioning();
10823         this.setPositioning(pos);
10824         if(!o.wrap){
10825             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10826             wrap.remove();
10827         }
10828     },
10829
10830         /* @private */
10831     getFxRestore : function(){
10832         var st = this.dom.style;
10833         return {pos: this.getPositioning(), width: st.width, height : st.height};
10834     },
10835
10836         /* @private */
10837     afterFx : function(o){
10838         if(o.afterStyle){
10839             this.applyStyles(o.afterStyle);
10840         }
10841         if(o.afterCls){
10842             this.addClass(o.afterCls);
10843         }
10844         if(o.remove === true){
10845             this.remove();
10846         }
10847         Roo.callback(o.callback, o.scope, [this]);
10848         if(!o.concurrent){
10849             this.fxQueue.shift();
10850             this.nextFx();
10851         }
10852     },
10853
10854         /* @private */
10855     getFxEl : function(){ // support for composite element fx
10856         return Roo.get(this.dom);
10857     },
10858
10859         /* @private */
10860     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10861         animType = animType || 'run';
10862         opt = opt || {};
10863         var anim = Roo.lib.Anim[animType](
10864             this.dom, args,
10865             (opt.duration || defaultDur) || .35,
10866             (opt.easing || defaultEase) || 'easeOut',
10867             function(){
10868                 Roo.callback(cb, this);
10869             },
10870             this
10871         );
10872         opt.anim = anim;
10873         return anim;
10874     }
10875 };
10876
10877 // backwords compat
10878 Roo.Fx.resize = Roo.Fx.scale;
10879
10880 //When included, Roo.Fx is automatically applied to Element so that all basic
10881 //effects are available directly via the Element API
10882 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10883  * Based on:
10884  * Ext JS Library 1.1.1
10885  * Copyright(c) 2006-2007, Ext JS, LLC.
10886  *
10887  * Originally Released Under LGPL - original licence link has changed is not relivant.
10888  *
10889  * Fork - LGPL
10890  * <script type="text/javascript">
10891  */
10892
10893
10894 /**
10895  * @class Roo.CompositeElement
10896  * Standard composite class. Creates a Roo.Element for every element in the collection.
10897  * <br><br>
10898  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10899  * actions will be performed on all the elements in this collection.</b>
10900  * <br><br>
10901  * All methods return <i>this</i> and can be chained.
10902  <pre><code>
10903  var els = Roo.select("#some-el div.some-class", true);
10904  // or select directly from an existing element
10905  var el = Roo.get('some-el');
10906  el.select('div.some-class', true);
10907
10908  els.setWidth(100); // all elements become 100 width
10909  els.hide(true); // all elements fade out and hide
10910  // or
10911  els.setWidth(100).hide(true);
10912  </code></pre>
10913  */
10914 Roo.CompositeElement = function(els){
10915     this.elements = [];
10916     this.addElements(els);
10917 };
10918 Roo.CompositeElement.prototype = {
10919     isComposite: true,
10920     addElements : function(els){
10921         if(!els) return this;
10922         if(typeof els == "string"){
10923             els = Roo.Element.selectorFunction(els);
10924         }
10925         var yels = this.elements;
10926         var index = yels.length-1;
10927         for(var i = 0, len = els.length; i < len; i++) {
10928                 yels[++index] = Roo.get(els[i]);
10929         }
10930         return this;
10931     },
10932
10933     /**
10934     * Clears this composite and adds the elements returned by the passed selector.
10935     * @param {String/Array} els A string CSS selector, an array of elements or an element
10936     * @return {CompositeElement} this
10937     */
10938     fill : function(els){
10939         this.elements = [];
10940         this.add(els);
10941         return this;
10942     },
10943
10944     /**
10945     * Filters this composite to only elements that match the passed selector.
10946     * @param {String} selector A string CSS selector
10947     * @return {CompositeElement} this
10948     */
10949     filter : function(selector){
10950         var els = [];
10951         this.each(function(el){
10952             if(el.is(selector)){
10953                 els[els.length] = el.dom;
10954             }
10955         });
10956         this.fill(els);
10957         return this;
10958     },
10959
10960     invoke : function(fn, args){
10961         var els = this.elements;
10962         for(var i = 0, len = els.length; i < len; i++) {
10963                 Roo.Element.prototype[fn].apply(els[i], args);
10964         }
10965         return this;
10966     },
10967     /**
10968     * Adds elements to this composite.
10969     * @param {String/Array} els A string CSS selector, an array of elements or an element
10970     * @return {CompositeElement} this
10971     */
10972     add : function(els){
10973         if(typeof els == "string"){
10974             this.addElements(Roo.Element.selectorFunction(els));
10975         }else if(els.length !== undefined){
10976             this.addElements(els);
10977         }else{
10978             this.addElements([els]);
10979         }
10980         return this;
10981     },
10982     /**
10983     * Calls the passed function passing (el, this, index) for each element in this composite.
10984     * @param {Function} fn The function to call
10985     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10986     * @return {CompositeElement} this
10987     */
10988     each : function(fn, scope){
10989         var els = this.elements;
10990         for(var i = 0, len = els.length; i < len; i++){
10991             if(fn.call(scope || els[i], els[i], this, i) === false) {
10992                 break;
10993             }
10994         }
10995         return this;
10996     },
10997
10998     /**
10999      * Returns the Element object at the specified index
11000      * @param {Number} index
11001      * @return {Roo.Element}
11002      */
11003     item : function(index){
11004         return this.elements[index] || null;
11005     },
11006
11007     /**
11008      * Returns the first Element
11009      * @return {Roo.Element}
11010      */
11011     first : function(){
11012         return this.item(0);
11013     },
11014
11015     /**
11016      * Returns the last Element
11017      * @return {Roo.Element}
11018      */
11019     last : function(){
11020         return this.item(this.elements.length-1);
11021     },
11022
11023     /**
11024      * Returns the number of elements in this composite
11025      * @return Number
11026      */
11027     getCount : function(){
11028         return this.elements.length;
11029     },
11030
11031     /**
11032      * Returns true if this composite contains the passed element
11033      * @return Boolean
11034      */
11035     contains : function(el){
11036         return this.indexOf(el) !== -1;
11037     },
11038
11039     /**
11040      * Returns true if this composite contains the passed element
11041      * @return Boolean
11042      */
11043     indexOf : function(el){
11044         return this.elements.indexOf(Roo.get(el));
11045     },
11046
11047
11048     /**
11049     * Removes the specified element(s).
11050     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11051     * or an array of any of those.
11052     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11053     * @return {CompositeElement} this
11054     */
11055     removeElement : function(el, removeDom){
11056         if(el instanceof Array){
11057             for(var i = 0, len = el.length; i < len; i++){
11058                 this.removeElement(el[i]);
11059             }
11060             return this;
11061         }
11062         var index = typeof el == 'number' ? el : this.indexOf(el);
11063         if(index !== -1){
11064             if(removeDom){
11065                 var d = this.elements[index];
11066                 if(d.dom){
11067                     d.remove();
11068                 }else{
11069                     d.parentNode.removeChild(d);
11070                 }
11071             }
11072             this.elements.splice(index, 1);
11073         }
11074         return this;
11075     },
11076
11077     /**
11078     * Replaces the specified element with the passed element.
11079     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11080     * to replace.
11081     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11082     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11083     * @return {CompositeElement} this
11084     */
11085     replaceElement : function(el, replacement, domReplace){
11086         var index = typeof el == 'number' ? el : this.indexOf(el);
11087         if(index !== -1){
11088             if(domReplace){
11089                 this.elements[index].replaceWith(replacement);
11090             }else{
11091                 this.elements.splice(index, 1, Roo.get(replacement))
11092             }
11093         }
11094         return this;
11095     },
11096
11097     /**
11098      * Removes all elements.
11099      */
11100     clear : function(){
11101         this.elements = [];
11102     }
11103 };
11104 (function(){
11105     Roo.CompositeElement.createCall = function(proto, fnName){
11106         if(!proto[fnName]){
11107             proto[fnName] = function(){
11108                 return this.invoke(fnName, arguments);
11109             };
11110         }
11111     };
11112     for(var fnName in Roo.Element.prototype){
11113         if(typeof Roo.Element.prototype[fnName] == "function"){
11114             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11115         }
11116     };
11117 })();
11118 /*
11119  * Based on:
11120  * Ext JS Library 1.1.1
11121  * Copyright(c) 2006-2007, Ext JS, LLC.
11122  *
11123  * Originally Released Under LGPL - original licence link has changed is not relivant.
11124  *
11125  * Fork - LGPL
11126  * <script type="text/javascript">
11127  */
11128
11129 /**
11130  * @class Roo.CompositeElementLite
11131  * @extends Roo.CompositeElement
11132  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11133  <pre><code>
11134  var els = Roo.select("#some-el div.some-class");
11135  // or select directly from an existing element
11136  var el = Roo.get('some-el');
11137  el.select('div.some-class');
11138
11139  els.setWidth(100); // all elements become 100 width
11140  els.hide(true); // all elements fade out and hide
11141  // or
11142  els.setWidth(100).hide(true);
11143  </code></pre><br><br>
11144  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11145  * actions will be performed on all the elements in this collection.</b>
11146  */
11147 Roo.CompositeElementLite = function(els){
11148     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11149     this.el = new Roo.Element.Flyweight();
11150 };
11151 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11152     addElements : function(els){
11153         if(els){
11154             if(els instanceof Array){
11155                 this.elements = this.elements.concat(els);
11156             }else{
11157                 var yels = this.elements;
11158                 var index = yels.length-1;
11159                 for(var i = 0, len = els.length; i < len; i++) {
11160                     yels[++index] = els[i];
11161                 }
11162             }
11163         }
11164         return this;
11165     },
11166     invoke : function(fn, args){
11167         var els = this.elements;
11168         var el = this.el;
11169         for(var i = 0, len = els.length; i < len; i++) {
11170             el.dom = els[i];
11171                 Roo.Element.prototype[fn].apply(el, args);
11172         }
11173         return this;
11174     },
11175     /**
11176      * Returns a flyweight Element of the dom element object at the specified index
11177      * @param {Number} index
11178      * @return {Roo.Element}
11179      */
11180     item : function(index){
11181         if(!this.elements[index]){
11182             return null;
11183         }
11184         this.el.dom = this.elements[index];
11185         return this.el;
11186     },
11187
11188     // fixes scope with flyweight
11189     addListener : function(eventName, handler, scope, opt){
11190         var els = this.elements;
11191         for(var i = 0, len = els.length; i < len; i++) {
11192             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11193         }
11194         return this;
11195     },
11196
11197     /**
11198     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11199     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11200     * a reference to the dom node, use el.dom.</b>
11201     * @param {Function} fn The function to call
11202     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11203     * @return {CompositeElement} this
11204     */
11205     each : function(fn, scope){
11206         var els = this.elements;
11207         var el = this.el;
11208         for(var i = 0, len = els.length; i < len; i++){
11209             el.dom = els[i];
11210                 if(fn.call(scope || el, el, this, i) === false){
11211                 break;
11212             }
11213         }
11214         return this;
11215     },
11216
11217     indexOf : function(el){
11218         return this.elements.indexOf(Roo.getDom(el));
11219     },
11220
11221     replaceElement : function(el, replacement, domReplace){
11222         var index = typeof el == 'number' ? el : this.indexOf(el);
11223         if(index !== -1){
11224             replacement = Roo.getDom(replacement);
11225             if(domReplace){
11226                 var d = this.elements[index];
11227                 d.parentNode.insertBefore(replacement, d);
11228                 d.parentNode.removeChild(d);
11229             }
11230             this.elements.splice(index, 1, replacement);
11231         }
11232         return this;
11233     }
11234 });
11235 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11236
11237 /*
11238  * Based on:
11239  * Ext JS Library 1.1.1
11240  * Copyright(c) 2006-2007, Ext JS, LLC.
11241  *
11242  * Originally Released Under LGPL - original licence link has changed is not relivant.
11243  *
11244  * Fork - LGPL
11245  * <script type="text/javascript">
11246  */
11247
11248  
11249
11250 /**
11251  * @class Roo.data.Connection
11252  * @extends Roo.util.Observable
11253  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11254  * either to a configured URL, or to a URL specified at request time.<br><br>
11255  * <p>
11256  * Requests made by this class are asynchronous, and will return immediately. No data from
11257  * the server will be available to the statement immediately following the {@link #request} call.
11258  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11259  * <p>
11260  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11261  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11262  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11263  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11264  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11265  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11266  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11267  * standard DOM methods.
11268  * @constructor
11269  * @param {Object} config a configuration object.
11270  */
11271 Roo.data.Connection = function(config){
11272     Roo.apply(this, config);
11273     this.addEvents({
11274         /**
11275          * @event beforerequest
11276          * Fires before a network request is made to retrieve a data object.
11277          * @param {Connection} conn This Connection object.
11278          * @param {Object} options The options config object passed to the {@link #request} method.
11279          */
11280         "beforerequest" : true,
11281         /**
11282          * @event requestcomplete
11283          * Fires if the request was successfully completed.
11284          * @param {Connection} conn This Connection object.
11285          * @param {Object} response The XHR object containing the response data.
11286          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11287          * @param {Object} options The options config object passed to the {@link #request} method.
11288          */
11289         "requestcomplete" : true,
11290         /**
11291          * @event requestexception
11292          * Fires if an error HTTP status was returned from the server.
11293          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11294          * @param {Connection} conn This Connection object.
11295          * @param {Object} response The XHR object containing the response data.
11296          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11297          * @param {Object} options The options config object passed to the {@link #request} method.
11298          */
11299         "requestexception" : true
11300     });
11301     Roo.data.Connection.superclass.constructor.call(this);
11302 };
11303
11304 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11305     /**
11306      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11307      */
11308     /**
11309      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11310      * extra parameters to each request made by this object. (defaults to undefined)
11311      */
11312     /**
11313      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11314      *  to each request made by this object. (defaults to undefined)
11315      */
11316     /**
11317      * @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)
11318      */
11319     /**
11320      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11321      */
11322     timeout : 30000,
11323     /**
11324      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11325      * @type Boolean
11326      */
11327     autoAbort:false,
11328
11329     /**
11330      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11331      * @type Boolean
11332      */
11333     disableCaching: true,
11334
11335     /**
11336      * Sends an HTTP request to a remote server.
11337      * @param {Object} options An object which may contain the following properties:<ul>
11338      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11339      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11340      * request, a url encoded string or a function to call to get either.</li>
11341      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11342      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11343      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11344      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11345      * <li>options {Object} The parameter to the request call.</li>
11346      * <li>success {Boolean} True if the request succeeded.</li>
11347      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11348      * </ul></li>
11349      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11350      * The callback is passed the following parameters:<ul>
11351      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11352      * <li>options {Object} The parameter to the request call.</li>
11353      * </ul></li>
11354      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11355      * The callback is passed the following parameters:<ul>
11356      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11357      * <li>options {Object} The parameter to the request call.</li>
11358      * </ul></li>
11359      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11360      * for the callback function. Defaults to the browser window.</li>
11361      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11362      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11363      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11364      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11365      * params for the post data. Any params will be appended to the URL.</li>
11366      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11367      * </ul>
11368      * @return {Number} transactionId
11369      */
11370     request : function(o){
11371         if(this.fireEvent("beforerequest", this, o) !== false){
11372             var p = o.params;
11373
11374             if(typeof p == "function"){
11375                 p = p.call(o.scope||window, o);
11376             }
11377             if(typeof p == "object"){
11378                 p = Roo.urlEncode(o.params);
11379             }
11380             if(this.extraParams){
11381                 var extras = Roo.urlEncode(this.extraParams);
11382                 p = p ? (p + '&' + extras) : extras;
11383             }
11384
11385             var url = o.url || this.url;
11386             if(typeof url == 'function'){
11387                 url = url.call(o.scope||window, o);
11388             }
11389
11390             if(o.form){
11391                 var form = Roo.getDom(o.form);
11392                 url = url || form.action;
11393
11394                 var enctype = form.getAttribute("enctype");
11395                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11396                     return this.doFormUpload(o, p, url);
11397                 }
11398                 var f = Roo.lib.Ajax.serializeForm(form);
11399                 p = p ? (p + '&' + f) : f;
11400             }
11401
11402             var hs = o.headers;
11403             if(this.defaultHeaders){
11404                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11405                 if(!o.headers){
11406                     o.headers = hs;
11407                 }
11408             }
11409
11410             var cb = {
11411                 success: this.handleResponse,
11412                 failure: this.handleFailure,
11413                 scope: this,
11414                 argument: {options: o},
11415                 timeout : o.timeout || this.timeout
11416             };
11417
11418             var method = o.method||this.method||(p ? "POST" : "GET");
11419
11420             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11421                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11422             }
11423
11424             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11425                 if(o.autoAbort){
11426                     this.abort();
11427                 }
11428             }else if(this.autoAbort !== false){
11429                 this.abort();
11430             }
11431
11432             if((method == 'GET' && p) || o.xmlData){
11433                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11434                 p = '';
11435             }
11436             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11437             return this.transId;
11438         }else{
11439             Roo.callback(o.callback, o.scope, [o, null, null]);
11440             return null;
11441         }
11442     },
11443
11444     /**
11445      * Determine whether this object has a request outstanding.
11446      * @param {Number} transactionId (Optional) defaults to the last transaction
11447      * @return {Boolean} True if there is an outstanding request.
11448      */
11449     isLoading : function(transId){
11450         if(transId){
11451             return Roo.lib.Ajax.isCallInProgress(transId);
11452         }else{
11453             return this.transId ? true : false;
11454         }
11455     },
11456
11457     /**
11458      * Aborts any outstanding request.
11459      * @param {Number} transactionId (Optional) defaults to the last transaction
11460      */
11461     abort : function(transId){
11462         if(transId || this.isLoading()){
11463             Roo.lib.Ajax.abort(transId || this.transId);
11464         }
11465     },
11466
11467     // private
11468     handleResponse : function(response){
11469         this.transId = false;
11470         var options = response.argument.options;
11471         response.argument = options ? options.argument : null;
11472         this.fireEvent("requestcomplete", this, response, options);
11473         Roo.callback(options.success, options.scope, [response, options]);
11474         Roo.callback(options.callback, options.scope, [options, true, response]);
11475     },
11476
11477     // private
11478     handleFailure : function(response, e){
11479         this.transId = false;
11480         var options = response.argument.options;
11481         response.argument = options ? options.argument : null;
11482         this.fireEvent("requestexception", this, response, options, e);
11483         Roo.callback(options.failure, options.scope, [response, options]);
11484         Roo.callback(options.callback, options.scope, [options, false, response]);
11485     },
11486
11487     // private
11488     doFormUpload : function(o, ps, url){
11489         var id = Roo.id();
11490         var frame = document.createElement('iframe');
11491         frame.id = id;
11492         frame.name = id;
11493         frame.className = 'x-hidden';
11494         if(Roo.isIE){
11495             frame.src = Roo.SSL_SECURE_URL;
11496         }
11497         document.body.appendChild(frame);
11498
11499         if(Roo.isIE){
11500            document.frames[id].name = id;
11501         }
11502
11503         var form = Roo.getDom(o.form);
11504         form.target = id;
11505         form.method = 'POST';
11506         form.enctype = form.encoding = 'multipart/form-data';
11507         if(url){
11508             form.action = url;
11509         }
11510
11511         var hiddens, hd;
11512         if(ps){ // add dynamic params
11513             hiddens = [];
11514             ps = Roo.urlDecode(ps, false);
11515             for(var k in ps){
11516                 if(ps.hasOwnProperty(k)){
11517                     hd = document.createElement('input');
11518                     hd.type = 'hidden';
11519                     hd.name = k;
11520                     hd.value = ps[k];
11521                     form.appendChild(hd);
11522                     hiddens.push(hd);
11523                 }
11524             }
11525         }
11526
11527         function cb(){
11528             var r = {  // bogus response object
11529                 responseText : '',
11530                 responseXML : null
11531             };
11532
11533             r.argument = o ? o.argument : null;
11534
11535             try { //
11536                 var doc;
11537                 if(Roo.isIE){
11538                     doc = frame.contentWindow.document;
11539                 }else {
11540                     doc = (frame.contentDocument || window.frames[id].document);
11541                 }
11542                 if(doc && doc.body){
11543                     r.responseText = doc.body.innerHTML;
11544                 }
11545                 if(doc && doc.XMLDocument){
11546                     r.responseXML = doc.XMLDocument;
11547                 }else {
11548                     r.responseXML = doc;
11549                 }
11550             }
11551             catch(e) {
11552                 // ignore
11553             }
11554
11555             Roo.EventManager.removeListener(frame, 'load', cb, this);
11556
11557             this.fireEvent("requestcomplete", this, r, o);
11558             Roo.callback(o.success, o.scope, [r, o]);
11559             Roo.callback(o.callback, o.scope, [o, true, r]);
11560
11561             setTimeout(function(){document.body.removeChild(frame);}, 100);
11562         }
11563
11564         Roo.EventManager.on(frame, 'load', cb, this);
11565         form.submit();
11566
11567         if(hiddens){ // remove dynamic params
11568             for(var i = 0, len = hiddens.length; i < len; i++){
11569                 form.removeChild(hiddens[i]);
11570             }
11571         }
11572     }
11573 });
11574 /*
11575  * Based on:
11576  * Ext JS Library 1.1.1
11577  * Copyright(c) 2006-2007, Ext JS, LLC.
11578  *
11579  * Originally Released Under LGPL - original licence link has changed is not relivant.
11580  *
11581  * Fork - LGPL
11582  * <script type="text/javascript">
11583  */
11584  
11585 /**
11586  * Global Ajax request class.
11587  * 
11588  * @class Roo.Ajax
11589  * @extends Roo.data.Connection
11590  * @static
11591  * 
11592  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11593  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11594  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11595  * @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)
11596  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11597  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11598  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11599  */
11600 Roo.Ajax = new Roo.data.Connection({
11601     // fix up the docs
11602     /**
11603      * @scope Roo.Ajax
11604      * @type {Boolear} 
11605      */
11606     autoAbort : false,
11607
11608     /**
11609      * Serialize the passed form into a url encoded string
11610      * @scope Roo.Ajax
11611      * @param {String/HTMLElement} form
11612      * @return {String}
11613      */
11614     serializeForm : function(form){
11615         return Roo.lib.Ajax.serializeForm(form);
11616     }
11617 });/*
11618  * Based on:
11619  * Ext JS Library 1.1.1
11620  * Copyright(c) 2006-2007, Ext JS, LLC.
11621  *
11622  * Originally Released Under LGPL - original licence link has changed is not relivant.
11623  *
11624  * Fork - LGPL
11625  * <script type="text/javascript">
11626  */
11627
11628  
11629 /**
11630  * @class Roo.UpdateManager
11631  * @extends Roo.util.Observable
11632  * Provides AJAX-style update for Element object.<br><br>
11633  * Usage:<br>
11634  * <pre><code>
11635  * // Get it from a Roo.Element object
11636  * var el = Roo.get("foo");
11637  * var mgr = el.getUpdateManager();
11638  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11639  * ...
11640  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11641  * <br>
11642  * // or directly (returns the same UpdateManager instance)
11643  * var mgr = new Roo.UpdateManager("myElementId");
11644  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11645  * mgr.on("update", myFcnNeedsToKnow);
11646  * <br>
11647    // short handed call directly from the element object
11648    Roo.get("foo").load({
11649         url: "bar.php",
11650         scripts:true,
11651         params: "for=bar",
11652         text: "Loading Foo..."
11653    });
11654  * </code></pre>
11655  * @constructor
11656  * Create new UpdateManager directly.
11657  * @param {String/HTMLElement/Roo.Element} el The element to update
11658  * @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).
11659  */
11660 Roo.UpdateManager = function(el, forceNew){
11661     el = Roo.get(el);
11662     if(!forceNew && el.updateManager){
11663         return el.updateManager;
11664     }
11665     /**
11666      * The Element object
11667      * @type Roo.Element
11668      */
11669     this.el = el;
11670     /**
11671      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11672      * @type String
11673      */
11674     this.defaultUrl = null;
11675
11676     this.addEvents({
11677         /**
11678          * @event beforeupdate
11679          * Fired before an update is made, return false from your handler and the update is cancelled.
11680          * @param {Roo.Element} el
11681          * @param {String/Object/Function} url
11682          * @param {String/Object} params
11683          */
11684         "beforeupdate": true,
11685         /**
11686          * @event update
11687          * Fired after successful update is made.
11688          * @param {Roo.Element} el
11689          * @param {Object} oResponseObject The response Object
11690          */
11691         "update": true,
11692         /**
11693          * @event failure
11694          * Fired on update failure.
11695          * @param {Roo.Element} el
11696          * @param {Object} oResponseObject The response Object
11697          */
11698         "failure": true
11699     });
11700     var d = Roo.UpdateManager.defaults;
11701     /**
11702      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11703      * @type String
11704      */
11705     this.sslBlankUrl = d.sslBlankUrl;
11706     /**
11707      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11708      * @type Boolean
11709      */
11710     this.disableCaching = d.disableCaching;
11711     /**
11712      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11713      * @type String
11714      */
11715     this.indicatorText = d.indicatorText;
11716     /**
11717      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11718      * @type String
11719      */
11720     this.showLoadIndicator = d.showLoadIndicator;
11721     /**
11722      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11723      * @type Number
11724      */
11725     this.timeout = d.timeout;
11726
11727     /**
11728      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11729      * @type Boolean
11730      */
11731     this.loadScripts = d.loadScripts;
11732
11733     /**
11734      * Transaction object of current executing transaction
11735      */
11736     this.transaction = null;
11737
11738     /**
11739      * @private
11740      */
11741     this.autoRefreshProcId = null;
11742     /**
11743      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11744      * @type Function
11745      */
11746     this.refreshDelegate = this.refresh.createDelegate(this);
11747     /**
11748      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11749      * @type Function
11750      */
11751     this.updateDelegate = this.update.createDelegate(this);
11752     /**
11753      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11754      * @type Function
11755      */
11756     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11757     /**
11758      * @private
11759      */
11760     this.successDelegate = this.processSuccess.createDelegate(this);
11761     /**
11762      * @private
11763      */
11764     this.failureDelegate = this.processFailure.createDelegate(this);
11765
11766     if(!this.renderer){
11767      /**
11768       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11769       */
11770     this.renderer = new Roo.UpdateManager.BasicRenderer();
11771     }
11772     
11773     Roo.UpdateManager.superclass.constructor.call(this);
11774 };
11775
11776 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11777     /**
11778      * Get the Element this UpdateManager is bound to
11779      * @return {Roo.Element} The element
11780      */
11781     getEl : function(){
11782         return this.el;
11783     },
11784     /**
11785      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11786      * @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:
11787 <pre><code>
11788 um.update({<br/>
11789     url: "your-url.php",<br/>
11790     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11791     callback: yourFunction,<br/>
11792     scope: yourObject, //(optional scope)  <br/>
11793     discardUrl: false, <br/>
11794     nocache: false,<br/>
11795     text: "Loading...",<br/>
11796     timeout: 30,<br/>
11797     scripts: false<br/>
11798 });
11799 </code></pre>
11800      * The only required property is url. The optional properties nocache, text and scripts
11801      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11802      * @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}
11803      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11804      * @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.
11805      */
11806     update : function(url, params, callback, discardUrl){
11807         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11808             var method = this.method,
11809                 cfg;
11810             if(typeof url == "object"){ // must be config object
11811                 cfg = url;
11812                 url = cfg.url;
11813                 params = params || cfg.params;
11814                 callback = callback || cfg.callback;
11815                 discardUrl = discardUrl || cfg.discardUrl;
11816                 if(callback && cfg.scope){
11817                     callback = callback.createDelegate(cfg.scope);
11818                 }
11819                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11820                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11821                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11822                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11823                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11824             }
11825             this.showLoading();
11826             if(!discardUrl){
11827                 this.defaultUrl = url;
11828             }
11829             if(typeof url == "function"){
11830                 url = url.call(this);
11831             }
11832
11833             method = method || (params ? "POST" : "GET");
11834             if(method == "GET"){
11835                 url = this.prepareUrl(url);
11836             }
11837
11838             var o = Roo.apply(cfg ||{}, {
11839                 url : url,
11840                 params: params,
11841                 success: this.successDelegate,
11842                 failure: this.failureDelegate,
11843                 callback: undefined,
11844                 timeout: (this.timeout*1000),
11845                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11846             });
11847             Roo.log("updated manager called with timeout of " + o.timeout);
11848             this.transaction = Roo.Ajax.request(o);
11849         }
11850     },
11851
11852     /**
11853      * 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.
11854      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11855      * @param {String/HTMLElement} form The form Id or form element
11856      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11857      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11858      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11859      */
11860     formUpdate : function(form, url, reset, callback){
11861         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11862             if(typeof url == "function"){
11863                 url = url.call(this);
11864             }
11865             form = Roo.getDom(form);
11866             this.transaction = Roo.Ajax.request({
11867                 form: form,
11868                 url:url,
11869                 success: this.successDelegate,
11870                 failure: this.failureDelegate,
11871                 timeout: (this.timeout*1000),
11872                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11873             });
11874             this.showLoading.defer(1, this);
11875         }
11876     },
11877
11878     /**
11879      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11880      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11881      */
11882     refresh : function(callback){
11883         if(this.defaultUrl == null){
11884             return;
11885         }
11886         this.update(this.defaultUrl, null, callback, true);
11887     },
11888
11889     /**
11890      * Set this element to auto refresh.
11891      * @param {Number} interval How often to update (in seconds).
11892      * @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)
11893      * @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}
11894      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11895      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11896      */
11897     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11898         if(refreshNow){
11899             this.update(url || this.defaultUrl, params, callback, true);
11900         }
11901         if(this.autoRefreshProcId){
11902             clearInterval(this.autoRefreshProcId);
11903         }
11904         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11905     },
11906
11907     /**
11908      * Stop auto refresh on this element.
11909      */
11910      stopAutoRefresh : function(){
11911         if(this.autoRefreshProcId){
11912             clearInterval(this.autoRefreshProcId);
11913             delete this.autoRefreshProcId;
11914         }
11915     },
11916
11917     isAutoRefreshing : function(){
11918        return this.autoRefreshProcId ? true : false;
11919     },
11920     /**
11921      * Called to update the element to "Loading" state. Override to perform custom action.
11922      */
11923     showLoading : function(){
11924         if(this.showLoadIndicator){
11925             this.el.update(this.indicatorText);
11926         }
11927     },
11928
11929     /**
11930      * Adds unique parameter to query string if disableCaching = true
11931      * @private
11932      */
11933     prepareUrl : function(url){
11934         if(this.disableCaching){
11935             var append = "_dc=" + (new Date().getTime());
11936             if(url.indexOf("?") !== -1){
11937                 url += "&" + append;
11938             }else{
11939                 url += "?" + append;
11940             }
11941         }
11942         return url;
11943     },
11944
11945     /**
11946      * @private
11947      */
11948     processSuccess : function(response){
11949         this.transaction = null;
11950         if(response.argument.form && response.argument.reset){
11951             try{ // put in try/catch since some older FF releases had problems with this
11952                 response.argument.form.reset();
11953             }catch(e){}
11954         }
11955         if(this.loadScripts){
11956             this.renderer.render(this.el, response, this,
11957                 this.updateComplete.createDelegate(this, [response]));
11958         }else{
11959             this.renderer.render(this.el, response, this);
11960             this.updateComplete(response);
11961         }
11962     },
11963
11964     updateComplete : function(response){
11965         this.fireEvent("update", this.el, response);
11966         if(typeof response.argument.callback == "function"){
11967             response.argument.callback(this.el, true, response);
11968         }
11969     },
11970
11971     /**
11972      * @private
11973      */
11974     processFailure : function(response){
11975         this.transaction = null;
11976         this.fireEvent("failure", this.el, response);
11977         if(typeof response.argument.callback == "function"){
11978             response.argument.callback(this.el, false, response);
11979         }
11980     },
11981
11982     /**
11983      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11984      * @param {Object} renderer The object implementing the render() method
11985      */
11986     setRenderer : function(renderer){
11987         this.renderer = renderer;
11988     },
11989
11990     getRenderer : function(){
11991        return this.renderer;
11992     },
11993
11994     /**
11995      * Set the defaultUrl used for updates
11996      * @param {String/Function} defaultUrl The url or a function to call to get the url
11997      */
11998     setDefaultUrl : function(defaultUrl){
11999         this.defaultUrl = defaultUrl;
12000     },
12001
12002     /**
12003      * Aborts the executing transaction
12004      */
12005     abort : function(){
12006         if(this.transaction){
12007             Roo.Ajax.abort(this.transaction);
12008         }
12009     },
12010
12011     /**
12012      * Returns true if an update is in progress
12013      * @return {Boolean}
12014      */
12015     isUpdating : function(){
12016         if(this.transaction){
12017             return Roo.Ajax.isLoading(this.transaction);
12018         }
12019         return false;
12020     }
12021 });
12022
12023 /**
12024  * @class Roo.UpdateManager.defaults
12025  * @static (not really - but it helps the doc tool)
12026  * The defaults collection enables customizing the default properties of UpdateManager
12027  */
12028    Roo.UpdateManager.defaults = {
12029        /**
12030          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12031          * @type Number
12032          */
12033          timeout : 30,
12034
12035          /**
12036          * True to process scripts by default (Defaults to false).
12037          * @type Boolean
12038          */
12039         loadScripts : false,
12040
12041         /**
12042         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12043         * @type String
12044         */
12045         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12046         /**
12047          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12048          * @type Boolean
12049          */
12050         disableCaching : false,
12051         /**
12052          * Whether to show indicatorText when loading (Defaults to true).
12053          * @type Boolean
12054          */
12055         showLoadIndicator : true,
12056         /**
12057          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12058          * @type String
12059          */
12060         indicatorText : '<div class="loading-indicator">Loading...</div>'
12061    };
12062
12063 /**
12064  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12065  *Usage:
12066  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12067  * @param {String/HTMLElement/Roo.Element} el The element to update
12068  * @param {String} url The url
12069  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12070  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12071  * @static
12072  * @deprecated
12073  * @member Roo.UpdateManager
12074  */
12075 Roo.UpdateManager.updateElement = function(el, url, params, options){
12076     var um = Roo.get(el, true).getUpdateManager();
12077     Roo.apply(um, options);
12078     um.update(url, params, options ? options.callback : null);
12079 };
12080 // alias for backwards compat
12081 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12082 /**
12083  * @class Roo.UpdateManager.BasicRenderer
12084  * Default Content renderer. Updates the elements innerHTML with the responseText.
12085  */
12086 Roo.UpdateManager.BasicRenderer = function(){};
12087
12088 Roo.UpdateManager.BasicRenderer.prototype = {
12089     /**
12090      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12091      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12092      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12093      * @param {Roo.Element} el The element being rendered
12094      * @param {Object} response The YUI Connect response object
12095      * @param {UpdateManager} updateManager The calling update manager
12096      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12097      */
12098      render : function(el, response, updateManager, callback){
12099         el.update(response.responseText, updateManager.loadScripts, callback);
12100     }
12101 };
12102 /*
12103  * Based on:
12104  * Roo JS
12105  * (c)) Alan Knowles
12106  * Licence : LGPL
12107  */
12108
12109
12110 /**
12111  * @class Roo.DomTemplate
12112  * @extends Roo.Template
12113  * An effort at a dom based template engine..
12114  *
12115  * Similar to XTemplate, except it uses dom parsing to create the template..
12116  *
12117  * Supported features:
12118  *
12119  *  Tags:
12120
12121 <pre><code>
12122       {a_variable} - output encoded.
12123       {a_variable.format:("Y-m-d")} - call a method on the variable
12124       {a_variable:raw} - unencoded output
12125       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12126       {a_variable:this.method_on_template(...)} - call a method on the template object.
12127  
12128 </code></pre>
12129  *  The tpl tag:
12130 <pre><code>
12131         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12132         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12133         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12134         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12135   
12136 </code></pre>
12137  *      
12138  */
12139 Roo.DomTemplate = function()
12140 {
12141      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12142      if (this.html) {
12143         this.compile();
12144      }
12145 };
12146
12147
12148 Roo.extend(Roo.DomTemplate, Roo.Template, {
12149     /**
12150      * id counter for sub templates.
12151      */
12152     id : 0,
12153     /**
12154      * flag to indicate if dom parser is inside a pre,
12155      * it will strip whitespace if not.
12156      */
12157     inPre : false,
12158     
12159     /**
12160      * The various sub templates
12161      */
12162     tpls : false,
12163     
12164     
12165     
12166     /**
12167      *
12168      * basic tag replacing syntax
12169      * WORD:WORD()
12170      *
12171      * // you can fake an object call by doing this
12172      *  x.t:(test,tesT) 
12173      * 
12174      */
12175     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12176     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12177     
12178     iterChild : function (node, method) {
12179         
12180         var oldPre = this.inPre;
12181         if (node.tagName == 'PRE') {
12182             this.inPre = true;
12183         }
12184         for( var i = 0; i < node.childNodes.length; i++) {
12185             method.call(this, node.childNodes[i]);
12186         }
12187         this.inPre = oldPre;
12188     },
12189     
12190     
12191     
12192     /**
12193      * compile the template
12194      *
12195      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12196      *
12197      */
12198     compile: function()
12199     {
12200         var s = this.html;
12201         
12202         // covert the html into DOM...
12203         var doc = false;
12204         var div =false;
12205         try {
12206             doc = document.implementation.createHTMLDocument("");
12207             doc.documentElement.innerHTML =   this.html  ;
12208             div = doc.documentElement;
12209         } catch (e) {
12210             // old IE... - nasty -- it causes all sorts of issues.. with
12211             // images getting pulled from server..
12212             div = document.createElement('div');
12213             div.innerHTML = this.html;
12214         }
12215         //doc.documentElement.innerHTML = htmlBody
12216          
12217         
12218         
12219         this.tpls = [];
12220         var _t = this;
12221         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12222         
12223         var tpls = this.tpls;
12224         
12225         // create a top level template from the snippet..
12226         
12227         //Roo.log(div.innerHTML);
12228         
12229         var tpl = {
12230             uid : 'master',
12231             id : this.id++,
12232             attr : false,
12233             value : false,
12234             body : div.innerHTML,
12235             
12236             forCall : false,
12237             execCall : false,
12238             dom : div,
12239             isTop : true
12240             
12241         };
12242         tpls.unshift(tpl);
12243         
12244         
12245         // compile them...
12246         this.tpls = [];
12247         Roo.each(tpls, function(tp){
12248             this.compileTpl(tp);
12249             this.tpls[tp.id] = tp;
12250         }, this);
12251         
12252         this.master = tpls[0];
12253         return this;
12254         
12255         
12256     },
12257     
12258     compileNode : function(node, istop) {
12259         // test for
12260         //Roo.log(node);
12261         
12262         
12263         // skip anything not a tag..
12264         if (node.nodeType != 1) {
12265             if (node.nodeType == 3 && !this.inPre) {
12266                 // reduce white space..
12267                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12268                 
12269             }
12270             return;
12271         }
12272         
12273         var tpl = {
12274             uid : false,
12275             id : false,
12276             attr : false,
12277             value : false,
12278             body : '',
12279             
12280             forCall : false,
12281             execCall : false,
12282             dom : false,
12283             isTop : istop
12284             
12285             
12286         };
12287         
12288         
12289         switch(true) {
12290             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12291             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12292             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12293             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12294             // no default..
12295         }
12296         
12297         
12298         if (!tpl.attr) {
12299             // just itterate children..
12300             this.iterChild(node,this.compileNode);
12301             return;
12302         }
12303         tpl.uid = this.id++;
12304         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12305         node.removeAttribute('roo-'+ tpl.attr);
12306         if (tpl.attr != 'name') {
12307             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12308             node.parentNode.replaceChild(placeholder,  node);
12309         } else {
12310             
12311             var placeholder =  document.createElement('span');
12312             placeholder.className = 'roo-tpl-' + tpl.value;
12313             node.parentNode.replaceChild(placeholder,  node);
12314         }
12315         
12316         // parent now sees '{domtplXXXX}
12317         this.iterChild(node,this.compileNode);
12318         
12319         // we should now have node body...
12320         var div = document.createElement('div');
12321         div.appendChild(node);
12322         tpl.dom = node;
12323         // this has the unfortunate side effect of converting tagged attributes
12324         // eg. href="{...}" into %7C...%7D
12325         // this has been fixed by searching for those combo's although it's a bit hacky..
12326         
12327         
12328         tpl.body = div.innerHTML;
12329         
12330         
12331          
12332         tpl.id = tpl.uid;
12333         switch(tpl.attr) {
12334             case 'for' :
12335                 switch (tpl.value) {
12336                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12337                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12338                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12339                 }
12340                 break;
12341             
12342             case 'exec':
12343                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12344                 break;
12345             
12346             case 'if':     
12347                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12348                 break;
12349             
12350             case 'name':
12351                 tpl.id  = tpl.value; // replace non characters???
12352                 break;
12353             
12354         }
12355         
12356         
12357         this.tpls.push(tpl);
12358         
12359         
12360         
12361     },
12362     
12363     
12364     
12365     
12366     /**
12367      * Compile a segment of the template into a 'sub-template'
12368      *
12369      * 
12370      * 
12371      *
12372      */
12373     compileTpl : function(tpl)
12374     {
12375         var fm = Roo.util.Format;
12376         var useF = this.disableFormats !== true;
12377         
12378         var sep = Roo.isGecko ? "+\n" : ",\n";
12379         
12380         var undef = function(str) {
12381             Roo.debug && Roo.log("Property not found :"  + str);
12382             return '';
12383         };
12384           
12385         //Roo.log(tpl.body);
12386         
12387         
12388         
12389         var fn = function(m, lbrace, name, format, args)
12390         {
12391             //Roo.log("ARGS");
12392             //Roo.log(arguments);
12393             args = args ? args.replace(/\\'/g,"'") : args;
12394             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12395             if (typeof(format) == 'undefined') {
12396                 format =  'htmlEncode'; 
12397             }
12398             if (format == 'raw' ) {
12399                 format = false;
12400             }
12401             
12402             if(name.substr(0, 6) == 'domtpl'){
12403                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12404             }
12405             
12406             // build an array of options to determine if value is undefined..
12407             
12408             // basically get 'xxxx.yyyy' then do
12409             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12410             //    (function () { Roo.log("Property not found"); return ''; })() :
12411             //    ......
12412             
12413             var udef_ar = [];
12414             var lookfor = '';
12415             Roo.each(name.split('.'), function(st) {
12416                 lookfor += (lookfor.length ? '.': '') + st;
12417                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12418             });
12419             
12420             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12421             
12422             
12423             if(format && useF){
12424                 
12425                 args = args ? ',' + args : "";
12426                  
12427                 if(format.substr(0, 5) != "this."){
12428                     format = "fm." + format + '(';
12429                 }else{
12430                     format = 'this.call("'+ format.substr(5) + '", ';
12431                     args = ", values";
12432                 }
12433                 
12434                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12435             }
12436              
12437             if (args && args.length) {
12438                 // called with xxyx.yuu:(test,test)
12439                 // change to ()
12440                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12441             }
12442             // raw.. - :raw modifier..
12443             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12444             
12445         };
12446         var body;
12447         // branched to use + in gecko and [].join() in others
12448         if(Roo.isGecko){
12449             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12450                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12451                     "';};};";
12452         }else{
12453             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12454             body.push(tpl.body.replace(/(\r\n|\n)/g,
12455                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12456             body.push("'].join('');};};");
12457             body = body.join('');
12458         }
12459         
12460         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12461        
12462         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12463         eval(body);
12464         
12465         return this;
12466     },
12467      
12468     /**
12469      * same as applyTemplate, except it's done to one of the subTemplates
12470      * when using named templates, you can do:
12471      *
12472      * var str = pl.applySubTemplate('your-name', values);
12473      *
12474      * 
12475      * @param {Number} id of the template
12476      * @param {Object} values to apply to template
12477      * @param {Object} parent (normaly the instance of this object)
12478      */
12479     applySubTemplate : function(id, values, parent)
12480     {
12481         
12482         
12483         var t = this.tpls[id];
12484         
12485         
12486         try { 
12487             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12488                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12489                 return '';
12490             }
12491         } catch(e) {
12492             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12493             Roo.log(values);
12494           
12495             return '';
12496         }
12497         try { 
12498             
12499             if(t.execCall && t.execCall.call(this, values, parent)){
12500                 return '';
12501             }
12502         } catch(e) {
12503             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12504             Roo.log(values);
12505             return '';
12506         }
12507         
12508         try {
12509             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12510             parent = t.target ? values : parent;
12511             if(t.forCall && vs instanceof Array){
12512                 var buf = [];
12513                 for(var i = 0, len = vs.length; i < len; i++){
12514                     try {
12515                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12516                     } catch (e) {
12517                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12518                         Roo.log(e.body);
12519                         //Roo.log(t.compiled);
12520                         Roo.log(vs[i]);
12521                     }   
12522                 }
12523                 return buf.join('');
12524             }
12525         } catch (e) {
12526             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12527             Roo.log(values);
12528             return '';
12529         }
12530         try {
12531             return t.compiled.call(this, vs, parent);
12532         } catch (e) {
12533             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12534             Roo.log(e.body);
12535             //Roo.log(t.compiled);
12536             Roo.log(values);
12537             return '';
12538         }
12539     },
12540
12541    
12542
12543     applyTemplate : function(values){
12544         return this.master.compiled.call(this, values, {});
12545         //var s = this.subs;
12546     },
12547
12548     apply : function(){
12549         return this.applyTemplate.apply(this, arguments);
12550     }
12551
12552  });
12553
12554 Roo.DomTemplate.from = function(el){
12555     el = Roo.getDom(el);
12556     return new Roo.Domtemplate(el.value || el.innerHTML);
12557 };/*
12558  * Based on:
12559  * Ext JS Library 1.1.1
12560  * Copyright(c) 2006-2007, Ext JS, LLC.
12561  *
12562  * Originally Released Under LGPL - original licence link has changed is not relivant.
12563  *
12564  * Fork - LGPL
12565  * <script type="text/javascript">
12566  */
12567
12568 /**
12569  * @class Roo.util.DelayedTask
12570  * Provides a convenient method of performing setTimeout where a new
12571  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12572  * You can use this class to buffer
12573  * the keypress events for a certain number of milliseconds, and perform only if they stop
12574  * for that amount of time.
12575  * @constructor The parameters to this constructor serve as defaults and are not required.
12576  * @param {Function} fn (optional) The default function to timeout
12577  * @param {Object} scope (optional) The default scope of that timeout
12578  * @param {Array} args (optional) The default Array of arguments
12579  */
12580 Roo.util.DelayedTask = function(fn, scope, args){
12581     var id = null, d, t;
12582
12583     var call = function(){
12584         var now = new Date().getTime();
12585         if(now - t >= d){
12586             clearInterval(id);
12587             id = null;
12588             fn.apply(scope, args || []);
12589         }
12590     };
12591     /**
12592      * Cancels any pending timeout and queues a new one
12593      * @param {Number} delay The milliseconds to delay
12594      * @param {Function} newFn (optional) Overrides function passed to constructor
12595      * @param {Object} newScope (optional) Overrides scope passed to constructor
12596      * @param {Array} newArgs (optional) Overrides args passed to constructor
12597      */
12598     this.delay = function(delay, newFn, newScope, newArgs){
12599         if(id && delay != d){
12600             this.cancel();
12601         }
12602         d = delay;
12603         t = new Date().getTime();
12604         fn = newFn || fn;
12605         scope = newScope || scope;
12606         args = newArgs || args;
12607         if(!id){
12608             id = setInterval(call, d);
12609         }
12610     };
12611
12612     /**
12613      * Cancel the last queued timeout
12614      */
12615     this.cancel = function(){
12616         if(id){
12617             clearInterval(id);
12618             id = null;
12619         }
12620     };
12621 };/*
12622  * Based on:
12623  * Ext JS Library 1.1.1
12624  * Copyright(c) 2006-2007, Ext JS, LLC.
12625  *
12626  * Originally Released Under LGPL - original licence link has changed is not relivant.
12627  *
12628  * Fork - LGPL
12629  * <script type="text/javascript">
12630  */
12631  
12632  
12633 Roo.util.TaskRunner = function(interval){
12634     interval = interval || 10;
12635     var tasks = [], removeQueue = [];
12636     var id = 0;
12637     var running = false;
12638
12639     var stopThread = function(){
12640         running = false;
12641         clearInterval(id);
12642         id = 0;
12643     };
12644
12645     var startThread = function(){
12646         if(!running){
12647             running = true;
12648             id = setInterval(runTasks, interval);
12649         }
12650     };
12651
12652     var removeTask = function(task){
12653         removeQueue.push(task);
12654         if(task.onStop){
12655             task.onStop();
12656         }
12657     };
12658
12659     var runTasks = function(){
12660         if(removeQueue.length > 0){
12661             for(var i = 0, len = removeQueue.length; i < len; i++){
12662                 tasks.remove(removeQueue[i]);
12663             }
12664             removeQueue = [];
12665             if(tasks.length < 1){
12666                 stopThread();
12667                 return;
12668             }
12669         }
12670         var now = new Date().getTime();
12671         for(var i = 0, len = tasks.length; i < len; ++i){
12672             var t = tasks[i];
12673             var itime = now - t.taskRunTime;
12674             if(t.interval <= itime){
12675                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12676                 t.taskRunTime = now;
12677                 if(rt === false || t.taskRunCount === t.repeat){
12678                     removeTask(t);
12679                     return;
12680                 }
12681             }
12682             if(t.duration && t.duration <= (now - t.taskStartTime)){
12683                 removeTask(t);
12684             }
12685         }
12686     };
12687
12688     /**
12689      * Queues a new task.
12690      * @param {Object} task
12691      */
12692     this.start = function(task){
12693         tasks.push(task);
12694         task.taskStartTime = new Date().getTime();
12695         task.taskRunTime = 0;
12696         task.taskRunCount = 0;
12697         startThread();
12698         return task;
12699     };
12700
12701     this.stop = function(task){
12702         removeTask(task);
12703         return task;
12704     };
12705
12706     this.stopAll = function(){
12707         stopThread();
12708         for(var i = 0, len = tasks.length; i < len; i++){
12709             if(tasks[i].onStop){
12710                 tasks[i].onStop();
12711             }
12712         }
12713         tasks = [];
12714         removeQueue = [];
12715     };
12716 };
12717
12718 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12719  * Based on:
12720  * Ext JS Library 1.1.1
12721  * Copyright(c) 2006-2007, Ext JS, LLC.
12722  *
12723  * Originally Released Under LGPL - original licence link has changed is not relivant.
12724  *
12725  * Fork - LGPL
12726  * <script type="text/javascript">
12727  */
12728
12729  
12730 /**
12731  * @class Roo.util.MixedCollection
12732  * @extends Roo.util.Observable
12733  * A Collection class that maintains both numeric indexes and keys and exposes events.
12734  * @constructor
12735  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12736  * collection (defaults to false)
12737  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12738  * and return the key value for that item.  This is used when available to look up the key on items that
12739  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12740  * equivalent to providing an implementation for the {@link #getKey} method.
12741  */
12742 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12743     this.items = [];
12744     this.map = {};
12745     this.keys = [];
12746     this.length = 0;
12747     this.addEvents({
12748         /**
12749          * @event clear
12750          * Fires when the collection is cleared.
12751          */
12752         "clear" : true,
12753         /**
12754          * @event add
12755          * Fires when an item is added to the collection.
12756          * @param {Number} index The index at which the item was added.
12757          * @param {Object} o The item added.
12758          * @param {String} key The key associated with the added item.
12759          */
12760         "add" : true,
12761         /**
12762          * @event replace
12763          * Fires when an item is replaced in the collection.
12764          * @param {String} key he key associated with the new added.
12765          * @param {Object} old The item being replaced.
12766          * @param {Object} new The new item.
12767          */
12768         "replace" : true,
12769         /**
12770          * @event remove
12771          * Fires when an item is removed from the collection.
12772          * @param {Object} o The item being removed.
12773          * @param {String} key (optional) The key associated with the removed item.
12774          */
12775         "remove" : true,
12776         "sort" : true
12777     });
12778     this.allowFunctions = allowFunctions === true;
12779     if(keyFn){
12780         this.getKey = keyFn;
12781     }
12782     Roo.util.MixedCollection.superclass.constructor.call(this);
12783 };
12784
12785 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12786     allowFunctions : false,
12787     
12788 /**
12789  * Adds an item to the collection.
12790  * @param {String} key The key to associate with the item
12791  * @param {Object} o The item to add.
12792  * @return {Object} The item added.
12793  */
12794     add : function(key, o){
12795         if(arguments.length == 1){
12796             o = arguments[0];
12797             key = this.getKey(o);
12798         }
12799         if(typeof key == "undefined" || key === null){
12800             this.length++;
12801             this.items.push(o);
12802             this.keys.push(null);
12803         }else{
12804             var old = this.map[key];
12805             if(old){
12806                 return this.replace(key, o);
12807             }
12808             this.length++;
12809             this.items.push(o);
12810             this.map[key] = o;
12811             this.keys.push(key);
12812         }
12813         this.fireEvent("add", this.length-1, o, key);
12814         return o;
12815     },
12816        
12817 /**
12818   * MixedCollection has a generic way to fetch keys if you implement getKey.
12819 <pre><code>
12820 // normal way
12821 var mc = new Roo.util.MixedCollection();
12822 mc.add(someEl.dom.id, someEl);
12823 mc.add(otherEl.dom.id, otherEl);
12824 //and so on
12825
12826 // using getKey
12827 var mc = new Roo.util.MixedCollection();
12828 mc.getKey = function(el){
12829    return el.dom.id;
12830 };
12831 mc.add(someEl);
12832 mc.add(otherEl);
12833
12834 // or via the constructor
12835 var mc = new Roo.util.MixedCollection(false, function(el){
12836    return el.dom.id;
12837 });
12838 mc.add(someEl);
12839 mc.add(otherEl);
12840 </code></pre>
12841  * @param o {Object} The item for which to find the key.
12842  * @return {Object} The key for the passed item.
12843  */
12844     getKey : function(o){
12845          return o.id; 
12846     },
12847    
12848 /**
12849  * Replaces an item in the collection.
12850  * @param {String} key The key associated with the item to replace, or the item to replace.
12851  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12852  * @return {Object}  The new item.
12853  */
12854     replace : function(key, o){
12855         if(arguments.length == 1){
12856             o = arguments[0];
12857             key = this.getKey(o);
12858         }
12859         var old = this.item(key);
12860         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12861              return this.add(key, o);
12862         }
12863         var index = this.indexOfKey(key);
12864         this.items[index] = o;
12865         this.map[key] = o;
12866         this.fireEvent("replace", key, old, o);
12867         return o;
12868     },
12869    
12870 /**
12871  * Adds all elements of an Array or an Object to the collection.
12872  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12873  * an Array of values, each of which are added to the collection.
12874  */
12875     addAll : function(objs){
12876         if(arguments.length > 1 || objs instanceof Array){
12877             var args = arguments.length > 1 ? arguments : objs;
12878             for(var i = 0, len = args.length; i < len; i++){
12879                 this.add(args[i]);
12880             }
12881         }else{
12882             for(var key in objs){
12883                 if(this.allowFunctions || typeof objs[key] != "function"){
12884                     this.add(key, objs[key]);
12885                 }
12886             }
12887         }
12888     },
12889    
12890 /**
12891  * Executes the specified function once for every item in the collection, passing each
12892  * item as the first and only parameter. returning false from the function will stop the iteration.
12893  * @param {Function} fn The function to execute for each item.
12894  * @param {Object} scope (optional) The scope in which to execute the function.
12895  */
12896     each : function(fn, scope){
12897         var items = [].concat(this.items); // each safe for removal
12898         for(var i = 0, len = items.length; i < len; i++){
12899             if(fn.call(scope || items[i], items[i], i, len) === false){
12900                 break;
12901             }
12902         }
12903     },
12904    
12905 /**
12906  * Executes the specified function once for every key in the collection, passing each
12907  * key, and its associated item as the first two parameters.
12908  * @param {Function} fn The function to execute for each item.
12909  * @param {Object} scope (optional) The scope in which to execute the function.
12910  */
12911     eachKey : function(fn, scope){
12912         for(var i = 0, len = this.keys.length; i < len; i++){
12913             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12914         }
12915     },
12916    
12917 /**
12918  * Returns the first item in the collection which elicits a true return value from the
12919  * passed selection function.
12920  * @param {Function} fn The selection function to execute for each item.
12921  * @param {Object} scope (optional) The scope in which to execute the function.
12922  * @return {Object} The first item in the collection which returned true from the selection function.
12923  */
12924     find : function(fn, scope){
12925         for(var i = 0, len = this.items.length; i < len; i++){
12926             if(fn.call(scope || window, this.items[i], this.keys[i])){
12927                 return this.items[i];
12928             }
12929         }
12930         return null;
12931     },
12932    
12933 /**
12934  * Inserts an item at the specified index in the collection.
12935  * @param {Number} index The index to insert the item at.
12936  * @param {String} key The key to associate with the new item, or the item itself.
12937  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12938  * @return {Object} The item inserted.
12939  */
12940     insert : function(index, key, o){
12941         if(arguments.length == 2){
12942             o = arguments[1];
12943             key = this.getKey(o);
12944         }
12945         if(index >= this.length){
12946             return this.add(key, o);
12947         }
12948         this.length++;
12949         this.items.splice(index, 0, o);
12950         if(typeof key != "undefined" && key != null){
12951             this.map[key] = o;
12952         }
12953         this.keys.splice(index, 0, key);
12954         this.fireEvent("add", index, o, key);
12955         return o;
12956     },
12957    
12958 /**
12959  * Removed an item from the collection.
12960  * @param {Object} o The item to remove.
12961  * @return {Object} The item removed.
12962  */
12963     remove : function(o){
12964         return this.removeAt(this.indexOf(o));
12965     },
12966    
12967 /**
12968  * Remove an item from a specified index in the collection.
12969  * @param {Number} index The index within the collection of the item to remove.
12970  */
12971     removeAt : function(index){
12972         if(index < this.length && index >= 0){
12973             this.length--;
12974             var o = this.items[index];
12975             this.items.splice(index, 1);
12976             var key = this.keys[index];
12977             if(typeof key != "undefined"){
12978                 delete this.map[key];
12979             }
12980             this.keys.splice(index, 1);
12981             this.fireEvent("remove", o, key);
12982         }
12983     },
12984    
12985 /**
12986  * Removed an item associated with the passed key fom the collection.
12987  * @param {String} key The key of the item to remove.
12988  */
12989     removeKey : function(key){
12990         return this.removeAt(this.indexOfKey(key));
12991     },
12992    
12993 /**
12994  * Returns the number of items in the collection.
12995  * @return {Number} the number of items in the collection.
12996  */
12997     getCount : function(){
12998         return this.length; 
12999     },
13000    
13001 /**
13002  * Returns index within the collection of the passed Object.
13003  * @param {Object} o The item to find the index of.
13004  * @return {Number} index of the item.
13005  */
13006     indexOf : function(o){
13007         if(!this.items.indexOf){
13008             for(var i = 0, len = this.items.length; i < len; i++){
13009                 if(this.items[i] == o) return i;
13010             }
13011             return -1;
13012         }else{
13013             return this.items.indexOf(o);
13014         }
13015     },
13016    
13017 /**
13018  * Returns index within the collection of the passed key.
13019  * @param {String} key The key to find the index of.
13020  * @return {Number} index of the key.
13021  */
13022     indexOfKey : function(key){
13023         if(!this.keys.indexOf){
13024             for(var i = 0, len = this.keys.length; i < len; i++){
13025                 if(this.keys[i] == key) return i;
13026             }
13027             return -1;
13028         }else{
13029             return this.keys.indexOf(key);
13030         }
13031     },
13032    
13033 /**
13034  * Returns the item associated with the passed key OR index. Key has priority over index.
13035  * @param {String/Number} key The key or index of the item.
13036  * @return {Object} The item associated with the passed key.
13037  */
13038     item : function(key){
13039         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13040         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13041     },
13042     
13043 /**
13044  * Returns the item at the specified index.
13045  * @param {Number} index The index of the item.
13046  * @return {Object}
13047  */
13048     itemAt : function(index){
13049         return this.items[index];
13050     },
13051     
13052 /**
13053  * Returns the item associated with the passed key.
13054  * @param {String/Number} key The key of the item.
13055  * @return {Object} The item associated with the passed key.
13056  */
13057     key : function(key){
13058         return this.map[key];
13059     },
13060    
13061 /**
13062  * Returns true if the collection contains the passed Object as an item.
13063  * @param {Object} o  The Object to look for in the collection.
13064  * @return {Boolean} True if the collection contains the Object as an item.
13065  */
13066     contains : function(o){
13067         return this.indexOf(o) != -1;
13068     },
13069    
13070 /**
13071  * Returns true if the collection contains the passed Object as a key.
13072  * @param {String} key The key to look for in the collection.
13073  * @return {Boolean} True if the collection contains the Object as a key.
13074  */
13075     containsKey : function(key){
13076         return typeof this.map[key] != "undefined";
13077     },
13078    
13079 /**
13080  * Removes all items from the collection.
13081  */
13082     clear : function(){
13083         this.length = 0;
13084         this.items = [];
13085         this.keys = [];
13086         this.map = {};
13087         this.fireEvent("clear");
13088     },
13089    
13090 /**
13091  * Returns the first item in the collection.
13092  * @return {Object} the first item in the collection..
13093  */
13094     first : function(){
13095         return this.items[0]; 
13096     },
13097    
13098 /**
13099  * Returns the last item in the collection.
13100  * @return {Object} the last item in the collection..
13101  */
13102     last : function(){
13103         return this.items[this.length-1];   
13104     },
13105     
13106     _sort : function(property, dir, fn){
13107         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13108         fn = fn || function(a, b){
13109             return a-b;
13110         };
13111         var c = [], k = this.keys, items = this.items;
13112         for(var i = 0, len = items.length; i < len; i++){
13113             c[c.length] = {key: k[i], value: items[i], index: i};
13114         }
13115         c.sort(function(a, b){
13116             var v = fn(a[property], b[property]) * dsc;
13117             if(v == 0){
13118                 v = (a.index < b.index ? -1 : 1);
13119             }
13120             return v;
13121         });
13122         for(var i = 0, len = c.length; i < len; i++){
13123             items[i] = c[i].value;
13124             k[i] = c[i].key;
13125         }
13126         this.fireEvent("sort", this);
13127     },
13128     
13129     /**
13130      * Sorts this collection with the passed comparison function
13131      * @param {String} direction (optional) "ASC" or "DESC"
13132      * @param {Function} fn (optional) comparison function
13133      */
13134     sort : function(dir, fn){
13135         this._sort("value", dir, fn);
13136     },
13137     
13138     /**
13139      * Sorts this collection by keys
13140      * @param {String} direction (optional) "ASC" or "DESC"
13141      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13142      */
13143     keySort : function(dir, fn){
13144         this._sort("key", dir, fn || function(a, b){
13145             return String(a).toUpperCase()-String(b).toUpperCase();
13146         });
13147     },
13148     
13149     /**
13150      * Returns a range of items in this collection
13151      * @param {Number} startIndex (optional) defaults to 0
13152      * @param {Number} endIndex (optional) default to the last item
13153      * @return {Array} An array of items
13154      */
13155     getRange : function(start, end){
13156         var items = this.items;
13157         if(items.length < 1){
13158             return [];
13159         }
13160         start = start || 0;
13161         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13162         var r = [];
13163         if(start <= end){
13164             for(var i = start; i <= end; i++) {
13165                     r[r.length] = items[i];
13166             }
13167         }else{
13168             for(var i = start; i >= end; i--) {
13169                     r[r.length] = items[i];
13170             }
13171         }
13172         return r;
13173     },
13174         
13175     /**
13176      * Filter the <i>objects</i> in this collection by a specific property. 
13177      * Returns a new collection that has been filtered.
13178      * @param {String} property A property on your objects
13179      * @param {String/RegExp} value Either string that the property values 
13180      * should start with or a RegExp to test against the property
13181      * @return {MixedCollection} The new filtered collection
13182      */
13183     filter : function(property, value){
13184         if(!value.exec){ // not a regex
13185             value = String(value);
13186             if(value.length == 0){
13187                 return this.clone();
13188             }
13189             value = new RegExp("^" + Roo.escapeRe(value), "i");
13190         }
13191         return this.filterBy(function(o){
13192             return o && value.test(o[property]);
13193         });
13194         },
13195     
13196     /**
13197      * Filter by a function. * Returns a new collection that has been filtered.
13198      * The passed function will be called with each 
13199      * object in the collection. If the function returns true, the value is included 
13200      * otherwise it is filtered.
13201      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13202      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13203      * @return {MixedCollection} The new filtered collection
13204      */
13205     filterBy : function(fn, scope){
13206         var r = new Roo.util.MixedCollection();
13207         r.getKey = this.getKey;
13208         var k = this.keys, it = this.items;
13209         for(var i = 0, len = it.length; i < len; i++){
13210             if(fn.call(scope||this, it[i], k[i])){
13211                                 r.add(k[i], it[i]);
13212                         }
13213         }
13214         return r;
13215     },
13216     
13217     /**
13218      * Creates a duplicate of this collection
13219      * @return {MixedCollection}
13220      */
13221     clone : function(){
13222         var r = new Roo.util.MixedCollection();
13223         var k = this.keys, it = this.items;
13224         for(var i = 0, len = it.length; i < len; i++){
13225             r.add(k[i], it[i]);
13226         }
13227         r.getKey = this.getKey;
13228         return r;
13229     }
13230 });
13231 /**
13232  * Returns the item associated with the passed key or index.
13233  * @method
13234  * @param {String/Number} key The key or index of the item.
13235  * @return {Object} The item associated with the passed key.
13236  */
13237 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13238  * Based on:
13239  * Ext JS Library 1.1.1
13240  * Copyright(c) 2006-2007, Ext JS, LLC.
13241  *
13242  * Originally Released Under LGPL - original licence link has changed is not relivant.
13243  *
13244  * Fork - LGPL
13245  * <script type="text/javascript">
13246  */
13247 /**
13248  * @class Roo.util.JSON
13249  * Modified version of Douglas Crockford"s json.js that doesn"t
13250  * mess with the Object prototype 
13251  * http://www.json.org/js.html
13252  * @singleton
13253  */
13254 Roo.util.JSON = new (function(){
13255     var useHasOwn = {}.hasOwnProperty ? true : false;
13256     
13257     // crashes Safari in some instances
13258     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13259     
13260     var pad = function(n) {
13261         return n < 10 ? "0" + n : n;
13262     };
13263     
13264     var m = {
13265         "\b": '\\b',
13266         "\t": '\\t',
13267         "\n": '\\n',
13268         "\f": '\\f',
13269         "\r": '\\r',
13270         '"' : '\\"',
13271         "\\": '\\\\'
13272     };
13273
13274     var encodeString = function(s){
13275         if (/["\\\x00-\x1f]/.test(s)) {
13276             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13277                 var c = m[b];
13278                 if(c){
13279                     return c;
13280                 }
13281                 c = b.charCodeAt();
13282                 return "\\u00" +
13283                     Math.floor(c / 16).toString(16) +
13284                     (c % 16).toString(16);
13285             }) + '"';
13286         }
13287         return '"' + s + '"';
13288     };
13289     
13290     var encodeArray = function(o){
13291         var a = ["["], b, i, l = o.length, v;
13292             for (i = 0; i < l; i += 1) {
13293                 v = o[i];
13294                 switch (typeof v) {
13295                     case "undefined":
13296                     case "function":
13297                     case "unknown":
13298                         break;
13299                     default:
13300                         if (b) {
13301                             a.push(',');
13302                         }
13303                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13304                         b = true;
13305                 }
13306             }
13307             a.push("]");
13308             return a.join("");
13309     };
13310     
13311     var encodeDate = function(o){
13312         return '"' + o.getFullYear() + "-" +
13313                 pad(o.getMonth() + 1) + "-" +
13314                 pad(o.getDate()) + "T" +
13315                 pad(o.getHours()) + ":" +
13316                 pad(o.getMinutes()) + ":" +
13317                 pad(o.getSeconds()) + '"';
13318     };
13319     
13320     /**
13321      * Encodes an Object, Array or other value
13322      * @param {Mixed} o The variable to encode
13323      * @return {String} The JSON string
13324      */
13325     this.encode = function(o)
13326     {
13327         // should this be extended to fully wrap stringify..
13328         
13329         if(typeof o == "undefined" || o === null){
13330             return "null";
13331         }else if(o instanceof Array){
13332             return encodeArray(o);
13333         }else if(o instanceof Date){
13334             return encodeDate(o);
13335         }else if(typeof o == "string"){
13336             return encodeString(o);
13337         }else if(typeof o == "number"){
13338             return isFinite(o) ? String(o) : "null";
13339         }else if(typeof o == "boolean"){
13340             return String(o);
13341         }else {
13342             var a = ["{"], b, i, v;
13343             for (i in o) {
13344                 if(!useHasOwn || o.hasOwnProperty(i)) {
13345                     v = o[i];
13346                     switch (typeof v) {
13347                     case "undefined":
13348                     case "function":
13349                     case "unknown":
13350                         break;
13351                     default:
13352                         if(b){
13353                             a.push(',');
13354                         }
13355                         a.push(this.encode(i), ":",
13356                                 v === null ? "null" : this.encode(v));
13357                         b = true;
13358                     }
13359                 }
13360             }
13361             a.push("}");
13362             return a.join("");
13363         }
13364     };
13365     
13366     /**
13367      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13368      * @param {String} json The JSON string
13369      * @return {Object} The resulting object
13370      */
13371     this.decode = function(json){
13372         
13373         return  /** eval:var:json */ eval("(" + json + ')');
13374     };
13375 })();
13376 /** 
13377  * Shorthand for {@link Roo.util.JSON#encode}
13378  * @member Roo encode 
13379  * @method */
13380 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13381 /** 
13382  * Shorthand for {@link Roo.util.JSON#decode}
13383  * @member Roo decode 
13384  * @method */
13385 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13386 /*
13387  * Based on:
13388  * Ext JS Library 1.1.1
13389  * Copyright(c) 2006-2007, Ext JS, LLC.
13390  *
13391  * Originally Released Under LGPL - original licence link has changed is not relivant.
13392  *
13393  * Fork - LGPL
13394  * <script type="text/javascript">
13395  */
13396  
13397 /**
13398  * @class Roo.util.Format
13399  * Reusable data formatting functions
13400  * @singleton
13401  */
13402 Roo.util.Format = function(){
13403     var trimRe = /^\s+|\s+$/g;
13404     return {
13405         /**
13406          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13407          * @param {String} value The string to truncate
13408          * @param {Number} length The maximum length to allow before truncating
13409          * @return {String} The converted text
13410          */
13411         ellipsis : function(value, len){
13412             if(value && value.length > len){
13413                 return value.substr(0, len-3)+"...";
13414             }
13415             return value;
13416         },
13417
13418         /**
13419          * Checks a reference and converts it to empty string if it is undefined
13420          * @param {Mixed} value Reference to check
13421          * @return {Mixed} Empty string if converted, otherwise the original value
13422          */
13423         undef : function(value){
13424             return typeof value != "undefined" ? value : "";
13425         },
13426
13427         /**
13428          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13429          * @param {String} value The string to encode
13430          * @return {String} The encoded text
13431          */
13432         htmlEncode : function(value){
13433             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13434         },
13435
13436         /**
13437          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13438          * @param {String} value The string to decode
13439          * @return {String} The decoded text
13440          */
13441         htmlDecode : function(value){
13442             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13443         },
13444
13445         /**
13446          * Trims any whitespace from either side of a string
13447          * @param {String} value The text to trim
13448          * @return {String} The trimmed text
13449          */
13450         trim : function(value){
13451             return String(value).replace(trimRe, "");
13452         },
13453
13454         /**
13455          * Returns a substring from within an original string
13456          * @param {String} value The original text
13457          * @param {Number} start The start index of the substring
13458          * @param {Number} length The length of the substring
13459          * @return {String} The substring
13460          */
13461         substr : function(value, start, length){
13462             return String(value).substr(start, length);
13463         },
13464
13465         /**
13466          * Converts a string to all lower case letters
13467          * @param {String} value The text to convert
13468          * @return {String} The converted text
13469          */
13470         lowercase : function(value){
13471             return String(value).toLowerCase();
13472         },
13473
13474         /**
13475          * Converts a string to all upper case letters
13476          * @param {String} value The text to convert
13477          * @return {String} The converted text
13478          */
13479         uppercase : function(value){
13480             return String(value).toUpperCase();
13481         },
13482
13483         /**
13484          * Converts the first character only of a string to upper case
13485          * @param {String} value The text to convert
13486          * @return {String} The converted text
13487          */
13488         capitalize : function(value){
13489             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13490         },
13491
13492         // private
13493         call : function(value, fn){
13494             if(arguments.length > 2){
13495                 var args = Array.prototype.slice.call(arguments, 2);
13496                 args.unshift(value);
13497                  
13498                 return /** eval:var:value */  eval(fn).apply(window, args);
13499             }else{
13500                 /** eval:var:value */
13501                 return /** eval:var:value */ eval(fn).call(window, value);
13502             }
13503         },
13504
13505        
13506         /**
13507          * safer version of Math.toFixed..??/
13508          * @param {Number/String} value The numeric value to format
13509          * @param {Number/String} value Decimal places 
13510          * @return {String} The formatted currency string
13511          */
13512         toFixed : function(v, n)
13513         {
13514             // why not use to fixed - precision is buggered???
13515             if (!n) {
13516                 return Math.round(v-0);
13517             }
13518             var fact = Math.pow(10,n+1);
13519             v = (Math.round((v-0)*fact))/fact;
13520             var z = (''+fact).substring(2);
13521             if (v == Math.floor(v)) {
13522                 return Math.floor(v) + '.' + z;
13523             }
13524             
13525             // now just padd decimals..
13526             var ps = String(v).split('.');
13527             var fd = (ps[1] + z);
13528             var r = fd.substring(0,n); 
13529             var rm = fd.substring(n); 
13530             if (rm < 5) {
13531                 return ps[0] + '.' + r;
13532             }
13533             r*=1; // turn it into a number;
13534             r++;
13535             if (String(r).length != n) {
13536                 ps[0]*=1;
13537                 ps[0]++;
13538                 r = String(r).substring(1); // chop the end off.
13539             }
13540             
13541             return ps[0] + '.' + r;
13542              
13543         },
13544         
13545         /**
13546          * Format a number as US currency
13547          * @param {Number/String} value The numeric value to format
13548          * @return {String} The formatted currency string
13549          */
13550         usMoney : function(v){
13551             return '$' + Roo.util.Format.number(v);
13552         },
13553         
13554         /**
13555          * Format a number
13556          * eventually this should probably emulate php's number_format
13557          * @param {Number/String} value The numeric value to format
13558          * @param {Number} decimals number of decimal places
13559          * @return {String} The formatted currency string
13560          */
13561         number : function(v,decimals)
13562         {
13563             // multiply and round.
13564             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13565             var mul = Math.pow(10, decimals);
13566             var zero = String(mul).substring(1);
13567             v = (Math.round((v-0)*mul))/mul;
13568             
13569             // if it's '0' number.. then
13570             
13571             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13572             v = String(v);
13573             var ps = v.split('.');
13574             var whole = ps[0];
13575             
13576             
13577             var r = /(\d+)(\d{3})/;
13578             // add comma's
13579             while (r.test(whole)) {
13580                 whole = whole.replace(r, '$1' + ',' + '$2');
13581             }
13582             
13583             
13584             var sub = ps[1] ?
13585                     // has decimals..
13586                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13587                     // does not have decimals
13588                     (decimals ? ('.' + zero) : '');
13589             
13590             
13591             return whole + sub ;
13592         },
13593         
13594         /**
13595          * Parse a value into a formatted date using the specified format pattern.
13596          * @param {Mixed} value The value to format
13597          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13598          * @return {String} The formatted date string
13599          */
13600         date : function(v, format){
13601             if(!v){
13602                 return "";
13603             }
13604             if(!(v instanceof Date)){
13605                 v = new Date(Date.parse(v));
13606             }
13607             return v.dateFormat(format || "m/d/Y");
13608         },
13609
13610         /**
13611          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13612          * @param {String} format Any valid date format string
13613          * @return {Function} The date formatting function
13614          */
13615         dateRenderer : function(format){
13616             return function(v){
13617                 return Roo.util.Format.date(v, format);  
13618             };
13619         },
13620
13621         // private
13622         stripTagsRE : /<\/?[^>]+>/gi,
13623         
13624         /**
13625          * Strips all HTML tags
13626          * @param {Mixed} value The text from which to strip tags
13627          * @return {String} The stripped text
13628          */
13629         stripTags : function(v){
13630             return !v ? v : String(v).replace(this.stripTagsRE, "");
13631         }
13632     };
13633 }();/*
13634  * Based on:
13635  * Ext JS Library 1.1.1
13636  * Copyright(c) 2006-2007, Ext JS, LLC.
13637  *
13638  * Originally Released Under LGPL - original licence link has changed is not relivant.
13639  *
13640  * Fork - LGPL
13641  * <script type="text/javascript">
13642  */
13643
13644
13645  
13646
13647 /**
13648  * @class Roo.MasterTemplate
13649  * @extends Roo.Template
13650  * Provides a template that can have child templates. The syntax is:
13651 <pre><code>
13652 var t = new Roo.MasterTemplate(
13653         '&lt;select name="{name}"&gt;',
13654                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13655         '&lt;/select&gt;'
13656 );
13657 t.add('options', {value: 'foo', text: 'bar'});
13658 // or you can add multiple child elements in one shot
13659 t.addAll('options', [
13660     {value: 'foo', text: 'bar'},
13661     {value: 'foo2', text: 'bar2'},
13662     {value: 'foo3', text: 'bar3'}
13663 ]);
13664 // then append, applying the master template values
13665 t.append('my-form', {name: 'my-select'});
13666 </code></pre>
13667 * A name attribute for the child template is not required if you have only one child
13668 * template or you want to refer to them by index.
13669  */
13670 Roo.MasterTemplate = function(){
13671     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13672     this.originalHtml = this.html;
13673     var st = {};
13674     var m, re = this.subTemplateRe;
13675     re.lastIndex = 0;
13676     var subIndex = 0;
13677     while(m = re.exec(this.html)){
13678         var name = m[1], content = m[2];
13679         st[subIndex] = {
13680             name: name,
13681             index: subIndex,
13682             buffer: [],
13683             tpl : new Roo.Template(content)
13684         };
13685         if(name){
13686             st[name] = st[subIndex];
13687         }
13688         st[subIndex].tpl.compile();
13689         st[subIndex].tpl.call = this.call.createDelegate(this);
13690         subIndex++;
13691     }
13692     this.subCount = subIndex;
13693     this.subs = st;
13694 };
13695 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13696     /**
13697     * The regular expression used to match sub templates
13698     * @type RegExp
13699     * @property
13700     */
13701     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13702
13703     /**
13704      * Applies the passed values to a child template.
13705      * @param {String/Number} name (optional) The name or index of the child template
13706      * @param {Array/Object} values The values to be applied to the template
13707      * @return {MasterTemplate} this
13708      */
13709      add : function(name, values){
13710         if(arguments.length == 1){
13711             values = arguments[0];
13712             name = 0;
13713         }
13714         var s = this.subs[name];
13715         s.buffer[s.buffer.length] = s.tpl.apply(values);
13716         return this;
13717     },
13718
13719     /**
13720      * Applies all the passed values to a child template.
13721      * @param {String/Number} name (optional) The name or index of the child template
13722      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13723      * @param {Boolean} reset (optional) True to reset the template first
13724      * @return {MasterTemplate} this
13725      */
13726     fill : function(name, values, reset){
13727         var a = arguments;
13728         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13729             values = a[0];
13730             name = 0;
13731             reset = a[1];
13732         }
13733         if(reset){
13734             this.reset();
13735         }
13736         for(var i = 0, len = values.length; i < len; i++){
13737             this.add(name, values[i]);
13738         }
13739         return this;
13740     },
13741
13742     /**
13743      * Resets the template for reuse
13744      * @return {MasterTemplate} this
13745      */
13746      reset : function(){
13747         var s = this.subs;
13748         for(var i = 0; i < this.subCount; i++){
13749             s[i].buffer = [];
13750         }
13751         return this;
13752     },
13753
13754     applyTemplate : function(values){
13755         var s = this.subs;
13756         var replaceIndex = -1;
13757         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13758             return s[++replaceIndex].buffer.join("");
13759         });
13760         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13761     },
13762
13763     apply : function(){
13764         return this.applyTemplate.apply(this, arguments);
13765     },
13766
13767     compile : function(){return this;}
13768 });
13769
13770 /**
13771  * Alias for fill().
13772  * @method
13773  */
13774 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13775  /**
13776  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13777  * var tpl = Roo.MasterTemplate.from('element-id');
13778  * @param {String/HTMLElement} el
13779  * @param {Object} config
13780  * @static
13781  */
13782 Roo.MasterTemplate.from = function(el, config){
13783     el = Roo.getDom(el);
13784     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13785 };/*
13786  * Based on:
13787  * Ext JS Library 1.1.1
13788  * Copyright(c) 2006-2007, Ext JS, LLC.
13789  *
13790  * Originally Released Under LGPL - original licence link has changed is not relivant.
13791  *
13792  * Fork - LGPL
13793  * <script type="text/javascript">
13794  */
13795
13796  
13797 /**
13798  * @class Roo.util.CSS
13799  * Utility class for manipulating CSS rules
13800  * @singleton
13801  */
13802 Roo.util.CSS = function(){
13803         var rules = null;
13804         var doc = document;
13805
13806     var camelRe = /(-[a-z])/gi;
13807     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13808
13809    return {
13810    /**
13811     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13812     * tag and appended to the HEAD of the document.
13813     * @param {String|Object} cssText The text containing the css rules
13814     * @param {String} id An id to add to the stylesheet for later removal
13815     * @return {StyleSheet}
13816     */
13817     createStyleSheet : function(cssText, id){
13818         var ss;
13819         var head = doc.getElementsByTagName("head")[0];
13820         var nrules = doc.createElement("style");
13821         nrules.setAttribute("type", "text/css");
13822         if(id){
13823             nrules.setAttribute("id", id);
13824         }
13825         if (typeof(cssText) != 'string') {
13826             // support object maps..
13827             // not sure if this a good idea.. 
13828             // perhaps it should be merged with the general css handling
13829             // and handle js style props.
13830             var cssTextNew = [];
13831             for(var n in cssText) {
13832                 var citems = [];
13833                 for(var k in cssText[n]) {
13834                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13835                 }
13836                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13837                 
13838             }
13839             cssText = cssTextNew.join("\n");
13840             
13841         }
13842        
13843        
13844        if(Roo.isIE){
13845            head.appendChild(nrules);
13846            ss = nrules.styleSheet;
13847            ss.cssText = cssText;
13848        }else{
13849            try{
13850                 nrules.appendChild(doc.createTextNode(cssText));
13851            }catch(e){
13852                nrules.cssText = cssText; 
13853            }
13854            head.appendChild(nrules);
13855            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13856        }
13857        this.cacheStyleSheet(ss);
13858        return ss;
13859    },
13860
13861    /**
13862     * Removes a style or link tag by id
13863     * @param {String} id The id of the tag
13864     */
13865    removeStyleSheet : function(id){
13866        var existing = doc.getElementById(id);
13867        if(existing){
13868            existing.parentNode.removeChild(existing);
13869        }
13870    },
13871
13872    /**
13873     * Dynamically swaps an existing stylesheet reference for a new one
13874     * @param {String} id The id of an existing link tag to remove
13875     * @param {String} url The href of the new stylesheet to include
13876     */
13877    swapStyleSheet : function(id, url){
13878        this.removeStyleSheet(id);
13879        var ss = doc.createElement("link");
13880        ss.setAttribute("rel", "stylesheet");
13881        ss.setAttribute("type", "text/css");
13882        ss.setAttribute("id", id);
13883        ss.setAttribute("href", url);
13884        doc.getElementsByTagName("head")[0].appendChild(ss);
13885    },
13886    
13887    /**
13888     * Refresh the rule cache if you have dynamically added stylesheets
13889     * @return {Object} An object (hash) of rules indexed by selector
13890     */
13891    refreshCache : function(){
13892        return this.getRules(true);
13893    },
13894
13895    // private
13896    cacheStyleSheet : function(stylesheet){
13897        if(!rules){
13898            rules = {};
13899        }
13900        try{// try catch for cross domain access issue
13901            var ssRules = stylesheet.cssRules || stylesheet.rules;
13902            for(var j = ssRules.length-1; j >= 0; --j){
13903                rules[ssRules[j].selectorText] = ssRules[j];
13904            }
13905        }catch(e){}
13906    },
13907    
13908    /**
13909     * Gets all css rules for the document
13910     * @param {Boolean} refreshCache true to refresh the internal cache
13911     * @return {Object} An object (hash) of rules indexed by selector
13912     */
13913    getRules : function(refreshCache){
13914                 if(rules == null || refreshCache){
13915                         rules = {};
13916                         var ds = doc.styleSheets;
13917                         for(var i =0, len = ds.length; i < len; i++){
13918                             try{
13919                         this.cacheStyleSheet(ds[i]);
13920                     }catch(e){} 
13921                 }
13922                 }
13923                 return rules;
13924         },
13925         
13926         /**
13927     * Gets an an individual CSS rule by selector(s)
13928     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13929     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13930     * @return {CSSRule} The CSS rule or null if one is not found
13931     */
13932    getRule : function(selector, refreshCache){
13933                 var rs = this.getRules(refreshCache);
13934                 if(!(selector instanceof Array)){
13935                     return rs[selector];
13936                 }
13937                 for(var i = 0; i < selector.length; i++){
13938                         if(rs[selector[i]]){
13939                                 return rs[selector[i]];
13940                         }
13941                 }
13942                 return null;
13943         },
13944         
13945         
13946         /**
13947     * Updates a rule property
13948     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13949     * @param {String} property The css property
13950     * @param {String} value The new value for the property
13951     * @return {Boolean} true If a rule was found and updated
13952     */
13953    updateRule : function(selector, property, value){
13954                 if(!(selector instanceof Array)){
13955                         var rule = this.getRule(selector);
13956                         if(rule){
13957                                 rule.style[property.replace(camelRe, camelFn)] = value;
13958                                 return true;
13959                         }
13960                 }else{
13961                         for(var i = 0; i < selector.length; i++){
13962                                 if(this.updateRule(selector[i], property, value)){
13963                                         return true;
13964                                 }
13965                         }
13966                 }
13967                 return false;
13968         }
13969    };   
13970 }();/*
13971  * Based on:
13972  * Ext JS Library 1.1.1
13973  * Copyright(c) 2006-2007, Ext JS, LLC.
13974  *
13975  * Originally Released Under LGPL - original licence link has changed is not relivant.
13976  *
13977  * Fork - LGPL
13978  * <script type="text/javascript">
13979  */
13980
13981  
13982
13983 /**
13984  * @class Roo.util.ClickRepeater
13985  * @extends Roo.util.Observable
13986  * 
13987  * A wrapper class which can be applied to any element. Fires a "click" event while the
13988  * mouse is pressed. The interval between firings may be specified in the config but
13989  * defaults to 10 milliseconds.
13990  * 
13991  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13992  * 
13993  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13994  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13995  * Similar to an autorepeat key delay.
13996  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13997  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13998  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13999  *           "interval" and "delay" are ignored. "immediate" is honored.
14000  * @cfg {Boolean} preventDefault True to prevent the default click event
14001  * @cfg {Boolean} stopDefault True to stop the default click event
14002  * 
14003  * @history
14004  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14005  *     2007-02-02 jvs Renamed to ClickRepeater
14006  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14007  *
14008  *  @constructor
14009  * @param {String/HTMLElement/Element} el The element to listen on
14010  * @param {Object} config
14011  **/
14012 Roo.util.ClickRepeater = function(el, config)
14013 {
14014     this.el = Roo.get(el);
14015     this.el.unselectable();
14016
14017     Roo.apply(this, config);
14018
14019     this.addEvents({
14020     /**
14021      * @event mousedown
14022      * Fires when the mouse button is depressed.
14023      * @param {Roo.util.ClickRepeater} this
14024      */
14025         "mousedown" : true,
14026     /**
14027      * @event click
14028      * Fires on a specified interval during the time the element is pressed.
14029      * @param {Roo.util.ClickRepeater} this
14030      */
14031         "click" : true,
14032     /**
14033      * @event mouseup
14034      * Fires when the mouse key is released.
14035      * @param {Roo.util.ClickRepeater} this
14036      */
14037         "mouseup" : true
14038     });
14039
14040     this.el.on("mousedown", this.handleMouseDown, this);
14041     if(this.preventDefault || this.stopDefault){
14042         this.el.on("click", function(e){
14043             if(this.preventDefault){
14044                 e.preventDefault();
14045             }
14046             if(this.stopDefault){
14047                 e.stopEvent();
14048             }
14049         }, this);
14050     }
14051
14052     // allow inline handler
14053     if(this.handler){
14054         this.on("click", this.handler,  this.scope || this);
14055     }
14056
14057     Roo.util.ClickRepeater.superclass.constructor.call(this);
14058 };
14059
14060 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14061     interval : 20,
14062     delay: 250,
14063     preventDefault : true,
14064     stopDefault : false,
14065     timer : 0,
14066
14067     // private
14068     handleMouseDown : function(){
14069         clearTimeout(this.timer);
14070         this.el.blur();
14071         if(this.pressClass){
14072             this.el.addClass(this.pressClass);
14073         }
14074         this.mousedownTime = new Date();
14075
14076         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14077         this.el.on("mouseout", this.handleMouseOut, this);
14078
14079         this.fireEvent("mousedown", this);
14080         this.fireEvent("click", this);
14081         
14082         this.timer = this.click.defer(this.delay || this.interval, this);
14083     },
14084
14085     // private
14086     click : function(){
14087         this.fireEvent("click", this);
14088         this.timer = this.click.defer(this.getInterval(), this);
14089     },
14090
14091     // private
14092     getInterval: function(){
14093         if(!this.accelerate){
14094             return this.interval;
14095         }
14096         var pressTime = this.mousedownTime.getElapsed();
14097         if(pressTime < 500){
14098             return 400;
14099         }else if(pressTime < 1700){
14100             return 320;
14101         }else if(pressTime < 2600){
14102             return 250;
14103         }else if(pressTime < 3500){
14104             return 180;
14105         }else if(pressTime < 4400){
14106             return 140;
14107         }else if(pressTime < 5300){
14108             return 80;
14109         }else if(pressTime < 6200){
14110             return 50;
14111         }else{
14112             return 10;
14113         }
14114     },
14115
14116     // private
14117     handleMouseOut : function(){
14118         clearTimeout(this.timer);
14119         if(this.pressClass){
14120             this.el.removeClass(this.pressClass);
14121         }
14122         this.el.on("mouseover", this.handleMouseReturn, this);
14123     },
14124
14125     // private
14126     handleMouseReturn : function(){
14127         this.el.un("mouseover", this.handleMouseReturn);
14128         if(this.pressClass){
14129             this.el.addClass(this.pressClass);
14130         }
14131         this.click();
14132     },
14133
14134     // private
14135     handleMouseUp : function(){
14136         clearTimeout(this.timer);
14137         this.el.un("mouseover", this.handleMouseReturn);
14138         this.el.un("mouseout", this.handleMouseOut);
14139         Roo.get(document).un("mouseup", this.handleMouseUp);
14140         this.el.removeClass(this.pressClass);
14141         this.fireEvent("mouseup", this);
14142     }
14143 });/*
14144  * Based on:
14145  * Ext JS Library 1.1.1
14146  * Copyright(c) 2006-2007, Ext JS, LLC.
14147  *
14148  * Originally Released Under LGPL - original licence link has changed is not relivant.
14149  *
14150  * Fork - LGPL
14151  * <script type="text/javascript">
14152  */
14153
14154  
14155 /**
14156  * @class Roo.KeyNav
14157  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14158  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14159  * way to implement custom navigation schemes for any UI component.</p>
14160  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14161  * pageUp, pageDown, del, home, end.  Usage:</p>
14162  <pre><code>
14163 var nav = new Roo.KeyNav("my-element", {
14164     "left" : function(e){
14165         this.moveLeft(e.ctrlKey);
14166     },
14167     "right" : function(e){
14168         this.moveRight(e.ctrlKey);
14169     },
14170     "enter" : function(e){
14171         this.save();
14172     },
14173     scope : this
14174 });
14175 </code></pre>
14176  * @constructor
14177  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14178  * @param {Object} config The config
14179  */
14180 Roo.KeyNav = function(el, config){
14181     this.el = Roo.get(el);
14182     Roo.apply(this, config);
14183     if(!this.disabled){
14184         this.disabled = true;
14185         this.enable();
14186     }
14187 };
14188
14189 Roo.KeyNav.prototype = {
14190     /**
14191      * @cfg {Boolean} disabled
14192      * True to disable this KeyNav instance (defaults to false)
14193      */
14194     disabled : false,
14195     /**
14196      * @cfg {String} defaultEventAction
14197      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14198      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14199      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14200      */
14201     defaultEventAction: "stopEvent",
14202     /**
14203      * @cfg {Boolean} forceKeyDown
14204      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14205      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14206      * handle keydown instead of keypress.
14207      */
14208     forceKeyDown : false,
14209
14210     // private
14211     prepareEvent : function(e){
14212         var k = e.getKey();
14213         var h = this.keyToHandler[k];
14214         //if(h && this[h]){
14215         //    e.stopPropagation();
14216         //}
14217         if(Roo.isSafari && h && k >= 37 && k <= 40){
14218             e.stopEvent();
14219         }
14220     },
14221
14222     // private
14223     relay : function(e){
14224         var k = e.getKey();
14225         var h = this.keyToHandler[k];
14226         if(h && this[h]){
14227             if(this.doRelay(e, this[h], h) !== true){
14228                 e[this.defaultEventAction]();
14229             }
14230         }
14231     },
14232
14233     // private
14234     doRelay : function(e, h, hname){
14235         return h.call(this.scope || this, e);
14236     },
14237
14238     // possible handlers
14239     enter : false,
14240     left : false,
14241     right : false,
14242     up : false,
14243     down : false,
14244     tab : false,
14245     esc : false,
14246     pageUp : false,
14247     pageDown : false,
14248     del : false,
14249     home : false,
14250     end : false,
14251
14252     // quick lookup hash
14253     keyToHandler : {
14254         37 : "left",
14255         39 : "right",
14256         38 : "up",
14257         40 : "down",
14258         33 : "pageUp",
14259         34 : "pageDown",
14260         46 : "del",
14261         36 : "home",
14262         35 : "end",
14263         13 : "enter",
14264         27 : "esc",
14265         9  : "tab"
14266     },
14267
14268         /**
14269          * Enable this KeyNav
14270          */
14271         enable: function(){
14272                 if(this.disabled){
14273             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14274             // the EventObject will normalize Safari automatically
14275             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14276                 this.el.on("keydown", this.relay,  this);
14277             }else{
14278                 this.el.on("keydown", this.prepareEvent,  this);
14279                 this.el.on("keypress", this.relay,  this);
14280             }
14281                     this.disabled = false;
14282                 }
14283         },
14284
14285         /**
14286          * Disable this KeyNav
14287          */
14288         disable: function(){
14289                 if(!this.disabled){
14290                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14291                 this.el.un("keydown", this.relay);
14292             }else{
14293                 this.el.un("keydown", this.prepareEvent);
14294                 this.el.un("keypress", this.relay);
14295             }
14296                     this.disabled = true;
14297                 }
14298         }
14299 };/*
14300  * Based on:
14301  * Ext JS Library 1.1.1
14302  * Copyright(c) 2006-2007, Ext JS, LLC.
14303  *
14304  * Originally Released Under LGPL - original licence link has changed is not relivant.
14305  *
14306  * Fork - LGPL
14307  * <script type="text/javascript">
14308  */
14309
14310  
14311 /**
14312  * @class Roo.KeyMap
14313  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14314  * The constructor accepts the same config object as defined by {@link #addBinding}.
14315  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14316  * combination it will call the function with this signature (if the match is a multi-key
14317  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14318  * A KeyMap can also handle a string representation of keys.<br />
14319  * Usage:
14320  <pre><code>
14321 // map one key by key code
14322 var map = new Roo.KeyMap("my-element", {
14323     key: 13, // or Roo.EventObject.ENTER
14324     fn: myHandler,
14325     scope: myObject
14326 });
14327
14328 // map multiple keys to one action by string
14329 var map = new Roo.KeyMap("my-element", {
14330     key: "a\r\n\t",
14331     fn: myHandler,
14332     scope: myObject
14333 });
14334
14335 // map multiple keys to multiple actions by strings and array of codes
14336 var map = new Roo.KeyMap("my-element", [
14337     {
14338         key: [10,13],
14339         fn: function(){ alert("Return was pressed"); }
14340     }, {
14341         key: "abc",
14342         fn: function(){ alert('a, b or c was pressed'); }
14343     }, {
14344         key: "\t",
14345         ctrl:true,
14346         shift:true,
14347         fn: function(){ alert('Control + shift + tab was pressed.'); }
14348     }
14349 ]);
14350 </code></pre>
14351  * <b>Note: A KeyMap starts enabled</b>
14352  * @constructor
14353  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14354  * @param {Object} config The config (see {@link #addBinding})
14355  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14356  */
14357 Roo.KeyMap = function(el, config, eventName){
14358     this.el  = Roo.get(el);
14359     this.eventName = eventName || "keydown";
14360     this.bindings = [];
14361     if(config){
14362         this.addBinding(config);
14363     }
14364     this.enable();
14365 };
14366
14367 Roo.KeyMap.prototype = {
14368     /**
14369      * True to stop the event from bubbling and prevent the default browser action if the
14370      * key was handled by the KeyMap (defaults to false)
14371      * @type Boolean
14372      */
14373     stopEvent : false,
14374
14375     /**
14376      * Add a new binding to this KeyMap. The following config object properties are supported:
14377      * <pre>
14378 Property    Type             Description
14379 ----------  ---------------  ----------------------------------------------------------------------
14380 key         String/Array     A single keycode or an array of keycodes to handle
14381 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14382 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14383 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14384 fn          Function         The function to call when KeyMap finds the expected key combination
14385 scope       Object           The scope of the callback function
14386 </pre>
14387      *
14388      * Usage:
14389      * <pre><code>
14390 // Create a KeyMap
14391 var map = new Roo.KeyMap(document, {
14392     key: Roo.EventObject.ENTER,
14393     fn: handleKey,
14394     scope: this
14395 });
14396
14397 //Add a new binding to the existing KeyMap later
14398 map.addBinding({
14399     key: 'abc',
14400     shift: true,
14401     fn: handleKey,
14402     scope: this
14403 });
14404 </code></pre>
14405      * @param {Object/Array} config A single KeyMap config or an array of configs
14406      */
14407         addBinding : function(config){
14408         if(config instanceof Array){
14409             for(var i = 0, len = config.length; i < len; i++){
14410                 this.addBinding(config[i]);
14411             }
14412             return;
14413         }
14414         var keyCode = config.key,
14415             shift = config.shift, 
14416             ctrl = config.ctrl, 
14417             alt = config.alt,
14418             fn = config.fn,
14419             scope = config.scope;
14420         if(typeof keyCode == "string"){
14421             var ks = [];
14422             var keyString = keyCode.toUpperCase();
14423             for(var j = 0, len = keyString.length; j < len; j++){
14424                 ks.push(keyString.charCodeAt(j));
14425             }
14426             keyCode = ks;
14427         }
14428         var keyArray = keyCode instanceof Array;
14429         var handler = function(e){
14430             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14431                 var k = e.getKey();
14432                 if(keyArray){
14433                     for(var i = 0, len = keyCode.length; i < len; i++){
14434                         if(keyCode[i] == k){
14435                           if(this.stopEvent){
14436                               e.stopEvent();
14437                           }
14438                           fn.call(scope || window, k, e);
14439                           return;
14440                         }
14441                     }
14442                 }else{
14443                     if(k == keyCode){
14444                         if(this.stopEvent){
14445                            e.stopEvent();
14446                         }
14447                         fn.call(scope || window, k, e);
14448                     }
14449                 }
14450             }
14451         };
14452         this.bindings.push(handler);  
14453         },
14454
14455     /**
14456      * Shorthand for adding a single key listener
14457      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14458      * following options:
14459      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14460      * @param {Function} fn The function to call
14461      * @param {Object} scope (optional) The scope of the function
14462      */
14463     on : function(key, fn, scope){
14464         var keyCode, shift, ctrl, alt;
14465         if(typeof key == "object" && !(key instanceof Array)){
14466             keyCode = key.key;
14467             shift = key.shift;
14468             ctrl = key.ctrl;
14469             alt = key.alt;
14470         }else{
14471             keyCode = key;
14472         }
14473         this.addBinding({
14474             key: keyCode,
14475             shift: shift,
14476             ctrl: ctrl,
14477             alt: alt,
14478             fn: fn,
14479             scope: scope
14480         })
14481     },
14482
14483     // private
14484     handleKeyDown : function(e){
14485             if(this.enabled){ //just in case
14486             var b = this.bindings;
14487             for(var i = 0, len = b.length; i < len; i++){
14488                 b[i].call(this, e);
14489             }
14490             }
14491         },
14492         
14493         /**
14494          * Returns true if this KeyMap is enabled
14495          * @return {Boolean} 
14496          */
14497         isEnabled : function(){
14498             return this.enabled;  
14499         },
14500         
14501         /**
14502          * Enables this KeyMap
14503          */
14504         enable: function(){
14505                 if(!this.enabled){
14506                     this.el.on(this.eventName, this.handleKeyDown, this);
14507                     this.enabled = true;
14508                 }
14509         },
14510
14511         /**
14512          * Disable this KeyMap
14513          */
14514         disable: function(){
14515                 if(this.enabled){
14516                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14517                     this.enabled = false;
14518                 }
14519         }
14520 };/*
14521  * Based on:
14522  * Ext JS Library 1.1.1
14523  * Copyright(c) 2006-2007, Ext JS, LLC.
14524  *
14525  * Originally Released Under LGPL - original licence link has changed is not relivant.
14526  *
14527  * Fork - LGPL
14528  * <script type="text/javascript">
14529  */
14530
14531  
14532 /**
14533  * @class Roo.util.TextMetrics
14534  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14535  * wide, in pixels, a given block of text will be.
14536  * @singleton
14537  */
14538 Roo.util.TextMetrics = function(){
14539     var shared;
14540     return {
14541         /**
14542          * Measures the size of the specified text
14543          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14544          * that can affect the size of the rendered text
14545          * @param {String} text The text to measure
14546          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14547          * in order to accurately measure the text height
14548          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14549          */
14550         measure : function(el, text, fixedWidth){
14551             if(!shared){
14552                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14553             }
14554             shared.bind(el);
14555             shared.setFixedWidth(fixedWidth || 'auto');
14556             return shared.getSize(text);
14557         },
14558
14559         /**
14560          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14561          * the overhead of multiple calls to initialize the style properties on each measurement.
14562          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14563          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14564          * in order to accurately measure the text height
14565          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14566          */
14567         createInstance : function(el, fixedWidth){
14568             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14569         }
14570     };
14571 }();
14572
14573  
14574
14575 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14576     var ml = new Roo.Element(document.createElement('div'));
14577     document.body.appendChild(ml.dom);
14578     ml.position('absolute');
14579     ml.setLeftTop(-1000, -1000);
14580     ml.hide();
14581
14582     if(fixedWidth){
14583         ml.setWidth(fixedWidth);
14584     }
14585      
14586     var instance = {
14587         /**
14588          * Returns the size of the specified text based on the internal element's style and width properties
14589          * @memberOf Roo.util.TextMetrics.Instance#
14590          * @param {String} text The text to measure
14591          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14592          */
14593         getSize : function(text){
14594             ml.update(text);
14595             var s = ml.getSize();
14596             ml.update('');
14597             return s;
14598         },
14599
14600         /**
14601          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14602          * that can affect the size of the rendered text
14603          * @memberOf Roo.util.TextMetrics.Instance#
14604          * @param {String/HTMLElement} el The element, dom node or id
14605          */
14606         bind : function(el){
14607             ml.setStyle(
14608                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14609             );
14610         },
14611
14612         /**
14613          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14614          * to set a fixed width in order to accurately measure the text height.
14615          * @memberOf Roo.util.TextMetrics.Instance#
14616          * @param {Number} width The width to set on the element
14617          */
14618         setFixedWidth : function(width){
14619             ml.setWidth(width);
14620         },
14621
14622         /**
14623          * Returns the measured width of the specified text
14624          * @memberOf Roo.util.TextMetrics.Instance#
14625          * @param {String} text The text to measure
14626          * @return {Number} width The width in pixels
14627          */
14628         getWidth : function(text){
14629             ml.dom.style.width = 'auto';
14630             return this.getSize(text).width;
14631         },
14632
14633         /**
14634          * Returns the measured height of the specified text.  For multiline text, be sure to call
14635          * {@link #setFixedWidth} if necessary.
14636          * @memberOf Roo.util.TextMetrics.Instance#
14637          * @param {String} text The text to measure
14638          * @return {Number} height The height in pixels
14639          */
14640         getHeight : function(text){
14641             return this.getSize(text).height;
14642         }
14643     };
14644
14645     instance.bind(bindTo);
14646
14647     return instance;
14648 };
14649
14650 // backwards compat
14651 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14652  * Based on:
14653  * Ext JS Library 1.1.1
14654  * Copyright(c) 2006-2007, Ext JS, LLC.
14655  *
14656  * Originally Released Under LGPL - original licence link has changed is not relivant.
14657  *
14658  * Fork - LGPL
14659  * <script type="text/javascript">
14660  */
14661
14662 /**
14663  * @class Roo.state.Provider
14664  * Abstract base class for state provider implementations. This class provides methods
14665  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14666  * Provider interface.
14667  */
14668 Roo.state.Provider = function(){
14669     /**
14670      * @event statechange
14671      * Fires when a state change occurs.
14672      * @param {Provider} this This state provider
14673      * @param {String} key The state key which was changed
14674      * @param {String} value The encoded value for the state
14675      */
14676     this.addEvents({
14677         "statechange": true
14678     });
14679     this.state = {};
14680     Roo.state.Provider.superclass.constructor.call(this);
14681 };
14682 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14683     /**
14684      * Returns the current value for a key
14685      * @param {String} name The key name
14686      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14687      * @return {Mixed} The state data
14688      */
14689     get : function(name, defaultValue){
14690         return typeof this.state[name] == "undefined" ?
14691             defaultValue : this.state[name];
14692     },
14693     
14694     /**
14695      * Clears a value from the state
14696      * @param {String} name The key name
14697      */
14698     clear : function(name){
14699         delete this.state[name];
14700         this.fireEvent("statechange", this, name, null);
14701     },
14702     
14703     /**
14704      * Sets the value for a key
14705      * @param {String} name The key name
14706      * @param {Mixed} value The value to set
14707      */
14708     set : function(name, value){
14709         this.state[name] = value;
14710         this.fireEvent("statechange", this, name, value);
14711     },
14712     
14713     /**
14714      * Decodes a string previously encoded with {@link #encodeValue}.
14715      * @param {String} value The value to decode
14716      * @return {Mixed} The decoded value
14717      */
14718     decodeValue : function(cookie){
14719         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14720         var matches = re.exec(unescape(cookie));
14721         if(!matches || !matches[1]) return; // non state cookie
14722         var type = matches[1];
14723         var v = matches[2];
14724         switch(type){
14725             case "n":
14726                 return parseFloat(v);
14727             case "d":
14728                 return new Date(Date.parse(v));
14729             case "b":
14730                 return (v == "1");
14731             case "a":
14732                 var all = [];
14733                 var values = v.split("^");
14734                 for(var i = 0, len = values.length; i < len; i++){
14735                     all.push(this.decodeValue(values[i]));
14736                 }
14737                 return all;
14738            case "o":
14739                 var all = {};
14740                 var values = v.split("^");
14741                 for(var i = 0, len = values.length; i < len; i++){
14742                     var kv = values[i].split("=");
14743                     all[kv[0]] = this.decodeValue(kv[1]);
14744                 }
14745                 return all;
14746            default:
14747                 return v;
14748         }
14749     },
14750     
14751     /**
14752      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14753      * @param {Mixed} value The value to encode
14754      * @return {String} The encoded value
14755      */
14756     encodeValue : function(v){
14757         var enc;
14758         if(typeof v == "number"){
14759             enc = "n:" + v;
14760         }else if(typeof v == "boolean"){
14761             enc = "b:" + (v ? "1" : "0");
14762         }else if(v instanceof Date){
14763             enc = "d:" + v.toGMTString();
14764         }else if(v instanceof Array){
14765             var flat = "";
14766             for(var i = 0, len = v.length; i < len; i++){
14767                 flat += this.encodeValue(v[i]);
14768                 if(i != len-1) flat += "^";
14769             }
14770             enc = "a:" + flat;
14771         }else if(typeof v == "object"){
14772             var flat = "";
14773             for(var key in v){
14774                 if(typeof v[key] != "function"){
14775                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14776                 }
14777             }
14778             enc = "o:" + flat.substring(0, flat.length-1);
14779         }else{
14780             enc = "s:" + v;
14781         }
14782         return escape(enc);        
14783     }
14784 });
14785
14786 /*
14787  * Based on:
14788  * Ext JS Library 1.1.1
14789  * Copyright(c) 2006-2007, Ext JS, LLC.
14790  *
14791  * Originally Released Under LGPL - original licence link has changed is not relivant.
14792  *
14793  * Fork - LGPL
14794  * <script type="text/javascript">
14795  */
14796 /**
14797  * @class Roo.state.Manager
14798  * This is the global state manager. By default all components that are "state aware" check this class
14799  * for state information if you don't pass them a custom state provider. In order for this class
14800  * to be useful, it must be initialized with a provider when your application initializes.
14801  <pre><code>
14802 // in your initialization function
14803 init : function(){
14804    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14805    ...
14806    // supposed you have a {@link Roo.BorderLayout}
14807    var layout = new Roo.BorderLayout(...);
14808    layout.restoreState();
14809    // or a {Roo.BasicDialog}
14810    var dialog = new Roo.BasicDialog(...);
14811    dialog.restoreState();
14812  </code></pre>
14813  * @singleton
14814  */
14815 Roo.state.Manager = function(){
14816     var provider = new Roo.state.Provider();
14817     
14818     return {
14819         /**
14820          * Configures the default state provider for your application
14821          * @param {Provider} stateProvider The state provider to set
14822          */
14823         setProvider : function(stateProvider){
14824             provider = stateProvider;
14825         },
14826         
14827         /**
14828          * Returns the current value for a key
14829          * @param {String} name The key name
14830          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14831          * @return {Mixed} The state data
14832          */
14833         get : function(key, defaultValue){
14834             return provider.get(key, defaultValue);
14835         },
14836         
14837         /**
14838          * Sets the value for a key
14839          * @param {String} name The key name
14840          * @param {Mixed} value The state data
14841          */
14842          set : function(key, value){
14843             provider.set(key, value);
14844         },
14845         
14846         /**
14847          * Clears a value from the state
14848          * @param {String} name The key name
14849          */
14850         clear : function(key){
14851             provider.clear(key);
14852         },
14853         
14854         /**
14855          * Gets the currently configured state provider
14856          * @return {Provider} The state provider
14857          */
14858         getProvider : function(){
14859             return provider;
14860         }
14861     };
14862 }();
14863 /*
14864  * Based on:
14865  * Ext JS Library 1.1.1
14866  * Copyright(c) 2006-2007, Ext JS, LLC.
14867  *
14868  * Originally Released Under LGPL - original licence link has changed is not relivant.
14869  *
14870  * Fork - LGPL
14871  * <script type="text/javascript">
14872  */
14873 /**
14874  * @class Roo.state.CookieProvider
14875  * @extends Roo.state.Provider
14876  * The default Provider implementation which saves state via cookies.
14877  * <br />Usage:
14878  <pre><code>
14879    var cp = new Roo.state.CookieProvider({
14880        path: "/cgi-bin/",
14881        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14882        domain: "roojs.com"
14883    })
14884    Roo.state.Manager.setProvider(cp);
14885  </code></pre>
14886  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14887  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14888  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14889  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14890  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14891  * domain the page is running on including the 'www' like 'www.roojs.com')
14892  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14893  * @constructor
14894  * Create a new CookieProvider
14895  * @param {Object} config The configuration object
14896  */
14897 Roo.state.CookieProvider = function(config){
14898     Roo.state.CookieProvider.superclass.constructor.call(this);
14899     this.path = "/";
14900     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14901     this.domain = null;
14902     this.secure = false;
14903     Roo.apply(this, config);
14904     this.state = this.readCookies();
14905 };
14906
14907 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14908     // private
14909     set : function(name, value){
14910         if(typeof value == "undefined" || value === null){
14911             this.clear(name);
14912             return;
14913         }
14914         this.setCookie(name, value);
14915         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14916     },
14917
14918     // private
14919     clear : function(name){
14920         this.clearCookie(name);
14921         Roo.state.CookieProvider.superclass.clear.call(this, name);
14922     },
14923
14924     // private
14925     readCookies : function(){
14926         var cookies = {};
14927         var c = document.cookie + ";";
14928         var re = /\s?(.*?)=(.*?);/g;
14929         var matches;
14930         while((matches = re.exec(c)) != null){
14931             var name = matches[1];
14932             var value = matches[2];
14933             if(name && name.substring(0,3) == "ys-"){
14934                 cookies[name.substr(3)] = this.decodeValue(value);
14935             }
14936         }
14937         return cookies;
14938     },
14939
14940     // private
14941     setCookie : function(name, value){
14942         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14943            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14944            ((this.path == null) ? "" : ("; path=" + this.path)) +
14945            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14946            ((this.secure == true) ? "; secure" : "");
14947     },
14948
14949     // private
14950     clearCookie : function(name){
14951         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14952            ((this.path == null) ? "" : ("; path=" + this.path)) +
14953            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14954            ((this.secure == true) ? "; secure" : "");
14955     }
14956 });/*
14957  * Based on:
14958  * Ext JS Library 1.1.1
14959  * Copyright(c) 2006-2007, Ext JS, LLC.
14960  *
14961  * Originally Released Under LGPL - original licence link has changed is not relivant.
14962  *
14963  * Fork - LGPL
14964  * <script type="text/javascript">
14965  */
14966
14967
14968
14969 /*
14970  * These classes are derivatives of the similarly named classes in the YUI Library.
14971  * The original license:
14972  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14973  * Code licensed under the BSD License:
14974  * http://developer.yahoo.net/yui/license.txt
14975  */
14976
14977 (function() {
14978
14979 var Event=Roo.EventManager;
14980 var Dom=Roo.lib.Dom;
14981
14982 /**
14983  * @class Roo.dd.DragDrop
14984  * @extends Roo.util.Observable
14985  * Defines the interface and base operation of items that that can be
14986  * dragged or can be drop targets.  It was designed to be extended, overriding
14987  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14988  * Up to three html elements can be associated with a DragDrop instance:
14989  * <ul>
14990  * <li>linked element: the element that is passed into the constructor.
14991  * This is the element which defines the boundaries for interaction with
14992  * other DragDrop objects.</li>
14993  * <li>handle element(s): The drag operation only occurs if the element that
14994  * was clicked matches a handle element.  By default this is the linked
14995  * element, but there are times that you will want only a portion of the
14996  * linked element to initiate the drag operation, and the setHandleElId()
14997  * method provides a way to define this.</li>
14998  * <li>drag element: this represents the element that would be moved along
14999  * with the cursor during a drag operation.  By default, this is the linked
15000  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
15001  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
15002  * </li>
15003  * </ul>
15004  * This class should not be instantiated until the onload event to ensure that
15005  * the associated elements are available.
15006  * The following would define a DragDrop obj that would interact with any
15007  * other DragDrop obj in the "group1" group:
15008  * <pre>
15009  *  dd = new Roo.dd.DragDrop("div1", "group1");
15010  * </pre>
15011  * Since none of the event handlers have been implemented, nothing would
15012  * actually happen if you were to run the code above.  Normally you would
15013  * override this class or one of the default implementations, but you can
15014  * also override the methods you want on an instance of the class...
15015  * <pre>
15016  *  dd.onDragDrop = function(e, id) {
15017  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
15018  *  }
15019  * </pre>
15020  * @constructor
15021  * @param {String} id of the element that is linked to this instance
15022  * @param {String} sGroup the group of related DragDrop objects
15023  * @param {object} config an object containing configurable attributes
15024  *                Valid properties for DragDrop:
15025  *                    padding, isTarget, maintainOffset, primaryButtonOnly
15026  */
15027 Roo.dd.DragDrop = function(id, sGroup, config) {
15028     if (id) {
15029         this.init(id, sGroup, config);
15030     }
15031     
15032 };
15033
15034 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
15035
15036     /**
15037      * The id of the element associated with this object.  This is what we
15038      * refer to as the "linked element" because the size and position of
15039      * this element is used to determine when the drag and drop objects have
15040      * interacted.
15041      * @property id
15042      * @type String
15043      */
15044     id: null,
15045
15046     /**
15047      * Configuration attributes passed into the constructor
15048      * @property config
15049      * @type object
15050      */
15051     config: null,
15052
15053     /**
15054      * The id of the element that will be dragged.  By default this is same
15055      * as the linked element , but could be changed to another element. Ex:
15056      * Roo.dd.DDProxy
15057      * @property dragElId
15058      * @type String
15059      * @private
15060      */
15061     dragElId: null,
15062
15063     /**
15064      * the id of the element that initiates the drag operation.  By default
15065      * this is the linked element, but could be changed to be a child of this
15066      * element.  This lets us do things like only starting the drag when the
15067      * header element within the linked html element is clicked.
15068      * @property handleElId
15069      * @type String
15070      * @private
15071      */
15072     handleElId: null,
15073
15074     /**
15075      * An associative array of HTML tags that will be ignored if clicked.
15076      * @property invalidHandleTypes
15077      * @type {string: string}
15078      */
15079     invalidHandleTypes: null,
15080
15081     /**
15082      * An associative array of ids for elements that will be ignored if clicked
15083      * @property invalidHandleIds
15084      * @type {string: string}
15085      */
15086     invalidHandleIds: null,
15087
15088     /**
15089      * An indexted array of css class names for elements that will be ignored
15090      * if clicked.
15091      * @property invalidHandleClasses
15092      * @type string[]
15093      */
15094     invalidHandleClasses: null,
15095
15096     /**
15097      * The linked element's absolute X position at the time the drag was
15098      * started
15099      * @property startPageX
15100      * @type int
15101      * @private
15102      */
15103     startPageX: 0,
15104
15105     /**
15106      * The linked element's absolute X position at the time the drag was
15107      * started
15108      * @property startPageY
15109      * @type int
15110      * @private
15111      */
15112     startPageY: 0,
15113
15114     /**
15115      * The group defines a logical collection of DragDrop objects that are
15116      * related.  Instances only get events when interacting with other
15117      * DragDrop object in the same group.  This lets us define multiple
15118      * groups using a single DragDrop subclass if we want.
15119      * @property groups
15120      * @type {string: string}
15121      */
15122     groups: null,
15123
15124     /**
15125      * Individual drag/drop instances can be locked.  This will prevent
15126      * onmousedown start drag.
15127      * @property locked
15128      * @type boolean
15129      * @private
15130      */
15131     locked: false,
15132
15133     /**
15134      * Lock this instance
15135      * @method lock
15136      */
15137     lock: function() { this.locked = true; },
15138
15139     /**
15140      * Unlock this instace
15141      * @method unlock
15142      */
15143     unlock: function() { this.locked = false; },
15144
15145     /**
15146      * By default, all insances can be a drop target.  This can be disabled by
15147      * setting isTarget to false.
15148      * @method isTarget
15149      * @type boolean
15150      */
15151     isTarget: true,
15152
15153     /**
15154      * The padding configured for this drag and drop object for calculating
15155      * the drop zone intersection with this object.
15156      * @method padding
15157      * @type int[]
15158      */
15159     padding: null,
15160
15161     /**
15162      * Cached reference to the linked element
15163      * @property _domRef
15164      * @private
15165      */
15166     _domRef: null,
15167
15168     /**
15169      * Internal typeof flag
15170      * @property __ygDragDrop
15171      * @private
15172      */
15173     __ygDragDrop: true,
15174
15175     /**
15176      * Set to true when horizontal contraints are applied
15177      * @property constrainX
15178      * @type boolean
15179      * @private
15180      */
15181     constrainX: false,
15182
15183     /**
15184      * Set to true when vertical contraints are applied
15185      * @property constrainY
15186      * @type boolean
15187      * @private
15188      */
15189     constrainY: false,
15190
15191     /**
15192      * The left constraint
15193      * @property minX
15194      * @type int
15195      * @private
15196      */
15197     minX: 0,
15198
15199     /**
15200      * The right constraint
15201      * @property maxX
15202      * @type int
15203      * @private
15204      */
15205     maxX: 0,
15206
15207     /**
15208      * The up constraint
15209      * @property minY
15210      * @type int
15211      * @type int
15212      * @private
15213      */
15214     minY: 0,
15215
15216     /**
15217      * The down constraint
15218      * @property maxY
15219      * @type int
15220      * @private
15221      */
15222     maxY: 0,
15223
15224     /**
15225      * Maintain offsets when we resetconstraints.  Set to true when you want
15226      * the position of the element relative to its parent to stay the same
15227      * when the page changes
15228      *
15229      * @property maintainOffset
15230      * @type boolean
15231      */
15232     maintainOffset: false,
15233
15234     /**
15235      * Array of pixel locations the element will snap to if we specified a
15236      * horizontal graduation/interval.  This array is generated automatically
15237      * when you define a tick interval.
15238      * @property xTicks
15239      * @type int[]
15240      */
15241     xTicks: null,
15242
15243     /**
15244      * Array of pixel locations the element will snap to if we specified a
15245      * vertical graduation/interval.  This array is generated automatically
15246      * when you define a tick interval.
15247      * @property yTicks
15248      * @type int[]
15249      */
15250     yTicks: null,
15251
15252     /**
15253      * By default the drag and drop instance will only respond to the primary
15254      * button click (left button for a right-handed mouse).  Set to true to
15255      * allow drag and drop to start with any mouse click that is propogated
15256      * by the browser
15257      * @property primaryButtonOnly
15258      * @type boolean
15259      */
15260     primaryButtonOnly: true,
15261
15262     /**
15263      * The availabe property is false until the linked dom element is accessible.
15264      * @property available
15265      * @type boolean
15266      */
15267     available: false,
15268
15269     /**
15270      * By default, drags can only be initiated if the mousedown occurs in the
15271      * region the linked element is.  This is done in part to work around a
15272      * bug in some browsers that mis-report the mousedown if the previous
15273      * mouseup happened outside of the window.  This property is set to true
15274      * if outer handles are defined.
15275      *
15276      * @property hasOuterHandles
15277      * @type boolean
15278      * @default false
15279      */
15280     hasOuterHandles: false,
15281
15282     /**
15283      * Code that executes immediately before the startDrag event
15284      * @method b4StartDrag
15285      * @private
15286      */
15287     b4StartDrag: function(x, y) { },
15288
15289     /**
15290      * Abstract method called after a drag/drop object is clicked
15291      * and the drag or mousedown time thresholds have beeen met.
15292      * @method startDrag
15293      * @param {int} X click location
15294      * @param {int} Y click location
15295      */
15296     startDrag: function(x, y) { /* override this */ },
15297
15298     /**
15299      * Code that executes immediately before the onDrag event
15300      * @method b4Drag
15301      * @private
15302      */
15303     b4Drag: function(e) { },
15304
15305     /**
15306      * Abstract method called during the onMouseMove event while dragging an
15307      * object.
15308      * @method onDrag
15309      * @param {Event} e the mousemove event
15310      */
15311     onDrag: function(e) { /* override this */ },
15312
15313     /**
15314      * Abstract method called when this element fist begins hovering over
15315      * another DragDrop obj
15316      * @method onDragEnter
15317      * @param {Event} e the mousemove event
15318      * @param {String|DragDrop[]} id In POINT mode, the element
15319      * id this is hovering over.  In INTERSECT mode, an array of one or more
15320      * dragdrop items being hovered over.
15321      */
15322     onDragEnter: function(e, id) { /* override this */ },
15323
15324     /**
15325      * Code that executes immediately before the onDragOver event
15326      * @method b4DragOver
15327      * @private
15328      */
15329     b4DragOver: function(e) { },
15330
15331     /**
15332      * Abstract method called when this element is hovering over another
15333      * DragDrop obj
15334      * @method onDragOver
15335      * @param {Event} e the mousemove event
15336      * @param {String|DragDrop[]} id In POINT mode, the element
15337      * id this is hovering over.  In INTERSECT mode, an array of dd items
15338      * being hovered over.
15339      */
15340     onDragOver: function(e, id) { /* override this */ },
15341
15342     /**
15343      * Code that executes immediately before the onDragOut event
15344      * @method b4DragOut
15345      * @private
15346      */
15347     b4DragOut: function(e) { },
15348
15349     /**
15350      * Abstract method called when we are no longer hovering over an element
15351      * @method onDragOut
15352      * @param {Event} e the mousemove event
15353      * @param {String|DragDrop[]} id In POINT mode, the element
15354      * id this was hovering over.  In INTERSECT mode, an array of dd items
15355      * that the mouse is no longer over.
15356      */
15357     onDragOut: function(e, id) { /* override this */ },
15358
15359     /**
15360      * Code that executes immediately before the onDragDrop event
15361      * @method b4DragDrop
15362      * @private
15363      */
15364     b4DragDrop: function(e) { },
15365
15366     /**
15367      * Abstract method called when this item is dropped on another DragDrop
15368      * obj
15369      * @method onDragDrop
15370      * @param {Event} e the mouseup event
15371      * @param {String|DragDrop[]} id In POINT mode, the element
15372      * id this was dropped on.  In INTERSECT mode, an array of dd items this
15373      * was dropped on.
15374      */
15375     onDragDrop: function(e, id) { /* override this */ },
15376
15377     /**
15378      * Abstract method called when this item is dropped on an area with no
15379      * drop target
15380      * @method onInvalidDrop
15381      * @param {Event} e the mouseup event
15382      */
15383     onInvalidDrop: function(e) { /* override this */ },
15384
15385     /**
15386      * Code that executes immediately before the endDrag event
15387      * @method b4EndDrag
15388      * @private
15389      */
15390     b4EndDrag: function(e) { },
15391
15392     /**
15393      * Fired when we are done dragging the object
15394      * @method endDrag
15395      * @param {Event} e the mouseup event
15396      */
15397     endDrag: function(e) { /* override this */ },
15398
15399     /**
15400      * Code executed immediately before the onMouseDown event
15401      * @method b4MouseDown
15402      * @param {Event} e the mousedown event
15403      * @private
15404      */
15405     b4MouseDown: function(e) {  },
15406
15407     /**
15408      * Event handler that fires when a drag/drop obj gets a mousedown
15409      * @method onMouseDown
15410      * @param {Event} e the mousedown event
15411      */
15412     onMouseDown: function(e) { /* override this */ },
15413
15414     /**
15415      * Event handler that fires when a drag/drop obj gets a mouseup
15416      * @method onMouseUp
15417      * @param {Event} e the mouseup event
15418      */
15419     onMouseUp: function(e) { /* override this */ },
15420
15421     /**
15422      * Override the onAvailable method to do what is needed after the initial
15423      * position was determined.
15424      * @method onAvailable
15425      */
15426     onAvailable: function () {
15427     },
15428
15429     /*
15430      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
15431      * @type Object
15432      */
15433     defaultPadding : {left:0, right:0, top:0, bottom:0},
15434
15435     /*
15436      * Initializes the drag drop object's constraints to restrict movement to a certain element.
15437  *
15438  * Usage:
15439  <pre><code>
15440  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
15441                 { dragElId: "existingProxyDiv" });
15442  dd.startDrag = function(){
15443      this.constrainTo("parent-id");
15444  };
15445  </code></pre>
15446  * Or you can initalize it using the {@link Roo.Element} object:
15447  <pre><code>
15448  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
15449      startDrag : function(){
15450          this.constrainTo("parent-id");
15451      }
15452  });
15453  </code></pre>
15454      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15455      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15456      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15457      * an object containing the sides to pad. For example: {right:10, bottom:10}
15458      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15459      */
15460     constrainTo : function(constrainTo, pad, inContent){
15461         if(typeof pad == "number"){
15462             pad = {left: pad, right:pad, top:pad, bottom:pad};
15463         }
15464         pad = pad || this.defaultPadding;
15465         var b = Roo.get(this.getEl()).getBox();
15466         var ce = Roo.get(constrainTo);
15467         var s = ce.getScroll();
15468         var c, cd = ce.dom;
15469         if(cd == document.body){
15470             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15471         }else{
15472             xy = ce.getXY();
15473             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15474         }
15475
15476
15477         var topSpace = b.y - c.y;
15478         var leftSpace = b.x - c.x;
15479
15480         this.resetConstraints();
15481         this.setXConstraint(leftSpace - (pad.left||0), // left
15482                 c.width - leftSpace - b.width - (pad.right||0) //right
15483         );
15484         this.setYConstraint(topSpace - (pad.top||0), //top
15485                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15486         );
15487     },
15488
15489     /**
15490      * Returns a reference to the linked element
15491      * @method getEl
15492      * @return {HTMLElement} the html element
15493      */
15494     getEl: function() {
15495         if (!this._domRef) {
15496             this._domRef = Roo.getDom(this.id);
15497         }
15498
15499         return this._domRef;
15500     },
15501
15502     /**
15503      * Returns a reference to the actual element to drag.  By default this is
15504      * the same as the html element, but it can be assigned to another
15505      * element. An example of this can be found in Roo.dd.DDProxy
15506      * @method getDragEl
15507      * @return {HTMLElement} the html element
15508      */
15509     getDragEl: function() {
15510         return Roo.getDom(this.dragElId);
15511     },
15512
15513     /**
15514      * Sets up the DragDrop object.  Must be called in the constructor of any
15515      * Roo.dd.DragDrop subclass
15516      * @method init
15517      * @param id the id of the linked element
15518      * @param {String} sGroup the group of related items
15519      * @param {object} config configuration attributes
15520      */
15521     init: function(id, sGroup, config) {
15522         this.initTarget(id, sGroup, config);
15523         if (!Roo.isTouch) {
15524             Event.on(this.id, "mousedown", this.handleMouseDown, this);
15525         }
15526         Event.on(this.id, "touchstart", this.handleMouseDown, this);
15527         // Event.on(this.id, "selectstart", Event.preventDefault);
15528     },
15529
15530     /**
15531      * Initializes Targeting functionality only... the object does not
15532      * get a mousedown handler.
15533      * @method initTarget
15534      * @param id the id of the linked element
15535      * @param {String} sGroup the group of related items
15536      * @param {object} config configuration attributes
15537      */
15538     initTarget: function(id, sGroup, config) {
15539
15540         // configuration attributes
15541         this.config = config || {};
15542
15543         // create a local reference to the drag and drop manager
15544         this.DDM = Roo.dd.DDM;
15545         // initialize the groups array
15546         this.groups = {};
15547
15548         // assume that we have an element reference instead of an id if the
15549         // parameter is not a string
15550         if (typeof id !== "string") {
15551             id = Roo.id(id);
15552         }
15553
15554         // set the id
15555         this.id = id;
15556
15557         // add to an interaction group
15558         this.addToGroup((sGroup) ? sGroup : "default");
15559
15560         // We don't want to register this as the handle with the manager
15561         // so we just set the id rather than calling the setter.
15562         this.handleElId = id;
15563
15564         // the linked element is the element that gets dragged by default
15565         this.setDragElId(id);
15566
15567         // by default, clicked anchors will not start drag operations.
15568         this.invalidHandleTypes = { A: "A" };
15569         this.invalidHandleIds = {};
15570         this.invalidHandleClasses = [];
15571
15572         this.applyConfig();
15573
15574         this.handleOnAvailable();
15575     },
15576
15577     /**
15578      * Applies the configuration parameters that were passed into the constructor.
15579      * This is supposed to happen at each level through the inheritance chain.  So
15580      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15581      * DragDrop in order to get all of the parameters that are available in
15582      * each object.
15583      * @method applyConfig
15584      */
15585     applyConfig: function() {
15586
15587         // configurable properties:
15588         //    padding, isTarget, maintainOffset, primaryButtonOnly
15589         this.padding           = this.config.padding || [0, 0, 0, 0];
15590         this.isTarget          = (this.config.isTarget !== false);
15591         this.maintainOffset    = (this.config.maintainOffset);
15592         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15593
15594     },
15595
15596     /**
15597      * Executed when the linked element is available
15598      * @method handleOnAvailable
15599      * @private
15600      */
15601     handleOnAvailable: function() {
15602         this.available = true;
15603         this.resetConstraints();
15604         this.onAvailable();
15605     },
15606
15607      /**
15608      * Configures the padding for the target zone in px.  Effectively expands
15609      * (or reduces) the virtual object size for targeting calculations.
15610      * Supports css-style shorthand; if only one parameter is passed, all sides
15611      * will have that padding, and if only two are passed, the top and bottom
15612      * will have the first param, the left and right the second.
15613      * @method setPadding
15614      * @param {int} iTop    Top pad
15615      * @param {int} iRight  Right pad
15616      * @param {int} iBot    Bot pad
15617      * @param {int} iLeft   Left pad
15618      */
15619     setPadding: function(iTop, iRight, iBot, iLeft) {
15620         // this.padding = [iLeft, iRight, iTop, iBot];
15621         if (!iRight && 0 !== iRight) {
15622             this.padding = [iTop, iTop, iTop, iTop];
15623         } else if (!iBot && 0 !== iBot) {
15624             this.padding = [iTop, iRight, iTop, iRight];
15625         } else {
15626             this.padding = [iTop, iRight, iBot, iLeft];
15627         }
15628     },
15629
15630     /**
15631      * Stores the initial placement of the linked element.
15632      * @method setInitialPosition
15633      * @param {int} diffX   the X offset, default 0
15634      * @param {int} diffY   the Y offset, default 0
15635      */
15636     setInitPosition: function(diffX, diffY) {
15637         var el = this.getEl();
15638
15639         if (!this.DDM.verifyEl(el)) {
15640             return;
15641         }
15642
15643         var dx = diffX || 0;
15644         var dy = diffY || 0;
15645
15646         var p = Dom.getXY( el );
15647
15648         this.initPageX = p[0] - dx;
15649         this.initPageY = p[1] - dy;
15650
15651         this.lastPageX = p[0];
15652         this.lastPageY = p[1];
15653
15654
15655         this.setStartPosition(p);
15656     },
15657
15658     /**
15659      * Sets the start position of the element.  This is set when the obj
15660      * is initialized, the reset when a drag is started.
15661      * @method setStartPosition
15662      * @param pos current position (from previous lookup)
15663      * @private
15664      */
15665     setStartPosition: function(pos) {
15666         var p = pos || Dom.getXY( this.getEl() );
15667         this.deltaSetXY = null;
15668
15669         this.startPageX = p[0];
15670         this.startPageY = p[1];
15671     },
15672
15673     /**
15674      * Add this instance to a group of related drag/drop objects.  All
15675      * instances belong to at least one group, and can belong to as many
15676      * groups as needed.
15677      * @method addToGroup
15678      * @param sGroup {string} the name of the group
15679      */
15680     addToGroup: function(sGroup) {
15681         this.groups[sGroup] = true;
15682         this.DDM.regDragDrop(this, sGroup);
15683     },
15684
15685     /**
15686      * Remove's this instance from the supplied interaction group
15687      * @method removeFromGroup
15688      * @param {string}  sGroup  The group to drop
15689      */
15690     removeFromGroup: function(sGroup) {
15691         if (this.groups[sGroup]) {
15692             delete this.groups[sGroup];
15693         }
15694
15695         this.DDM.removeDDFromGroup(this, sGroup);
15696     },
15697
15698     /**
15699      * Allows you to specify that an element other than the linked element
15700      * will be moved with the cursor during a drag
15701      * @method setDragElId
15702      * @param id {string} the id of the element that will be used to initiate the drag
15703      */
15704     setDragElId: function(id) {
15705         this.dragElId = id;
15706     },
15707
15708     /**
15709      * Allows you to specify a child of the linked element that should be
15710      * used to initiate the drag operation.  An example of this would be if
15711      * you have a content div with text and links.  Clicking anywhere in the
15712      * content area would normally start the drag operation.  Use this method
15713      * to specify that an element inside of the content div is the element
15714      * that starts the drag operation.
15715      * @method setHandleElId
15716      * @param id {string} the id of the element that will be used to
15717      * initiate the drag.
15718      */
15719     setHandleElId: function(id) {
15720         if (typeof id !== "string") {
15721             id = Roo.id(id);
15722         }
15723         this.handleElId = id;
15724         this.DDM.regHandle(this.id, id);
15725     },
15726
15727     /**
15728      * Allows you to set an element outside of the linked element as a drag
15729      * handle
15730      * @method setOuterHandleElId
15731      * @param id the id of the element that will be used to initiate the drag
15732      */
15733     setOuterHandleElId: function(id) {
15734         if (typeof id !== "string") {
15735             id = Roo.id(id);
15736         }
15737         Event.on(id, "mousedown",
15738                 this.handleMouseDown, this);
15739         this.setHandleElId(id);
15740
15741         this.hasOuterHandles = true;
15742     },
15743
15744     /**
15745      * Remove all drag and drop hooks for this element
15746      * @method unreg
15747      */
15748     unreg: function() {
15749         Event.un(this.id, "mousedown",
15750                 this.handleMouseDown);
15751         Event.un(this.id, "touchstart",
15752                 this.handleMouseDown);
15753         this._domRef = null;
15754         this.DDM._remove(this);
15755     },
15756
15757     destroy : function(){
15758         this.unreg();
15759     },
15760
15761     /**
15762      * Returns true if this instance is locked, or the drag drop mgr is locked
15763      * (meaning that all drag/drop is disabled on the page.)
15764      * @method isLocked
15765      * @return {boolean} true if this obj or all drag/drop is locked, else
15766      * false
15767      */
15768     isLocked: function() {
15769         return (this.DDM.isLocked() || this.locked);
15770     },
15771
15772     /**
15773      * Fired when this object is clicked
15774      * @method handleMouseDown
15775      * @param {Event} e
15776      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15777      * @private
15778      */
15779     handleMouseDown: function(e, oDD){
15780         Roo.log(this);
15781         Roo.log(e);
15782         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
15783             //Roo.log('not touch/ button !=0');
15784             return;
15785         }
15786         if (ev.browserEvent.touches && ev.browserEvent.touches.length != 1) {
15787             return; // double touch..
15788         }
15789         
15790
15791         if (this.isLocked()) {
15792             //Roo.log('locked');
15793             return;
15794         }
15795
15796         this.DDM.refreshCache(this.groups);
15797         Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
15798         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15799         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15800             //Roo.log('no outer handes or not over target');
15801                 // do nothing.
15802         } else {
15803             Roo.log('check validator');
15804             if (this.clickValidator(e)) {
15805                 Roo.log('validate success');
15806                 // set the initial element position
15807                 this.setStartPosition();
15808
15809
15810                 this.b4MouseDown(e);
15811                 this.onMouseDown(e);
15812
15813                 this.DDM.handleMouseDown(e, this);
15814
15815                 this.DDM.stopEvent(e);
15816             } else {
15817
15818
15819             }
15820         }
15821     },
15822
15823     clickValidator: function(e) {
15824         var target = e.getTarget();
15825         return ( this.isValidHandleChild(target) &&
15826                     (this.id == this.handleElId ||
15827                         this.DDM.handleWasClicked(target, this.id)) );
15828     },
15829
15830     /**
15831      * Allows you to specify a tag name that should not start a drag operation
15832      * when clicked.  This is designed to facilitate embedding links within a
15833      * drag handle that do something other than start the drag.
15834      * @method addInvalidHandleType
15835      * @param {string} tagName the type of element to exclude
15836      */
15837     addInvalidHandleType: function(tagName) {
15838         var type = tagName.toUpperCase();
15839         this.invalidHandleTypes[type] = type;
15840     },
15841
15842     /**
15843      * Lets you to specify an element id for a child of a drag handle
15844      * that should not initiate a drag
15845      * @method addInvalidHandleId
15846      * @param {string} id the element id of the element you wish to ignore
15847      */
15848     addInvalidHandleId: function(id) {
15849         if (typeof id !== "string") {
15850             id = Roo.id(id);
15851         }
15852         this.invalidHandleIds[id] = id;
15853     },
15854
15855     /**
15856      * Lets you specify a css class of elements that will not initiate a drag
15857      * @method addInvalidHandleClass
15858      * @param {string} cssClass the class of the elements you wish to ignore
15859      */
15860     addInvalidHandleClass: function(cssClass) {
15861         this.invalidHandleClasses.push(cssClass);
15862     },
15863
15864     /**
15865      * Unsets an excluded tag name set by addInvalidHandleType
15866      * @method removeInvalidHandleType
15867      * @param {string} tagName the type of element to unexclude
15868      */
15869     removeInvalidHandleType: function(tagName) {
15870         var type = tagName.toUpperCase();
15871         // this.invalidHandleTypes[type] = null;
15872         delete this.invalidHandleTypes[type];
15873     },
15874
15875     /**
15876      * Unsets an invalid handle id
15877      * @method removeInvalidHandleId
15878      * @param {string} id the id of the element to re-enable
15879      */
15880     removeInvalidHandleId: function(id) {
15881         if (typeof id !== "string") {
15882             id = Roo.id(id);
15883         }
15884         delete this.invalidHandleIds[id];
15885     },
15886
15887     /**
15888      * Unsets an invalid css class
15889      * @method removeInvalidHandleClass
15890      * @param {string} cssClass the class of the element(s) you wish to
15891      * re-enable
15892      */
15893     removeInvalidHandleClass: function(cssClass) {
15894         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15895             if (this.invalidHandleClasses[i] == cssClass) {
15896                 delete this.invalidHandleClasses[i];
15897             }
15898         }
15899     },
15900
15901     /**
15902      * Checks the tag exclusion list to see if this click should be ignored
15903      * @method isValidHandleChild
15904      * @param {HTMLElement} node the HTMLElement to evaluate
15905      * @return {boolean} true if this is a valid tag type, false if not
15906      */
15907     isValidHandleChild: function(node) {
15908
15909         var valid = true;
15910         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15911         var nodeName;
15912         try {
15913             nodeName = node.nodeName.toUpperCase();
15914         } catch(e) {
15915             nodeName = node.nodeName;
15916         }
15917         valid = valid && !this.invalidHandleTypes[nodeName];
15918         valid = valid && !this.invalidHandleIds[node.id];
15919
15920         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15921             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15922         }
15923
15924
15925         return valid;
15926
15927     },
15928
15929     /**
15930      * Create the array of horizontal tick marks if an interval was specified
15931      * in setXConstraint().
15932      * @method setXTicks
15933      * @private
15934      */
15935     setXTicks: function(iStartX, iTickSize) {
15936         this.xTicks = [];
15937         this.xTickSize = iTickSize;
15938
15939         var tickMap = {};
15940
15941         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15942             if (!tickMap[i]) {
15943                 this.xTicks[this.xTicks.length] = i;
15944                 tickMap[i] = true;
15945             }
15946         }
15947
15948         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15949             if (!tickMap[i]) {
15950                 this.xTicks[this.xTicks.length] = i;
15951                 tickMap[i] = true;
15952             }
15953         }
15954
15955         this.xTicks.sort(this.DDM.numericSort) ;
15956     },
15957
15958     /**
15959      * Create the array of vertical tick marks if an interval was specified in
15960      * setYConstraint().
15961      * @method setYTicks
15962      * @private
15963      */
15964     setYTicks: function(iStartY, iTickSize) {
15965         this.yTicks = [];
15966         this.yTickSize = iTickSize;
15967
15968         var tickMap = {};
15969
15970         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15971             if (!tickMap[i]) {
15972                 this.yTicks[this.yTicks.length] = i;
15973                 tickMap[i] = true;
15974             }
15975         }
15976
15977         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15978             if (!tickMap[i]) {
15979                 this.yTicks[this.yTicks.length] = i;
15980                 tickMap[i] = true;
15981             }
15982         }
15983
15984         this.yTicks.sort(this.DDM.numericSort) ;
15985     },
15986
15987     /**
15988      * By default, the element can be dragged any place on the screen.  Use
15989      * this method to limit the horizontal travel of the element.  Pass in
15990      * 0,0 for the parameters if you want to lock the drag to the y axis.
15991      * @method setXConstraint
15992      * @param {int} iLeft the number of pixels the element can move to the left
15993      * @param {int} iRight the number of pixels the element can move to the
15994      * right
15995      * @param {int} iTickSize optional parameter for specifying that the
15996      * element
15997      * should move iTickSize pixels at a time.
15998      */
15999     setXConstraint: function(iLeft, iRight, iTickSize) {
16000         this.leftConstraint = iLeft;
16001         this.rightConstraint = iRight;
16002
16003         this.minX = this.initPageX - iLeft;
16004         this.maxX = this.initPageX + iRight;
16005         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
16006
16007         this.constrainX = true;
16008     },
16009
16010     /**
16011      * Clears any constraints applied to this instance.  Also clears ticks
16012      * since they can't exist independent of a constraint at this time.
16013      * @method clearConstraints
16014      */
16015     clearConstraints: function() {
16016         this.constrainX = false;
16017         this.constrainY = false;
16018         this.clearTicks();
16019     },
16020
16021     /**
16022      * Clears any tick interval defined for this instance
16023      * @method clearTicks
16024      */
16025     clearTicks: function() {
16026         this.xTicks = null;
16027         this.yTicks = null;
16028         this.xTickSize = 0;
16029         this.yTickSize = 0;
16030     },
16031
16032     /**
16033      * By default, the element can be dragged any place on the screen.  Set
16034      * this to limit the vertical travel of the element.  Pass in 0,0 for the
16035      * parameters if you want to lock the drag to the x axis.
16036      * @method setYConstraint
16037      * @param {int} iUp the number of pixels the element can move up
16038      * @param {int} iDown the number of pixels the element can move down
16039      * @param {int} iTickSize optional parameter for specifying that the
16040      * element should move iTickSize pixels at a time.
16041      */
16042     setYConstraint: function(iUp, iDown, iTickSize) {
16043         this.topConstraint = iUp;
16044         this.bottomConstraint = iDown;
16045
16046         this.minY = this.initPageY - iUp;
16047         this.maxY = this.initPageY + iDown;
16048         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
16049
16050         this.constrainY = true;
16051
16052     },
16053
16054     /**
16055      * resetConstraints must be called if you manually reposition a dd element.
16056      * @method resetConstraints
16057      * @param {boolean} maintainOffset
16058      */
16059     resetConstraints: function() {
16060
16061
16062         // Maintain offsets if necessary
16063         if (this.initPageX || this.initPageX === 0) {
16064             // figure out how much this thing has moved
16065             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
16066             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
16067
16068             this.setInitPosition(dx, dy);
16069
16070         // This is the first time we have detected the element's position
16071         } else {
16072             this.setInitPosition();
16073         }
16074
16075         if (this.constrainX) {
16076             this.setXConstraint( this.leftConstraint,
16077                                  this.rightConstraint,
16078                                  this.xTickSize        );
16079         }
16080
16081         if (this.constrainY) {
16082             this.setYConstraint( this.topConstraint,
16083                                  this.bottomConstraint,
16084                                  this.yTickSize         );
16085         }
16086     },
16087
16088     /**
16089      * Normally the drag element is moved pixel by pixel, but we can specify
16090      * that it move a number of pixels at a time.  This method resolves the
16091      * location when we have it set up like this.
16092      * @method getTick
16093      * @param {int} val where we want to place the object
16094      * @param {int[]} tickArray sorted array of valid points
16095      * @return {int} the closest tick
16096      * @private
16097      */
16098     getTick: function(val, tickArray) {
16099
16100         if (!tickArray) {
16101             // If tick interval is not defined, it is effectively 1 pixel,
16102             // so we return the value passed to us.
16103             return val;
16104         } else if (tickArray[0] >= val) {
16105             // The value is lower than the first tick, so we return the first
16106             // tick.
16107             return tickArray[0];
16108         } else {
16109             for (var i=0, len=tickArray.length; i<len; ++i) {
16110                 var next = i + 1;
16111                 if (tickArray[next] && tickArray[next] >= val) {
16112                     var diff1 = val - tickArray[i];
16113                     var diff2 = tickArray[next] - val;
16114                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
16115                 }
16116             }
16117
16118             // The value is larger than the last tick, so we return the last
16119             // tick.
16120             return tickArray[tickArray.length - 1];
16121         }
16122     },
16123
16124     /**
16125      * toString method
16126      * @method toString
16127      * @return {string} string representation of the dd obj
16128      */
16129     toString: function() {
16130         return ("DragDrop " + this.id);
16131     }
16132
16133 });
16134
16135 })();
16136 /*
16137  * Based on:
16138  * Ext JS Library 1.1.1
16139  * Copyright(c) 2006-2007, Ext JS, LLC.
16140  *
16141  * Originally Released Under LGPL - original licence link has changed is not relivant.
16142  *
16143  * Fork - LGPL
16144  * <script type="text/javascript">
16145  */
16146
16147
16148 /**
16149  * The drag and drop utility provides a framework for building drag and drop
16150  * applications.  In addition to enabling drag and drop for specific elements,
16151  * the drag and drop elements are tracked by the manager class, and the
16152  * interactions between the various elements are tracked during the drag and
16153  * the implementing code is notified about these important moments.
16154  */
16155
16156 // Only load the library once.  Rewriting the manager class would orphan
16157 // existing drag and drop instances.
16158 if (!Roo.dd.DragDropMgr) {
16159
16160 /**
16161  * @class Roo.dd.DragDropMgr
16162  * DragDropMgr is a singleton that tracks the element interaction for
16163  * all DragDrop items in the window.  Generally, you will not call
16164  * this class directly, but it does have helper methods that could
16165  * be useful in your DragDrop implementations.
16166  * @singleton
16167  */
16168 Roo.dd.DragDropMgr = function() {
16169
16170     var Event = Roo.EventManager;
16171
16172     return {
16173
16174         /**
16175          * Two dimensional Array of registered DragDrop objects.  The first
16176          * dimension is the DragDrop item group, the second the DragDrop
16177          * object.
16178          * @property ids
16179          * @type {string: string}
16180          * @private
16181          * @static
16182          */
16183         ids: {},
16184
16185         /**
16186          * Array of element ids defined as drag handles.  Used to determine
16187          * if the element that generated the mousedown event is actually the
16188          * handle and not the html element itself.
16189          * @property handleIds
16190          * @type {string: string}
16191          * @private
16192          * @static
16193          */
16194         handleIds: {},
16195
16196         /**
16197          * the DragDrop object that is currently being dragged
16198          * @property dragCurrent
16199          * @type DragDrop
16200          * @private
16201          * @static
16202          **/
16203         dragCurrent: null,
16204
16205         /**
16206          * the DragDrop object(s) that are being hovered over
16207          * @property dragOvers
16208          * @type Array
16209          * @private
16210          * @static
16211          */
16212         dragOvers: {},
16213
16214         /**
16215          * the X distance between the cursor and the object being dragged
16216          * @property deltaX
16217          * @type int
16218          * @private
16219          * @static
16220          */
16221         deltaX: 0,
16222
16223         /**
16224          * the Y distance between the cursor and the object being dragged
16225          * @property deltaY
16226          * @type int
16227          * @private
16228          * @static
16229          */
16230         deltaY: 0,
16231
16232         /**
16233          * Flag to determine if we should prevent the default behavior of the
16234          * events we define. By default this is true, but this can be set to
16235          * false if you need the default behavior (not recommended)
16236          * @property preventDefault
16237          * @type boolean
16238          * @static
16239          */
16240         preventDefault: true,
16241
16242         /**
16243          * Flag to determine if we should stop the propagation of the events
16244          * we generate. This is true by default but you may want to set it to
16245          * false if the html element contains other features that require the
16246          * mouse click.
16247          * @property stopPropagation
16248          * @type boolean
16249          * @static
16250          */
16251         stopPropagation: true,
16252
16253         /**
16254          * Internal flag that is set to true when drag and drop has been
16255          * intialized
16256          * @property initialized
16257          * @private
16258          * @static
16259          */
16260         initalized: false,
16261
16262         /**
16263          * All drag and drop can be disabled.
16264          * @property locked
16265          * @private
16266          * @static
16267          */
16268         locked: false,
16269
16270         /**
16271          * Called the first time an element is registered.
16272          * @method init
16273          * @private
16274          * @static
16275          */
16276         init: function() {
16277             this.initialized = true;
16278         },
16279
16280         /**
16281          * In point mode, drag and drop interaction is defined by the
16282          * location of the cursor during the drag/drop
16283          * @property POINT
16284          * @type int
16285          * @static
16286          */
16287         POINT: 0,
16288
16289         /**
16290          * In intersect mode, drag and drop interactio nis defined by the
16291          * overlap of two or more drag and drop objects.
16292          * @property INTERSECT
16293          * @type int
16294          * @static
16295          */
16296         INTERSECT: 1,
16297
16298         /**
16299          * The current drag and drop mode.  Default: POINT
16300          * @property mode
16301          * @type int
16302          * @static
16303          */
16304         mode: 0,
16305
16306         /**
16307          * Runs method on all drag and drop objects
16308          * @method _execOnAll
16309          * @private
16310          * @static
16311          */
16312         _execOnAll: function(sMethod, args) {
16313             for (var i in this.ids) {
16314                 for (var j in this.ids[i]) {
16315                     var oDD = this.ids[i][j];
16316                     if (! this.isTypeOfDD(oDD)) {
16317                         continue;
16318                     }
16319                     oDD[sMethod].apply(oDD, args);
16320                 }
16321             }
16322         },
16323
16324         /**
16325          * Drag and drop initialization.  Sets up the global event handlers
16326          * @method _onLoad
16327          * @private
16328          * @static
16329          */
16330         _onLoad: function() {
16331
16332             this.init();
16333
16334             if (!Roo.isTouch) {
16335                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
16336                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
16337             }
16338             Event.on(document, "touchend",   this.handleMouseUp, this, true);
16339             Event.on(document, "touchmove", this.handleMouseMove, this, true);
16340             
16341             Event.on(window,   "unload",    this._onUnload, this, true);
16342             Event.on(window,   "resize",    this._onResize, this, true);
16343             // Event.on(window,   "mouseout",    this._test);
16344
16345         },
16346
16347         /**
16348          * Reset constraints on all drag and drop objs
16349          * @method _onResize
16350          * @private
16351          * @static
16352          */
16353         _onResize: function(e) {
16354             this._execOnAll("resetConstraints", []);
16355         },
16356
16357         /**
16358          * Lock all drag and drop functionality
16359          * @method lock
16360          * @static
16361          */
16362         lock: function() { this.locked = true; },
16363
16364         /**
16365          * Unlock all drag and drop functionality
16366          * @method unlock
16367          * @static
16368          */
16369         unlock: function() { this.locked = false; },
16370
16371         /**
16372          * Is drag and drop locked?
16373          * @method isLocked
16374          * @return {boolean} True if drag and drop is locked, false otherwise.
16375          * @static
16376          */
16377         isLocked: function() { return this.locked; },
16378
16379         /**
16380          * Location cache that is set for all drag drop objects when a drag is
16381          * initiated, cleared when the drag is finished.
16382          * @property locationCache
16383          * @private
16384          * @static
16385          */
16386         locationCache: {},
16387
16388         /**
16389          * Set useCache to false if you want to force object the lookup of each
16390          * drag and drop linked element constantly during a drag.
16391          * @property useCache
16392          * @type boolean
16393          * @static
16394          */
16395         useCache: true,
16396
16397         /**
16398          * The number of pixels that the mouse needs to move after the
16399          * mousedown before the drag is initiated.  Default=3;
16400          * @property clickPixelThresh
16401          * @type int
16402          * @static
16403          */
16404         clickPixelThresh: 3,
16405
16406         /**
16407          * The number of milliseconds after the mousedown event to initiate the
16408          * drag if we don't get a mouseup event. Default=1000
16409          * @property clickTimeThresh
16410          * @type int
16411          * @static
16412          */
16413         clickTimeThresh: 350,
16414
16415         /**
16416          * Flag that indicates that either the drag pixel threshold or the
16417          * mousdown time threshold has been met
16418          * @property dragThreshMet
16419          * @type boolean
16420          * @private
16421          * @static
16422          */
16423         dragThreshMet: false,
16424
16425         /**
16426          * Timeout used for the click time threshold
16427          * @property clickTimeout
16428          * @type Object
16429          * @private
16430          * @static
16431          */
16432         clickTimeout: null,
16433
16434         /**
16435          * The X position of the mousedown event stored for later use when a
16436          * drag threshold is met.
16437          * @property startX
16438          * @type int
16439          * @private
16440          * @static
16441          */
16442         startX: 0,
16443
16444         /**
16445          * The Y position of the mousedown event stored for later use when a
16446          * drag threshold is met.
16447          * @property startY
16448          * @type int
16449          * @private
16450          * @static
16451          */
16452         startY: 0,
16453
16454         /**
16455          * Each DragDrop instance must be registered with the DragDropMgr.
16456          * This is executed in DragDrop.init()
16457          * @method regDragDrop
16458          * @param {DragDrop} oDD the DragDrop object to register
16459          * @param {String} sGroup the name of the group this element belongs to
16460          * @static
16461          */
16462         regDragDrop: function(oDD, sGroup) {
16463             if (!this.initialized) { this.init(); }
16464
16465             if (!this.ids[sGroup]) {
16466                 this.ids[sGroup] = {};
16467             }
16468             this.ids[sGroup][oDD.id] = oDD;
16469         },
16470
16471         /**
16472          * Removes the supplied dd instance from the supplied group. Executed
16473          * by DragDrop.removeFromGroup, so don't call this function directly.
16474          * @method removeDDFromGroup
16475          * @private
16476          * @static
16477          */
16478         removeDDFromGroup: function(oDD, sGroup) {
16479             if (!this.ids[sGroup]) {
16480                 this.ids[sGroup] = {};
16481             }
16482
16483             var obj = this.ids[sGroup];
16484             if (obj && obj[oDD.id]) {
16485                 delete obj[oDD.id];
16486             }
16487         },
16488
16489         /**
16490          * Unregisters a drag and drop item.  This is executed in
16491          * DragDrop.unreg, use that method instead of calling this directly.
16492          * @method _remove
16493          * @private
16494          * @static
16495          */
16496         _remove: function(oDD) {
16497             for (var g in oDD.groups) {
16498                 if (g && this.ids[g][oDD.id]) {
16499                     delete this.ids[g][oDD.id];
16500                 }
16501             }
16502             delete this.handleIds[oDD.id];
16503         },
16504
16505         /**
16506          * Each DragDrop handle element must be registered.  This is done
16507          * automatically when executing DragDrop.setHandleElId()
16508          * @method regHandle
16509          * @param {String} sDDId the DragDrop id this element is a handle for
16510          * @param {String} sHandleId the id of the element that is the drag
16511          * handle
16512          * @static
16513          */
16514         regHandle: function(sDDId, sHandleId) {
16515             if (!this.handleIds[sDDId]) {
16516                 this.handleIds[sDDId] = {};
16517             }
16518             this.handleIds[sDDId][sHandleId] = sHandleId;
16519         },
16520
16521         /**
16522          * Utility function to determine if a given element has been
16523          * registered as a drag drop item.
16524          * @method isDragDrop
16525          * @param {String} id the element id to check
16526          * @return {boolean} true if this element is a DragDrop item,
16527          * false otherwise
16528          * @static
16529          */
16530         isDragDrop: function(id) {
16531             return ( this.getDDById(id) ) ? true : false;
16532         },
16533
16534         /**
16535          * Returns the drag and drop instances that are in all groups the
16536          * passed in instance belongs to.
16537          * @method getRelated
16538          * @param {DragDrop} p_oDD the obj to get related data for
16539          * @param {boolean} bTargetsOnly if true, only return targetable objs
16540          * @return {DragDrop[]} the related instances
16541          * @static
16542          */
16543         getRelated: function(p_oDD, bTargetsOnly) {
16544             var oDDs = [];
16545             for (var i in p_oDD.groups) {
16546                 for (j in this.ids[i]) {
16547                     var dd = this.ids[i][j];
16548                     if (! this.isTypeOfDD(dd)) {
16549                         continue;
16550                     }
16551                     if (!bTargetsOnly || dd.isTarget) {
16552                         oDDs[oDDs.length] = dd;
16553                     }
16554                 }
16555             }
16556
16557             return oDDs;
16558         },
16559
16560         /**
16561          * Returns true if the specified dd target is a legal target for
16562          * the specifice drag obj
16563          * @method isLegalTarget
16564          * @param {DragDrop} the drag obj
16565          * @param {DragDrop} the target
16566          * @return {boolean} true if the target is a legal target for the
16567          * dd obj
16568          * @static
16569          */
16570         isLegalTarget: function (oDD, oTargetDD) {
16571             var targets = this.getRelated(oDD, true);
16572             for (var i=0, len=targets.length;i<len;++i) {
16573                 if (targets[i].id == oTargetDD.id) {
16574                     return true;
16575                 }
16576             }
16577
16578             return false;
16579         },
16580
16581         /**
16582          * My goal is to be able to transparently determine if an object is
16583          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16584          * returns "object", oDD.constructor.toString() always returns
16585          * "DragDrop" and not the name of the subclass.  So for now it just
16586          * evaluates a well-known variable in DragDrop.
16587          * @method isTypeOfDD
16588          * @param {Object} the object to evaluate
16589          * @return {boolean} true if typeof oDD = DragDrop
16590          * @static
16591          */
16592         isTypeOfDD: function (oDD) {
16593             return (oDD && oDD.__ygDragDrop);
16594         },
16595
16596         /**
16597          * Utility function to determine if a given element has been
16598          * registered as a drag drop handle for the given Drag Drop object.
16599          * @method isHandle
16600          * @param {String} id the element id to check
16601          * @return {boolean} true if this element is a DragDrop handle, false
16602          * otherwise
16603          * @static
16604          */
16605         isHandle: function(sDDId, sHandleId) {
16606             return ( this.handleIds[sDDId] &&
16607                             this.handleIds[sDDId][sHandleId] );
16608         },
16609
16610         /**
16611          * Returns the DragDrop instance for a given id
16612          * @method getDDById
16613          * @param {String} id the id of the DragDrop object
16614          * @return {DragDrop} the drag drop object, null if it is not found
16615          * @static
16616          */
16617         getDDById: function(id) {
16618             for (var i in this.ids) {
16619                 if (this.ids[i][id]) {
16620                     return this.ids[i][id];
16621                 }
16622             }
16623             return null;
16624         },
16625
16626         /**
16627          * Fired after a registered DragDrop object gets the mousedown event.
16628          * Sets up the events required to track the object being dragged
16629          * @method handleMouseDown
16630          * @param {Event} e the event
16631          * @param oDD the DragDrop object being dragged
16632          * @private
16633          * @static
16634          */
16635         handleMouseDown: function(e, oDD) {
16636             if(Roo.QuickTips){
16637                 Roo.QuickTips.disable();
16638             }
16639             this.currentTarget = e.getTarget();
16640
16641             this.dragCurrent = oDD;
16642
16643             var el = oDD.getEl();
16644
16645             // track start position
16646             this.startX = e.getPageX();
16647             this.startY = e.getPageY();
16648
16649             this.deltaX = this.startX - el.offsetLeft;
16650             this.deltaY = this.startY - el.offsetTop;
16651
16652             this.dragThreshMet = false;
16653
16654             this.clickTimeout = setTimeout(
16655                     function() {
16656                         var DDM = Roo.dd.DDM;
16657                         DDM.startDrag(DDM.startX, DDM.startY);
16658                     },
16659                     this.clickTimeThresh );
16660         },
16661
16662         /**
16663          * Fired when either the drag pixel threshol or the mousedown hold
16664          * time threshold has been met.
16665          * @method startDrag
16666          * @param x {int} the X position of the original mousedown
16667          * @param y {int} the Y position of the original mousedown
16668          * @static
16669          */
16670         startDrag: function(x, y) {
16671             clearTimeout(this.clickTimeout);
16672             if (this.dragCurrent) {
16673                 this.dragCurrent.b4StartDrag(x, y);
16674                 this.dragCurrent.startDrag(x, y);
16675             }
16676             this.dragThreshMet = true;
16677         },
16678
16679         /**
16680          * Internal function to handle the mouseup event.  Will be invoked
16681          * from the context of the document.
16682          * @method handleMouseUp
16683          * @param {Event} e the event
16684          * @private
16685          * @static
16686          */
16687         handleMouseUp: function(e) {
16688
16689             if(Roo.QuickTips){
16690                 Roo.QuickTips.enable();
16691             }
16692             if (! this.dragCurrent) {
16693                 return;
16694             }
16695
16696             clearTimeout(this.clickTimeout);
16697
16698             if (this.dragThreshMet) {
16699                 this.fireEvents(e, true);
16700             } else {
16701             }
16702
16703             this.stopDrag(e);
16704
16705             this.stopEvent(e);
16706         },
16707
16708         /**
16709          * Utility to stop event propagation and event default, if these
16710          * features are turned on.
16711          * @method stopEvent
16712          * @param {Event} e the event as returned by this.getEvent()
16713          * @static
16714          */
16715         stopEvent: function(e){
16716             if(this.stopPropagation) {
16717                 e.stopPropagation();
16718             }
16719
16720             if (this.preventDefault) {
16721                 e.preventDefault();
16722             }
16723         },
16724
16725         /**
16726          * Internal function to clean up event handlers after the drag
16727          * operation is complete
16728          * @method stopDrag
16729          * @param {Event} e the event
16730          * @private
16731          * @static
16732          */
16733         stopDrag: function(e) {
16734             // Fire the drag end event for the item that was dragged
16735             if (this.dragCurrent) {
16736                 if (this.dragThreshMet) {
16737                     this.dragCurrent.b4EndDrag(e);
16738                     this.dragCurrent.endDrag(e);
16739                 }
16740
16741                 this.dragCurrent.onMouseUp(e);
16742             }
16743
16744             this.dragCurrent = null;
16745             this.dragOvers = {};
16746         },
16747
16748         /**
16749          * Internal function to handle the mousemove event.  Will be invoked
16750          * from the context of the html element.
16751          *
16752          * @TODO figure out what we can do about mouse events lost when the
16753          * user drags objects beyond the window boundary.  Currently we can
16754          * detect this in internet explorer by verifying that the mouse is
16755          * down during the mousemove event.  Firefox doesn't give us the
16756          * button state on the mousemove event.
16757          * @method handleMouseMove
16758          * @param {Event} e the event
16759          * @private
16760          * @static
16761          */
16762         handleMouseMove: function(e) {
16763             if (! this.dragCurrent) {
16764                 return true;
16765             }
16766
16767             // var button = e.which || e.button;
16768
16769             // check for IE mouseup outside of page boundary
16770             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16771                 this.stopEvent(e);
16772                 return this.handleMouseUp(e);
16773             }
16774
16775             if (!this.dragThreshMet) {
16776                 var diffX = Math.abs(this.startX - e.getPageX());
16777                 var diffY = Math.abs(this.startY - e.getPageY());
16778                 if (diffX > this.clickPixelThresh ||
16779                             diffY > this.clickPixelThresh) {
16780                     this.startDrag(this.startX, this.startY);
16781                 }
16782             }
16783
16784             if (this.dragThreshMet) {
16785                 this.dragCurrent.b4Drag(e);
16786                 this.dragCurrent.onDrag(e);
16787                 if(!this.dragCurrent.moveOnly){
16788                     this.fireEvents(e, false);
16789                 }
16790             }
16791
16792             this.stopEvent(e);
16793
16794             return true;
16795         },
16796
16797         /**
16798          * Iterates over all of the DragDrop elements to find ones we are
16799          * hovering over or dropping on
16800          * @method fireEvents
16801          * @param {Event} e the event
16802          * @param {boolean} isDrop is this a drop op or a mouseover op?
16803          * @private
16804          * @static
16805          */
16806         fireEvents: function(e, isDrop) {
16807             var dc = this.dragCurrent;
16808
16809             // If the user did the mouse up outside of the window, we could
16810             // get here even though we have ended the drag.
16811             if (!dc || dc.isLocked()) {
16812                 return;
16813             }
16814
16815             var pt = e.getPoint();
16816
16817             // cache the previous dragOver array
16818             var oldOvers = [];
16819
16820             var outEvts   = [];
16821             var overEvts  = [];
16822             var dropEvts  = [];
16823             var enterEvts = [];
16824
16825             // Check to see if the object(s) we were hovering over is no longer
16826             // being hovered over so we can fire the onDragOut event
16827             for (var i in this.dragOvers) {
16828
16829                 var ddo = this.dragOvers[i];
16830
16831                 if (! this.isTypeOfDD(ddo)) {
16832                     continue;
16833                 }
16834
16835                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16836                     outEvts.push( ddo );
16837                 }
16838
16839                 oldOvers[i] = true;
16840                 delete this.dragOvers[i];
16841             }
16842
16843             for (var sGroup in dc.groups) {
16844
16845                 if ("string" != typeof sGroup) {
16846                     continue;
16847                 }
16848
16849                 for (i in this.ids[sGroup]) {
16850                     var oDD = this.ids[sGroup][i];
16851                     if (! this.isTypeOfDD(oDD)) {
16852                         continue;
16853                     }
16854
16855                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16856                         if (this.isOverTarget(pt, oDD, this.mode)) {
16857                             // look for drop interactions
16858                             if (isDrop) {
16859                                 dropEvts.push( oDD );
16860                             // look for drag enter and drag over interactions
16861                             } else {
16862
16863                                 // initial drag over: dragEnter fires
16864                                 if (!oldOvers[oDD.id]) {
16865                                     enterEvts.push( oDD );
16866                                 // subsequent drag overs: dragOver fires
16867                                 } else {
16868                                     overEvts.push( oDD );
16869                                 }
16870
16871                                 this.dragOvers[oDD.id] = oDD;
16872                             }
16873                         }
16874                     }
16875                 }
16876             }
16877
16878             if (this.mode) {
16879                 if (outEvts.length) {
16880                     dc.b4DragOut(e, outEvts);
16881                     dc.onDragOut(e, outEvts);
16882                 }
16883
16884                 if (enterEvts.length) {
16885                     dc.onDragEnter(e, enterEvts);
16886                 }
16887
16888                 if (overEvts.length) {
16889                     dc.b4DragOver(e, overEvts);
16890                     dc.onDragOver(e, overEvts);
16891                 }
16892
16893                 if (dropEvts.length) {
16894                     dc.b4DragDrop(e, dropEvts);
16895                     dc.onDragDrop(e, dropEvts);
16896                 }
16897
16898             } else {
16899                 // fire dragout events
16900                 var len = 0;
16901                 for (i=0, len=outEvts.length; i<len; ++i) {
16902                     dc.b4DragOut(e, outEvts[i].id);
16903                     dc.onDragOut(e, outEvts[i].id);
16904                 }
16905
16906                 // fire enter events
16907                 for (i=0,len=enterEvts.length; i<len; ++i) {
16908                     // dc.b4DragEnter(e, oDD.id);
16909                     dc.onDragEnter(e, enterEvts[i].id);
16910                 }
16911
16912                 // fire over events
16913                 for (i=0,len=overEvts.length; i<len; ++i) {
16914                     dc.b4DragOver(e, overEvts[i].id);
16915                     dc.onDragOver(e, overEvts[i].id);
16916                 }
16917
16918                 // fire drop events
16919                 for (i=0, len=dropEvts.length; i<len; ++i) {
16920                     dc.b4DragDrop(e, dropEvts[i].id);
16921                     dc.onDragDrop(e, dropEvts[i].id);
16922                 }
16923
16924             }
16925
16926             // notify about a drop that did not find a target
16927             if (isDrop && !dropEvts.length) {
16928                 dc.onInvalidDrop(e);
16929             }
16930
16931         },
16932
16933         /**
16934          * Helper function for getting the best match from the list of drag
16935          * and drop objects returned by the drag and drop events when we are
16936          * in INTERSECT mode.  It returns either the first object that the
16937          * cursor is over, or the object that has the greatest overlap with
16938          * the dragged element.
16939          * @method getBestMatch
16940          * @param  {DragDrop[]} dds The array of drag and drop objects
16941          * targeted
16942          * @return {DragDrop}       The best single match
16943          * @static
16944          */
16945         getBestMatch: function(dds) {
16946             var winner = null;
16947             // Return null if the input is not what we expect
16948             //if (!dds || !dds.length || dds.length == 0) {
16949                // winner = null;
16950             // If there is only one item, it wins
16951             //} else if (dds.length == 1) {
16952
16953             var len = dds.length;
16954
16955             if (len == 1) {
16956                 winner = dds[0];
16957             } else {
16958                 // Loop through the targeted items
16959                 for (var i=0; i<len; ++i) {
16960                     var dd = dds[i];
16961                     // If the cursor is over the object, it wins.  If the
16962                     // cursor is over multiple matches, the first one we come
16963                     // to wins.
16964                     if (dd.cursorIsOver) {
16965                         winner = dd;
16966                         break;
16967                     // Otherwise the object with the most overlap wins
16968                     } else {
16969                         if (!winner ||
16970                             winner.overlap.getArea() < dd.overlap.getArea()) {
16971                             winner = dd;
16972                         }
16973                     }
16974                 }
16975             }
16976
16977             return winner;
16978         },
16979
16980         /**
16981          * Refreshes the cache of the top-left and bottom-right points of the
16982          * drag and drop objects in the specified group(s).  This is in the
16983          * format that is stored in the drag and drop instance, so typical
16984          * usage is:
16985          * <code>
16986          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16987          * </code>
16988          * Alternatively:
16989          * <code>
16990          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16991          * </code>
16992          * @TODO this really should be an indexed array.  Alternatively this
16993          * method could accept both.
16994          * @method refreshCache
16995          * @param {Object} groups an associative array of groups to refresh
16996          * @static
16997          */
16998         refreshCache: function(groups) {
16999             for (var sGroup in groups) {
17000                 if ("string" != typeof sGroup) {
17001                     continue;
17002                 }
17003                 for (var i in this.ids[sGroup]) {
17004                     var oDD = this.ids[sGroup][i];
17005
17006                     if (this.isTypeOfDD(oDD)) {
17007                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
17008                         var loc = this.getLocation(oDD);
17009                         if (loc) {
17010                             this.locationCache[oDD.id] = loc;
17011                         } else {
17012                             delete this.locationCache[oDD.id];
17013                             // this will unregister the drag and drop object if
17014                             // the element is not in a usable state
17015                             // oDD.unreg();
17016                         }
17017                     }
17018                 }
17019             }
17020         },
17021
17022         /**
17023          * This checks to make sure an element exists and is in the DOM.  The
17024          * main purpose is to handle cases where innerHTML is used to remove
17025          * drag and drop objects from the DOM.  IE provides an 'unspecified
17026          * error' when trying to access the offsetParent of such an element
17027          * @method verifyEl
17028          * @param {HTMLElement} el the element to check
17029          * @return {boolean} true if the element looks usable
17030          * @static
17031          */
17032         verifyEl: function(el) {
17033             if (el) {
17034                 var parent;
17035                 if(Roo.isIE){
17036                     try{
17037                         parent = el.offsetParent;
17038                     }catch(e){}
17039                 }else{
17040                     parent = el.offsetParent;
17041                 }
17042                 if (parent) {
17043                     return true;
17044                 }
17045             }
17046
17047             return false;
17048         },
17049
17050         /**
17051          * Returns a Region object containing the drag and drop element's position
17052          * and size, including the padding configured for it
17053          * @method getLocation
17054          * @param {DragDrop} oDD the drag and drop object to get the
17055          *                       location for
17056          * @return {Roo.lib.Region} a Region object representing the total area
17057          *                             the element occupies, including any padding
17058          *                             the instance is configured for.
17059          * @static
17060          */
17061         getLocation: function(oDD) {
17062             if (! this.isTypeOfDD(oDD)) {
17063                 return null;
17064             }
17065
17066             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
17067
17068             try {
17069                 pos= Roo.lib.Dom.getXY(el);
17070             } catch (e) { }
17071
17072             if (!pos) {
17073                 return null;
17074             }
17075
17076             x1 = pos[0];
17077             x2 = x1 + el.offsetWidth;
17078             y1 = pos[1];
17079             y2 = y1 + el.offsetHeight;
17080
17081             t = y1 - oDD.padding[0];
17082             r = x2 + oDD.padding[1];
17083             b = y2 + oDD.padding[2];
17084             l = x1 - oDD.padding[3];
17085
17086             return new Roo.lib.Region( t, r, b, l );
17087         },
17088
17089         /**
17090          * Checks the cursor location to see if it over the target
17091          * @method isOverTarget
17092          * @param {Roo.lib.Point} pt The point to evaluate
17093          * @param {DragDrop} oTarget the DragDrop object we are inspecting
17094          * @return {boolean} true if the mouse is over the target
17095          * @private
17096          * @static
17097          */
17098         isOverTarget: function(pt, oTarget, intersect) {
17099             // use cache if available
17100             var loc = this.locationCache[oTarget.id];
17101             if (!loc || !this.useCache) {
17102                 loc = this.getLocation(oTarget);
17103                 this.locationCache[oTarget.id] = loc;
17104
17105             }
17106
17107             if (!loc) {
17108                 return false;
17109             }
17110
17111             oTarget.cursorIsOver = loc.contains( pt );
17112
17113             // DragDrop is using this as a sanity check for the initial mousedown
17114             // in this case we are done.  In POINT mode, if the drag obj has no
17115             // contraints, we are also done. Otherwise we need to evaluate the
17116             // location of the target as related to the actual location of the
17117             // dragged element.
17118             var dc = this.dragCurrent;
17119             if (!dc || !dc.getTargetCoord ||
17120                     (!intersect && !dc.constrainX && !dc.constrainY)) {
17121                 return oTarget.cursorIsOver;
17122             }
17123
17124             oTarget.overlap = null;
17125
17126             // Get the current location of the drag element, this is the
17127             // location of the mouse event less the delta that represents
17128             // where the original mousedown happened on the element.  We
17129             // need to consider constraints and ticks as well.
17130             var pos = dc.getTargetCoord(pt.x, pt.y);
17131
17132             var el = dc.getDragEl();
17133             var curRegion = new Roo.lib.Region( pos.y,
17134                                                    pos.x + el.offsetWidth,
17135                                                    pos.y + el.offsetHeight,
17136                                                    pos.x );
17137
17138             var overlap = curRegion.intersect(loc);
17139
17140             if (overlap) {
17141                 oTarget.overlap = overlap;
17142                 return (intersect) ? true : oTarget.cursorIsOver;
17143             } else {
17144                 return false;
17145             }
17146         },
17147
17148         /**
17149          * unload event handler
17150          * @method _onUnload
17151          * @private
17152          * @static
17153          */
17154         _onUnload: function(e, me) {
17155             Roo.dd.DragDropMgr.unregAll();
17156         },
17157
17158         /**
17159          * Cleans up the drag and drop events and objects.
17160          * @method unregAll
17161          * @private
17162          * @static
17163          */
17164         unregAll: function() {
17165
17166             if (this.dragCurrent) {
17167                 this.stopDrag();
17168                 this.dragCurrent = null;
17169             }
17170
17171             this._execOnAll("unreg", []);
17172
17173             for (i in this.elementCache) {
17174                 delete this.elementCache[i];
17175             }
17176
17177             this.elementCache = {};
17178             this.ids = {};
17179         },
17180
17181         /**
17182          * A cache of DOM elements
17183          * @property elementCache
17184          * @private
17185          * @static
17186          */
17187         elementCache: {},
17188
17189         /**
17190          * Get the wrapper for the DOM element specified
17191          * @method getElWrapper
17192          * @param {String} id the id of the element to get
17193          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
17194          * @private
17195          * @deprecated This wrapper isn't that useful
17196          * @static
17197          */
17198         getElWrapper: function(id) {
17199             var oWrapper = this.elementCache[id];
17200             if (!oWrapper || !oWrapper.el) {
17201                 oWrapper = this.elementCache[id] =
17202                     new this.ElementWrapper(Roo.getDom(id));
17203             }
17204             return oWrapper;
17205         },
17206
17207         /**
17208          * Returns the actual DOM element
17209          * @method getElement
17210          * @param {String} id the id of the elment to get
17211          * @return {Object} The element
17212          * @deprecated use Roo.getDom instead
17213          * @static
17214          */
17215         getElement: function(id) {
17216             return Roo.getDom(id);
17217         },
17218
17219         /**
17220          * Returns the style property for the DOM element (i.e.,
17221          * document.getElById(id).style)
17222          * @method getCss
17223          * @param {String} id the id of the elment to get
17224          * @return {Object} The style property of the element
17225          * @deprecated use Roo.getDom instead
17226          * @static
17227          */
17228         getCss: function(id) {
17229             var el = Roo.getDom(id);
17230             return (el) ? el.style : null;
17231         },
17232
17233         /**
17234          * Inner class for cached elements
17235          * @class DragDropMgr.ElementWrapper
17236          * @for DragDropMgr
17237          * @private
17238          * @deprecated
17239          */
17240         ElementWrapper: function(el) {
17241                 /**
17242                  * The element
17243                  * @property el
17244                  */
17245                 this.el = el || null;
17246                 /**
17247                  * The element id
17248                  * @property id
17249                  */
17250                 this.id = this.el && el.id;
17251                 /**
17252                  * A reference to the style property
17253                  * @property css
17254                  */
17255                 this.css = this.el && el.style;
17256             },
17257
17258         /**
17259          * Returns the X position of an html element
17260          * @method getPosX
17261          * @param el the element for which to get the position
17262          * @return {int} the X coordinate
17263          * @for DragDropMgr
17264          * @deprecated use Roo.lib.Dom.getX instead
17265          * @static
17266          */
17267         getPosX: function(el) {
17268             return Roo.lib.Dom.getX(el);
17269         },
17270
17271         /**
17272          * Returns the Y position of an html element
17273          * @method getPosY
17274          * @param el the element for which to get the position
17275          * @return {int} the Y coordinate
17276          * @deprecated use Roo.lib.Dom.getY instead
17277          * @static
17278          */
17279         getPosY: function(el) {
17280             return Roo.lib.Dom.getY(el);
17281         },
17282
17283         /**
17284          * Swap two nodes.  In IE, we use the native method, for others we
17285          * emulate the IE behavior
17286          * @method swapNode
17287          * @param n1 the first node to swap
17288          * @param n2 the other node to swap
17289          * @static
17290          */
17291         swapNode: function(n1, n2) {
17292             if (n1.swapNode) {
17293                 n1.swapNode(n2);
17294             } else {
17295                 var p = n2.parentNode;
17296                 var s = n2.nextSibling;
17297
17298                 if (s == n1) {
17299                     p.insertBefore(n1, n2);
17300                 } else if (n2 == n1.nextSibling) {
17301                     p.insertBefore(n2, n1);
17302                 } else {
17303                     n1.parentNode.replaceChild(n2, n1);
17304                     p.insertBefore(n1, s);
17305                 }
17306             }
17307         },
17308
17309         /**
17310          * Returns the current scroll position
17311          * @method getScroll
17312          * @private
17313          * @static
17314          */
17315         getScroll: function () {
17316             var t, l, dde=document.documentElement, db=document.body;
17317             if (dde && (dde.scrollTop || dde.scrollLeft)) {
17318                 t = dde.scrollTop;
17319                 l = dde.scrollLeft;
17320             } else if (db) {
17321                 t = db.scrollTop;
17322                 l = db.scrollLeft;
17323             } else {
17324
17325             }
17326             return { top: t, left: l };
17327         },
17328
17329         /**
17330          * Returns the specified element style property
17331          * @method getStyle
17332          * @param {HTMLElement} el          the element
17333          * @param {string}      styleProp   the style property
17334          * @return {string} The value of the style property
17335          * @deprecated use Roo.lib.Dom.getStyle
17336          * @static
17337          */
17338         getStyle: function(el, styleProp) {
17339             return Roo.fly(el).getStyle(styleProp);
17340         },
17341
17342         /**
17343          * Gets the scrollTop
17344          * @method getScrollTop
17345          * @return {int} the document's scrollTop
17346          * @static
17347          */
17348         getScrollTop: function () { return this.getScroll().top; },
17349
17350         /**
17351          * Gets the scrollLeft
17352          * @method getScrollLeft
17353          * @return {int} the document's scrollTop
17354          * @static
17355          */
17356         getScrollLeft: function () { return this.getScroll().left; },
17357
17358         /**
17359          * Sets the x/y position of an element to the location of the
17360          * target element.
17361          * @method moveToEl
17362          * @param {HTMLElement} moveEl      The element to move
17363          * @param {HTMLElement} targetEl    The position reference element
17364          * @static
17365          */
17366         moveToEl: function (moveEl, targetEl) {
17367             var aCoord = Roo.lib.Dom.getXY(targetEl);
17368             Roo.lib.Dom.setXY(moveEl, aCoord);
17369         },
17370
17371         /**
17372          * Numeric array sort function
17373          * @method numericSort
17374          * @static
17375          */
17376         numericSort: function(a, b) { return (a - b); },
17377
17378         /**
17379          * Internal counter
17380          * @property _timeoutCount
17381          * @private
17382          * @static
17383          */
17384         _timeoutCount: 0,
17385
17386         /**
17387          * Trying to make the load order less important.  Without this we get
17388          * an error if this file is loaded before the Event Utility.
17389          * @method _addListeners
17390          * @private
17391          * @static
17392          */
17393         _addListeners: function() {
17394             var DDM = Roo.dd.DDM;
17395             if ( Roo.lib.Event && document ) {
17396                 DDM._onLoad();
17397             } else {
17398                 if (DDM._timeoutCount > 2000) {
17399                 } else {
17400                     setTimeout(DDM._addListeners, 10);
17401                     if (document && document.body) {
17402                         DDM._timeoutCount += 1;
17403                     }
17404                 }
17405             }
17406         },
17407
17408         /**
17409          * Recursively searches the immediate parent and all child nodes for
17410          * the handle element in order to determine wheter or not it was
17411          * clicked.
17412          * @method handleWasClicked
17413          * @param node the html element to inspect
17414          * @static
17415          */
17416         handleWasClicked: function(node, id) {
17417             if (this.isHandle(id, node.id)) {
17418                 return true;
17419             } else {
17420                 // check to see if this is a text node child of the one we want
17421                 var p = node.parentNode;
17422
17423                 while (p) {
17424                     if (this.isHandle(id, p.id)) {
17425                         return true;
17426                     } else {
17427                         p = p.parentNode;
17428                     }
17429                 }
17430             }
17431
17432             return false;
17433         }
17434
17435     };
17436
17437 }();
17438
17439 // shorter alias, save a few bytes
17440 Roo.dd.DDM = Roo.dd.DragDropMgr;
17441 Roo.dd.DDM._addListeners();
17442
17443 }/*
17444  * Based on:
17445  * Ext JS Library 1.1.1
17446  * Copyright(c) 2006-2007, Ext JS, LLC.
17447  *
17448  * Originally Released Under LGPL - original licence link has changed is not relivant.
17449  *
17450  * Fork - LGPL
17451  * <script type="text/javascript">
17452  */
17453
17454 /**
17455  * @class Roo.dd.DD
17456  * A DragDrop implementation where the linked element follows the
17457  * mouse cursor during a drag.
17458  * @extends Roo.dd.DragDrop
17459  * @constructor
17460  * @param {String} id the id of the linked element
17461  * @param {String} sGroup the group of related DragDrop items
17462  * @param {object} config an object containing configurable attributes
17463  *                Valid properties for DD:
17464  *                    scroll
17465  */
17466 Roo.dd.DD = function(id, sGroup, config) {
17467     if (id) {
17468         this.init(id, sGroup, config);
17469     }
17470 };
17471
17472 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17473
17474     /**
17475      * When set to true, the utility automatically tries to scroll the browser
17476      * window wehn a drag and drop element is dragged near the viewport boundary.
17477      * Defaults to true.
17478      * @property scroll
17479      * @type boolean
17480      */
17481     scroll: true,
17482
17483     /**
17484      * Sets the pointer offset to the distance between the linked element's top
17485      * left corner and the location the element was clicked
17486      * @method autoOffset
17487      * @param {int} iPageX the X coordinate of the click
17488      * @param {int} iPageY the Y coordinate of the click
17489      */
17490     autoOffset: function(iPageX, iPageY) {
17491         var x = iPageX - this.startPageX;
17492         var y = iPageY - this.startPageY;
17493         this.setDelta(x, y);
17494     },
17495
17496     /**
17497      * Sets the pointer offset.  You can call this directly to force the
17498      * offset to be in a particular location (e.g., pass in 0,0 to set it
17499      * to the center of the object)
17500      * @method setDelta
17501      * @param {int} iDeltaX the distance from the left
17502      * @param {int} iDeltaY the distance from the top
17503      */
17504     setDelta: function(iDeltaX, iDeltaY) {
17505         this.deltaX = iDeltaX;
17506         this.deltaY = iDeltaY;
17507     },
17508
17509     /**
17510      * Sets the drag element to the location of the mousedown or click event,
17511      * maintaining the cursor location relative to the location on the element
17512      * that was clicked.  Override this if you want to place the element in a
17513      * location other than where the cursor is.
17514      * @method setDragElPos
17515      * @param {int} iPageX the X coordinate of the mousedown or drag event
17516      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17517      */
17518     setDragElPos: function(iPageX, iPageY) {
17519         // the first time we do this, we are going to check to make sure
17520         // the element has css positioning
17521
17522         var el = this.getDragEl();
17523         this.alignElWithMouse(el, iPageX, iPageY);
17524     },
17525
17526     /**
17527      * Sets the element to the location of the mousedown or click event,
17528      * maintaining the cursor location relative to the location on the element
17529      * that was clicked.  Override this if you want to place the element in a
17530      * location other than where the cursor is.
17531      * @method alignElWithMouse
17532      * @param {HTMLElement} el the element to move
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     alignElWithMouse: function(el, iPageX, iPageY) {
17537         var oCoord = this.getTargetCoord(iPageX, iPageY);
17538         var fly = el.dom ? el : Roo.fly(el);
17539         if (!this.deltaSetXY) {
17540             var aCoord = [oCoord.x, oCoord.y];
17541             fly.setXY(aCoord);
17542             var newLeft = fly.getLeft(true);
17543             var newTop  = fly.getTop(true);
17544             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17545         } else {
17546             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17547         }
17548
17549         this.cachePosition(oCoord.x, oCoord.y);
17550         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17551         return oCoord;
17552     },
17553
17554     /**
17555      * Saves the most recent position so that we can reset the constraints and
17556      * tick marks on-demand.  We need to know this so that we can calculate the
17557      * number of pixels the element is offset from its original position.
17558      * @method cachePosition
17559      * @param iPageX the current x position (optional, this just makes it so we
17560      * don't have to look it up again)
17561      * @param iPageY the current y position (optional, this just makes it so we
17562      * don't have to look it up again)
17563      */
17564     cachePosition: function(iPageX, iPageY) {
17565         if (iPageX) {
17566             this.lastPageX = iPageX;
17567             this.lastPageY = iPageY;
17568         } else {
17569             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17570             this.lastPageX = aCoord[0];
17571             this.lastPageY = aCoord[1];
17572         }
17573     },
17574
17575     /**
17576      * Auto-scroll the window if the dragged object has been moved beyond the
17577      * visible window boundary.
17578      * @method autoScroll
17579      * @param {int} x the drag element's x position
17580      * @param {int} y the drag element's y position
17581      * @param {int} h the height of the drag element
17582      * @param {int} w the width of the drag element
17583      * @private
17584      */
17585     autoScroll: function(x, y, h, w) {
17586
17587         if (this.scroll) {
17588             // The client height
17589             var clientH = Roo.lib.Dom.getViewWidth();
17590
17591             // The client width
17592             var clientW = Roo.lib.Dom.getViewHeight();
17593
17594             // The amt scrolled down
17595             var st = this.DDM.getScrollTop();
17596
17597             // The amt scrolled right
17598             var sl = this.DDM.getScrollLeft();
17599
17600             // Location of the bottom of the element
17601             var bot = h + y;
17602
17603             // Location of the right of the element
17604             var right = w + x;
17605
17606             // The distance from the cursor to the bottom of the visible area,
17607             // adjusted so that we don't scroll if the cursor is beyond the
17608             // element drag constraints
17609             var toBot = (clientH + st - y - this.deltaY);
17610
17611             // The distance from the cursor to the right of the visible area
17612             var toRight = (clientW + sl - x - this.deltaX);
17613
17614
17615             // How close to the edge the cursor must be before we scroll
17616             // var thresh = (document.all) ? 100 : 40;
17617             var thresh = 40;
17618
17619             // How many pixels to scroll per autoscroll op.  This helps to reduce
17620             // clunky scrolling. IE is more sensitive about this ... it needs this
17621             // value to be higher.
17622             var scrAmt = (document.all) ? 80 : 30;
17623
17624             // Scroll down if we are near the bottom of the visible page and the
17625             // obj extends below the crease
17626             if ( bot > clientH && toBot < thresh ) {
17627                 window.scrollTo(sl, st + scrAmt);
17628             }
17629
17630             // Scroll up if the window is scrolled down and the top of the object
17631             // goes above the top border
17632             if ( y < st && st > 0 && y - st < thresh ) {
17633                 window.scrollTo(sl, st - scrAmt);
17634             }
17635
17636             // Scroll right if the obj is beyond the right border and the cursor is
17637             // near the border.
17638             if ( right > clientW && toRight < thresh ) {
17639                 window.scrollTo(sl + scrAmt, st);
17640             }
17641
17642             // Scroll left if the window has been scrolled to the right and the obj
17643             // extends past the left border
17644             if ( x < sl && sl > 0 && x - sl < thresh ) {
17645                 window.scrollTo(sl - scrAmt, st);
17646             }
17647         }
17648     },
17649
17650     /**
17651      * Finds the location the element should be placed if we want to move
17652      * it to where the mouse location less the click offset would place us.
17653      * @method getTargetCoord
17654      * @param {int} iPageX the X coordinate of the click
17655      * @param {int} iPageY the Y coordinate of the click
17656      * @return an object that contains the coordinates (Object.x and Object.y)
17657      * @private
17658      */
17659     getTargetCoord: function(iPageX, iPageY) {
17660
17661
17662         var x = iPageX - this.deltaX;
17663         var y = iPageY - this.deltaY;
17664
17665         if (this.constrainX) {
17666             if (x < this.minX) { x = this.minX; }
17667             if (x > this.maxX) { x = this.maxX; }
17668         }
17669
17670         if (this.constrainY) {
17671             if (y < this.minY) { y = this.minY; }
17672             if (y > this.maxY) { y = this.maxY; }
17673         }
17674
17675         x = this.getTick(x, this.xTicks);
17676         y = this.getTick(y, this.yTicks);
17677
17678
17679         return {x:x, y:y};
17680     },
17681
17682     /*
17683      * Sets up config options specific to this class. Overrides
17684      * Roo.dd.DragDrop, but all versions of this method through the
17685      * inheritance chain are called
17686      */
17687     applyConfig: function() {
17688         Roo.dd.DD.superclass.applyConfig.call(this);
17689         this.scroll = (this.config.scroll !== false);
17690     },
17691
17692     /*
17693      * Event that fires prior to the onMouseDown event.  Overrides
17694      * Roo.dd.DragDrop.
17695      */
17696     b4MouseDown: function(e) {
17697         // this.resetConstraints();
17698         this.autoOffset(e.getPageX(),
17699                             e.getPageY());
17700     },
17701
17702     /*
17703      * Event that fires prior to the onDrag event.  Overrides
17704      * Roo.dd.DragDrop.
17705      */
17706     b4Drag: function(e) {
17707         this.setDragElPos(e.getPageX(),
17708                             e.getPageY());
17709     },
17710
17711     toString: function() {
17712         return ("DD " + this.id);
17713     }
17714
17715     //////////////////////////////////////////////////////////////////////////
17716     // Debugging ygDragDrop events that can be overridden
17717     //////////////////////////////////////////////////////////////////////////
17718     /*
17719     startDrag: function(x, y) {
17720     },
17721
17722     onDrag: function(e) {
17723     },
17724
17725     onDragEnter: function(e, id) {
17726     },
17727
17728     onDragOver: function(e, id) {
17729     },
17730
17731     onDragOut: function(e, id) {
17732     },
17733
17734     onDragDrop: function(e, id) {
17735     },
17736
17737     endDrag: function(e) {
17738     }
17739
17740     */
17741
17742 });/*
17743  * Based on:
17744  * Ext JS Library 1.1.1
17745  * Copyright(c) 2006-2007, Ext JS, LLC.
17746  *
17747  * Originally Released Under LGPL - original licence link has changed is not relivant.
17748  *
17749  * Fork - LGPL
17750  * <script type="text/javascript">
17751  */
17752
17753 /**
17754  * @class Roo.dd.DDProxy
17755  * A DragDrop implementation that inserts an empty, bordered div into
17756  * the document that follows the cursor during drag operations.  At the time of
17757  * the click, the frame div is resized to the dimensions of the linked html
17758  * element, and moved to the exact location of the linked element.
17759  *
17760  * References to the "frame" element refer to the single proxy element that
17761  * was created to be dragged in place of all DDProxy elements on the
17762  * page.
17763  *
17764  * @extends Roo.dd.DD
17765  * @constructor
17766  * @param {String} id the id of the linked html element
17767  * @param {String} sGroup the group of related DragDrop objects
17768  * @param {object} config an object containing configurable attributes
17769  *                Valid properties for DDProxy in addition to those in DragDrop:
17770  *                   resizeFrame, centerFrame, dragElId
17771  */
17772 Roo.dd.DDProxy = function(id, sGroup, config) {
17773     if (id) {
17774         this.init(id, sGroup, config);
17775         this.initFrame();
17776     }
17777 };
17778
17779 /**
17780  * The default drag frame div id
17781  * @property Roo.dd.DDProxy.dragElId
17782  * @type String
17783  * @static
17784  */
17785 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17786
17787 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17788
17789     /**
17790      * By default we resize the drag frame to be the same size as the element
17791      * we want to drag (this is to get the frame effect).  We can turn it off
17792      * if we want a different behavior.
17793      * @property resizeFrame
17794      * @type boolean
17795      */
17796     resizeFrame: true,
17797
17798     /**
17799      * By default the frame is positioned exactly where the drag element is, so
17800      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17801      * you do not have constraints on the obj is to have the drag frame centered
17802      * around the cursor.  Set centerFrame to true for this effect.
17803      * @property centerFrame
17804      * @type boolean
17805      */
17806     centerFrame: false,
17807
17808     /**
17809      * Creates the proxy element if it does not yet exist
17810      * @method createFrame
17811      */
17812     createFrame: function() {
17813         var self = this;
17814         var body = document.body;
17815
17816         if (!body || !body.firstChild) {
17817             setTimeout( function() { self.createFrame(); }, 50 );
17818             return;
17819         }
17820
17821         var div = this.getDragEl();
17822
17823         if (!div) {
17824             div    = document.createElement("div");
17825             div.id = this.dragElId;
17826             var s  = div.style;
17827
17828             s.position   = "absolute";
17829             s.visibility = "hidden";
17830             s.cursor     = "move";
17831             s.border     = "2px solid #aaa";
17832             s.zIndex     = 999;
17833
17834             // appendChild can blow up IE if invoked prior to the window load event
17835             // while rendering a table.  It is possible there are other scenarios
17836             // that would cause this to happen as well.
17837             body.insertBefore(div, body.firstChild);
17838         }
17839     },
17840
17841     /**
17842      * Initialization for the drag frame element.  Must be called in the
17843      * constructor of all subclasses
17844      * @method initFrame
17845      */
17846     initFrame: function() {
17847         this.createFrame();
17848     },
17849
17850     applyConfig: function() {
17851         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17852
17853         this.resizeFrame = (this.config.resizeFrame !== false);
17854         this.centerFrame = (this.config.centerFrame);
17855         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17856     },
17857
17858     /**
17859      * Resizes the drag frame to the dimensions of the clicked object, positions
17860      * it over the object, and finally displays it
17861      * @method showFrame
17862      * @param {int} iPageX X click position
17863      * @param {int} iPageY Y click position
17864      * @private
17865      */
17866     showFrame: function(iPageX, iPageY) {
17867         var el = this.getEl();
17868         var dragEl = this.getDragEl();
17869         var s = dragEl.style;
17870
17871         this._resizeProxy();
17872
17873         if (this.centerFrame) {
17874             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17875                            Math.round(parseInt(s.height, 10)/2) );
17876         }
17877
17878         this.setDragElPos(iPageX, iPageY);
17879
17880         Roo.fly(dragEl).show();
17881     },
17882
17883     /**
17884      * The proxy is automatically resized to the dimensions of the linked
17885      * element when a drag is initiated, unless resizeFrame is set to false
17886      * @method _resizeProxy
17887      * @private
17888      */
17889     _resizeProxy: function() {
17890         if (this.resizeFrame) {
17891             var el = this.getEl();
17892             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17893         }
17894     },
17895
17896     // overrides Roo.dd.DragDrop
17897     b4MouseDown: function(e) {
17898         var x = e.getPageX();
17899         var y = e.getPageY();
17900         this.autoOffset(x, y);
17901         this.setDragElPos(x, y);
17902     },
17903
17904     // overrides Roo.dd.DragDrop
17905     b4StartDrag: function(x, y) {
17906         // show the drag frame
17907         this.showFrame(x, y);
17908     },
17909
17910     // overrides Roo.dd.DragDrop
17911     b4EndDrag: function(e) {
17912         Roo.fly(this.getDragEl()).hide();
17913     },
17914
17915     // overrides Roo.dd.DragDrop
17916     // By default we try to move the element to the last location of the frame.
17917     // This is so that the default behavior mirrors that of Roo.dd.DD.
17918     endDrag: function(e) {
17919
17920         var lel = this.getEl();
17921         var del = this.getDragEl();
17922
17923         // Show the drag frame briefly so we can get its position
17924         del.style.visibility = "";
17925
17926         this.beforeMove();
17927         // Hide the linked element before the move to get around a Safari
17928         // rendering bug.
17929         lel.style.visibility = "hidden";
17930         Roo.dd.DDM.moveToEl(lel, del);
17931         del.style.visibility = "hidden";
17932         lel.style.visibility = "";
17933
17934         this.afterDrag();
17935     },
17936
17937     beforeMove : function(){
17938
17939     },
17940
17941     afterDrag : function(){
17942
17943     },
17944
17945     toString: function() {
17946         return ("DDProxy " + this.id);
17947     }
17948
17949 });
17950 /*
17951  * Based on:
17952  * Ext JS Library 1.1.1
17953  * Copyright(c) 2006-2007, Ext JS, LLC.
17954  *
17955  * Originally Released Under LGPL - original licence link has changed is not relivant.
17956  *
17957  * Fork - LGPL
17958  * <script type="text/javascript">
17959  */
17960
17961  /**
17962  * @class Roo.dd.DDTarget
17963  * A DragDrop implementation that does not move, but can be a drop
17964  * target.  You would get the same result by simply omitting implementation
17965  * for the event callbacks, but this way we reduce the processing cost of the
17966  * event listener and the callbacks.
17967  * @extends Roo.dd.DragDrop
17968  * @constructor
17969  * @param {String} id the id of the element that is a drop target
17970  * @param {String} sGroup the group of related DragDrop objects
17971  * @param {object} config an object containing configurable attributes
17972  *                 Valid properties for DDTarget in addition to those in
17973  *                 DragDrop:
17974  *                    none
17975  */
17976 Roo.dd.DDTarget = function(id, sGroup, config) {
17977     if (id) {
17978         this.initTarget(id, sGroup, config);
17979     }
17980     if (config.listeners || config.events) { 
17981        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17982             listeners : config.listeners || {}, 
17983             events : config.events || {} 
17984         });    
17985     }
17986 };
17987
17988 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17989 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17990     toString: function() {
17991         return ("DDTarget " + this.id);
17992     }
17993 });
17994 /*
17995  * Based on:
17996  * Ext JS Library 1.1.1
17997  * Copyright(c) 2006-2007, Ext JS, LLC.
17998  *
17999  * Originally Released Under LGPL - original licence link has changed is not relivant.
18000  *
18001  * Fork - LGPL
18002  * <script type="text/javascript">
18003  */
18004  
18005
18006 /**
18007  * @class Roo.dd.ScrollManager
18008  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
18009  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
18010  * @singleton
18011  */
18012 Roo.dd.ScrollManager = function(){
18013     var ddm = Roo.dd.DragDropMgr;
18014     var els = {};
18015     var dragEl = null;
18016     var proc = {};
18017     
18018     
18019     
18020     var onStop = function(e){
18021         dragEl = null;
18022         clearProc();
18023     };
18024     
18025     var triggerRefresh = function(){
18026         if(ddm.dragCurrent){
18027              ddm.refreshCache(ddm.dragCurrent.groups);
18028         }
18029     };
18030     
18031     var doScroll = function(){
18032         if(ddm.dragCurrent){
18033             var dds = Roo.dd.ScrollManager;
18034             if(!dds.animate){
18035                 if(proc.el.scroll(proc.dir, dds.increment)){
18036                     triggerRefresh();
18037                 }
18038             }else{
18039                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
18040             }
18041         }
18042     };
18043     
18044     var clearProc = function(){
18045         if(proc.id){
18046             clearInterval(proc.id);
18047         }
18048         proc.id = 0;
18049         proc.el = null;
18050         proc.dir = "";
18051     };
18052     
18053     var startProc = function(el, dir){
18054          Roo.log('scroll startproc');
18055         clearProc();
18056         proc.el = el;
18057         proc.dir = dir;
18058         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
18059     };
18060     
18061     var onFire = function(e, isDrop){
18062        
18063         if(isDrop || !ddm.dragCurrent){ return; }
18064         var dds = Roo.dd.ScrollManager;
18065         if(!dragEl || dragEl != ddm.dragCurrent){
18066             dragEl = ddm.dragCurrent;
18067             // refresh regions on drag start
18068             dds.refreshCache();
18069         }
18070         
18071         var xy = Roo.lib.Event.getXY(e);
18072         var pt = new Roo.lib.Point(xy[0], xy[1]);
18073         for(var id in els){
18074             var el = els[id], r = el._region;
18075             if(r && r.contains(pt) && el.isScrollable()){
18076                 if(r.bottom - pt.y <= dds.thresh){
18077                     if(proc.el != el){
18078                         startProc(el, "down");
18079                     }
18080                     return;
18081                 }else if(r.right - pt.x <= dds.thresh){
18082                     if(proc.el != el){
18083                         startProc(el, "left");
18084                     }
18085                     return;
18086                 }else if(pt.y - r.top <= dds.thresh){
18087                     if(proc.el != el){
18088                         startProc(el, "up");
18089                     }
18090                     return;
18091                 }else if(pt.x - r.left <= dds.thresh){
18092                     if(proc.el != el){
18093                         startProc(el, "right");
18094                     }
18095                     return;
18096                 }
18097             }
18098         }
18099         clearProc();
18100     };
18101     
18102     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
18103     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
18104     
18105     return {
18106         /**
18107          * Registers new overflow element(s) to auto scroll
18108          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
18109          */
18110         register : function(el){
18111             if(el instanceof Array){
18112                 for(var i = 0, len = el.length; i < len; i++) {
18113                         this.register(el[i]);
18114                 }
18115             }else{
18116                 el = Roo.get(el);
18117                 els[el.id] = el;
18118             }
18119             Roo.dd.ScrollManager.els = els;
18120         },
18121         
18122         /**
18123          * Unregisters overflow element(s) so they are no longer scrolled
18124          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
18125          */
18126         unregister : function(el){
18127             if(el instanceof Array){
18128                 for(var i = 0, len = el.length; i < len; i++) {
18129                         this.unregister(el[i]);
18130                 }
18131             }else{
18132                 el = Roo.get(el);
18133                 delete els[el.id];
18134             }
18135         },
18136         
18137         /**
18138          * The number of pixels from the edge of a container the pointer needs to be to 
18139          * trigger scrolling (defaults to 25)
18140          * @type Number
18141          */
18142         thresh : 25,
18143         
18144         /**
18145          * The number of pixels to scroll in each scroll increment (defaults to 50)
18146          * @type Number
18147          */
18148         increment : 100,
18149         
18150         /**
18151          * The frequency of scrolls in milliseconds (defaults to 500)
18152          * @type Number
18153          */
18154         frequency : 500,
18155         
18156         /**
18157          * True to animate the scroll (defaults to true)
18158          * @type Boolean
18159          */
18160         animate: true,
18161         
18162         /**
18163          * The animation duration in seconds - 
18164          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
18165          * @type Number
18166          */
18167         animDuration: .4,
18168         
18169         /**
18170          * Manually trigger a cache refresh.
18171          */
18172         refreshCache : function(){
18173             for(var id in els){
18174                 if(typeof els[id] == 'object'){ // for people extending the object prototype
18175                     els[id]._region = els[id].getRegion();
18176                 }
18177             }
18178         }
18179     };
18180 }();/*
18181  * Based on:
18182  * Ext JS Library 1.1.1
18183  * Copyright(c) 2006-2007, Ext JS, LLC.
18184  *
18185  * Originally Released Under LGPL - original licence link has changed is not relivant.
18186  *
18187  * Fork - LGPL
18188  * <script type="text/javascript">
18189  */
18190  
18191
18192 /**
18193  * @class Roo.dd.Registry
18194  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
18195  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
18196  * @singleton
18197  */
18198 Roo.dd.Registry = function(){
18199     var elements = {}; 
18200     var handles = {}; 
18201     var autoIdSeed = 0;
18202
18203     var getId = function(el, autogen){
18204         if(typeof el == "string"){
18205             return el;
18206         }
18207         var id = el.id;
18208         if(!id && autogen !== false){
18209             id = "roodd-" + (++autoIdSeed);
18210             el.id = id;
18211         }
18212         return id;
18213     };
18214     
18215     return {
18216     /**
18217      * Register a drag drop element
18218      * @param {String|HTMLElement} element The id or DOM node to register
18219      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
18220      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
18221      * knows how to interpret, plus there are some specific properties known to the Registry that should be
18222      * populated in the data object (if applicable):
18223      * <pre>
18224 Value      Description<br />
18225 ---------  ------------------------------------------<br />
18226 handles    Array of DOM nodes that trigger dragging<br />
18227            for the element being registered<br />
18228 isHandle   True if the element passed in triggers<br />
18229            dragging itself, else false
18230 </pre>
18231      */
18232         register : function(el, data){
18233             data = data || {};
18234             if(typeof el == "string"){
18235                 el = document.getElementById(el);
18236             }
18237             data.ddel = el;
18238             elements[getId(el)] = data;
18239             if(data.isHandle !== false){
18240                 handles[data.ddel.id] = data;
18241             }
18242             if(data.handles){
18243                 var hs = data.handles;
18244                 for(var i = 0, len = hs.length; i < len; i++){
18245                         handles[getId(hs[i])] = data;
18246                 }
18247             }
18248         },
18249
18250     /**
18251      * Unregister a drag drop element
18252      * @param {String|HTMLElement}  element The id or DOM node to unregister
18253      */
18254         unregister : function(el){
18255             var id = getId(el, false);
18256             var data = elements[id];
18257             if(data){
18258                 delete elements[id];
18259                 if(data.handles){
18260                     var hs = data.handles;
18261                     for(var i = 0, len = hs.length; i < len; i++){
18262                         delete handles[getId(hs[i], false)];
18263                     }
18264                 }
18265             }
18266         },
18267
18268     /**
18269      * Returns the handle registered for a DOM Node by id
18270      * @param {String|HTMLElement} id The DOM node or id to look up
18271      * @return {Object} handle The custom handle data
18272      */
18273         getHandle : function(id){
18274             if(typeof id != "string"){ // must be element?
18275                 id = id.id;
18276             }
18277             return handles[id];
18278         },
18279
18280     /**
18281      * Returns the handle that is registered for the DOM node that is the target of the event
18282      * @param {Event} e The event
18283      * @return {Object} handle The custom handle data
18284      */
18285         getHandleFromEvent : function(e){
18286             var t = Roo.lib.Event.getTarget(e);
18287             return t ? handles[t.id] : null;
18288         },
18289
18290     /**
18291      * Returns a custom data object that is registered for a DOM node by id
18292      * @param {String|HTMLElement} id The DOM node or id to look up
18293      * @return {Object} data The custom data
18294      */
18295         getTarget : function(id){
18296             if(typeof id != "string"){ // must be element?
18297                 id = id.id;
18298             }
18299             return elements[id];
18300         },
18301
18302     /**
18303      * Returns a custom data object that is registered for the DOM node that is the target of the event
18304      * @param {Event} e The event
18305      * @return {Object} data The custom data
18306      */
18307         getTargetFromEvent : function(e){
18308             var t = Roo.lib.Event.getTarget(e);
18309             return t ? elements[t.id] || handles[t.id] : null;
18310         }
18311     };
18312 }();/*
18313  * Based on:
18314  * Ext JS Library 1.1.1
18315  * Copyright(c) 2006-2007, Ext JS, LLC.
18316  *
18317  * Originally Released Under LGPL - original licence link has changed is not relivant.
18318  *
18319  * Fork - LGPL
18320  * <script type="text/javascript">
18321  */
18322  
18323
18324 /**
18325  * @class Roo.dd.StatusProxy
18326  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
18327  * default drag proxy used by all Roo.dd components.
18328  * @constructor
18329  * @param {Object} config
18330  */
18331 Roo.dd.StatusProxy = function(config){
18332     Roo.apply(this, config);
18333     this.id = this.id || Roo.id();
18334     this.el = new Roo.Layer({
18335         dh: {
18336             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
18337                 {tag: "div", cls: "x-dd-drop-icon"},
18338                 {tag: "div", cls: "x-dd-drag-ghost"}
18339             ]
18340         }, 
18341         shadow: !config || config.shadow !== false
18342     });
18343     this.ghost = Roo.get(this.el.dom.childNodes[1]);
18344     this.dropStatus = this.dropNotAllowed;
18345 };
18346
18347 Roo.dd.StatusProxy.prototype = {
18348     /**
18349      * @cfg {String} dropAllowed
18350      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
18351      */
18352     dropAllowed : "x-dd-drop-ok",
18353     /**
18354      * @cfg {String} dropNotAllowed
18355      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
18356      */
18357     dropNotAllowed : "x-dd-drop-nodrop",
18358
18359     /**
18360      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
18361      * over the current target element.
18362      * @param {String} cssClass The css class for the new drop status indicator image
18363      */
18364     setStatus : function(cssClass){
18365         cssClass = cssClass || this.dropNotAllowed;
18366         if(this.dropStatus != cssClass){
18367             this.el.replaceClass(this.dropStatus, cssClass);
18368             this.dropStatus = cssClass;
18369         }
18370     },
18371
18372     /**
18373      * Resets the status indicator to the default dropNotAllowed value
18374      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
18375      */
18376     reset : function(clearGhost){
18377         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
18378         this.dropStatus = this.dropNotAllowed;
18379         if(clearGhost){
18380             this.ghost.update("");
18381         }
18382     },
18383
18384     /**
18385      * Updates the contents of the ghost element
18386      * @param {String} html The html that will replace the current innerHTML of the ghost element
18387      */
18388     update : function(html){
18389         if(typeof html == "string"){
18390             this.ghost.update(html);
18391         }else{
18392             this.ghost.update("");
18393             html.style.margin = "0";
18394             this.ghost.dom.appendChild(html);
18395         }
18396         // ensure float = none set?? cant remember why though.
18397         var el = this.ghost.dom.firstChild;
18398                 if(el){
18399                         Roo.fly(el).setStyle('float', 'none');
18400                 }
18401     },
18402     
18403     /**
18404      * Returns the underlying proxy {@link Roo.Layer}
18405      * @return {Roo.Layer} el
18406     */
18407     getEl : function(){
18408         return this.el;
18409     },
18410
18411     /**
18412      * Returns the ghost element
18413      * @return {Roo.Element} el
18414      */
18415     getGhost : function(){
18416         return this.ghost;
18417     },
18418
18419     /**
18420      * Hides the proxy
18421      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
18422      */
18423     hide : function(clear){
18424         this.el.hide();
18425         if(clear){
18426             this.reset(true);
18427         }
18428     },
18429
18430     /**
18431      * Stops the repair animation if it's currently running
18432      */
18433     stop : function(){
18434         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
18435             this.anim.stop();
18436         }
18437     },
18438
18439     /**
18440      * Displays this proxy
18441      */
18442     show : function(){
18443         this.el.show();
18444     },
18445
18446     /**
18447      * Force the Layer to sync its shadow and shim positions to the element
18448      */
18449     sync : function(){
18450         this.el.sync();
18451     },
18452
18453     /**
18454      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
18455      * invalid drop operation by the item being dragged.
18456      * @param {Array} xy The XY position of the element ([x, y])
18457      * @param {Function} callback The function to call after the repair is complete
18458      * @param {Object} scope The scope in which to execute the callback
18459      */
18460     repair : function(xy, callback, scope){
18461         this.callback = callback;
18462         this.scope = scope;
18463         if(xy && this.animRepair !== false){
18464             this.el.addClass("x-dd-drag-repair");
18465             this.el.hideUnders(true);
18466             this.anim = this.el.shift({
18467                 duration: this.repairDuration || .5,
18468                 easing: 'easeOut',
18469                 xy: xy,
18470                 stopFx: true,
18471                 callback: this.afterRepair,
18472                 scope: this
18473             });
18474         }else{
18475             this.afterRepair();
18476         }
18477     },
18478
18479     // private
18480     afterRepair : function(){
18481         this.hide(true);
18482         if(typeof this.callback == "function"){
18483             this.callback.call(this.scope || this);
18484         }
18485         this.callback = null;
18486         this.scope = null;
18487     }
18488 };/*
18489  * Based on:
18490  * Ext JS Library 1.1.1
18491  * Copyright(c) 2006-2007, Ext JS, LLC.
18492  *
18493  * Originally Released Under LGPL - original licence link has changed is not relivant.
18494  *
18495  * Fork - LGPL
18496  * <script type="text/javascript">
18497  */
18498
18499 /**
18500  * @class Roo.dd.DragSource
18501  * @extends Roo.dd.DDProxy
18502  * A simple class that provides the basic implementation needed to make any element draggable.
18503  * @constructor
18504  * @param {String/HTMLElement/Element} el The container element
18505  * @param {Object} config
18506  */
18507 Roo.dd.DragSource = function(el, config){
18508     this.el = Roo.get(el);
18509     this.dragData = {};
18510     
18511     Roo.apply(this, config);
18512     
18513     if(!this.proxy){
18514         this.proxy = new Roo.dd.StatusProxy();
18515     }
18516
18517     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18518           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18519     
18520     this.dragging = false;
18521 };
18522
18523 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18524     /**
18525      * @cfg {String} dropAllowed
18526      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18527      */
18528     dropAllowed : "x-dd-drop-ok",
18529     /**
18530      * @cfg {String} dropNotAllowed
18531      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18532      */
18533     dropNotAllowed : "x-dd-drop-nodrop",
18534
18535     /**
18536      * Returns the data object associated with this drag source
18537      * @return {Object} data An object containing arbitrary data
18538      */
18539     getDragData : function(e){
18540         return this.dragData;
18541     },
18542
18543     // private
18544     onDragEnter : function(e, id){
18545         var target = Roo.dd.DragDropMgr.getDDById(id);
18546         this.cachedTarget = target;
18547         if(this.beforeDragEnter(target, e, id) !== false){
18548             if(target.isNotifyTarget){
18549                 var status = target.notifyEnter(this, e, this.dragData);
18550                 this.proxy.setStatus(status);
18551             }else{
18552                 this.proxy.setStatus(this.dropAllowed);
18553             }
18554             
18555             if(this.afterDragEnter){
18556                 /**
18557                  * An empty function by default, but provided so that you can perform a custom action
18558                  * when the dragged item enters the drop target by providing an implementation.
18559                  * @param {Roo.dd.DragDrop} target The drop target
18560                  * @param {Event} e The event object
18561                  * @param {String} id The id of the dragged element
18562                  * @method afterDragEnter
18563                  */
18564                 this.afterDragEnter(target, e, id);
18565             }
18566         }
18567     },
18568
18569     /**
18570      * An empty function by default, but provided so that you can perform a custom action
18571      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18572      * @param {Roo.dd.DragDrop} target The drop target
18573      * @param {Event} e The event object
18574      * @param {String} id The id of the dragged element
18575      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18576      */
18577     beforeDragEnter : function(target, e, id){
18578         return true;
18579     },
18580
18581     // private
18582     alignElWithMouse: function() {
18583         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18584         this.proxy.sync();
18585     },
18586
18587     // private
18588     onDragOver : function(e, id){
18589         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18590         if(this.beforeDragOver(target, e, id) !== false){
18591             if(target.isNotifyTarget){
18592                 var status = target.notifyOver(this, e, this.dragData);
18593                 this.proxy.setStatus(status);
18594             }
18595
18596             if(this.afterDragOver){
18597                 /**
18598                  * An empty function by default, but provided so that you can perform a custom action
18599                  * while the dragged item is over the drop target by providing an implementation.
18600                  * @param {Roo.dd.DragDrop} target The drop target
18601                  * @param {Event} e The event object
18602                  * @param {String} id The id of the dragged element
18603                  * @method afterDragOver
18604                  */
18605                 this.afterDragOver(target, e, id);
18606             }
18607         }
18608     },
18609
18610     /**
18611      * An empty function by default, but provided so that you can perform a custom action
18612      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18613      * @param {Roo.dd.DragDrop} target The drop target
18614      * @param {Event} e The event object
18615      * @param {String} id The id of the dragged element
18616      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18617      */
18618     beforeDragOver : function(target, e, id){
18619         return true;
18620     },
18621
18622     // private
18623     onDragOut : function(e, id){
18624         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18625         if(this.beforeDragOut(target, e, id) !== false){
18626             if(target.isNotifyTarget){
18627                 target.notifyOut(this, e, this.dragData);
18628             }
18629             this.proxy.reset();
18630             if(this.afterDragOut){
18631                 /**
18632                  * An empty function by default, but provided so that you can perform a custom action
18633                  * after the dragged item is dragged out of the target without dropping.
18634                  * @param {Roo.dd.DragDrop} target The drop target
18635                  * @param {Event} e The event object
18636                  * @param {String} id The id of the dragged element
18637                  * @method afterDragOut
18638                  */
18639                 this.afterDragOut(target, e, id);
18640             }
18641         }
18642         this.cachedTarget = null;
18643     },
18644
18645     /**
18646      * An empty function by default, but provided so that you can perform a custom action before the dragged
18647      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18648      * @param {Roo.dd.DragDrop} target The drop target
18649      * @param {Event} e The event object
18650      * @param {String} id The id of the dragged element
18651      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18652      */
18653     beforeDragOut : function(target, e, id){
18654         return true;
18655     },
18656     
18657     // private
18658     onDragDrop : function(e, id){
18659         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18660         if(this.beforeDragDrop(target, e, id) !== false){
18661             if(target.isNotifyTarget){
18662                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18663                     this.onValidDrop(target, e, id);
18664                 }else{
18665                     this.onInvalidDrop(target, e, id);
18666                 }
18667             }else{
18668                 this.onValidDrop(target, e, id);
18669             }
18670             
18671             if(this.afterDragDrop){
18672                 /**
18673                  * An empty function by default, but provided so that you can perform a custom action
18674                  * after a valid drag drop has occurred by providing an implementation.
18675                  * @param {Roo.dd.DragDrop} target The drop target
18676                  * @param {Event} e The event object
18677                  * @param {String} id The id of the dropped element
18678                  * @method afterDragDrop
18679                  */
18680                 this.afterDragDrop(target, e, id);
18681             }
18682         }
18683         delete this.cachedTarget;
18684     },
18685
18686     /**
18687      * An empty function by default, but provided so that you can perform a custom action before the dragged
18688      * item is dropped onto the target and optionally cancel the onDragDrop.
18689      * @param {Roo.dd.DragDrop} target The drop target
18690      * @param {Event} e The event object
18691      * @param {String} id The id of the dragged element
18692      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18693      */
18694     beforeDragDrop : function(target, e, id){
18695         return true;
18696     },
18697
18698     // private
18699     onValidDrop : function(target, e, id){
18700         this.hideProxy();
18701         if(this.afterValidDrop){
18702             /**
18703              * An empty function by default, but provided so that you can perform a custom action
18704              * after a valid drop has occurred by providing an implementation.
18705              * @param {Object} target The target DD 
18706              * @param {Event} e The event object
18707              * @param {String} id The id of the dropped element
18708              * @method afterInvalidDrop
18709              */
18710             this.afterValidDrop(target, e, id);
18711         }
18712     },
18713
18714     // private
18715     getRepairXY : function(e, data){
18716         return this.el.getXY();  
18717     },
18718
18719     // private
18720     onInvalidDrop : function(target, e, id){
18721         this.beforeInvalidDrop(target, e, id);
18722         if(this.cachedTarget){
18723             if(this.cachedTarget.isNotifyTarget){
18724                 this.cachedTarget.notifyOut(this, e, this.dragData);
18725             }
18726             this.cacheTarget = null;
18727         }
18728         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18729
18730         if(this.afterInvalidDrop){
18731             /**
18732              * An empty function by default, but provided so that you can perform a custom action
18733              * after an invalid drop has occurred by providing an implementation.
18734              * @param {Event} e The event object
18735              * @param {String} id The id of the dropped element
18736              * @method afterInvalidDrop
18737              */
18738             this.afterInvalidDrop(e, id);
18739         }
18740     },
18741
18742     // private
18743     afterRepair : function(){
18744         if(Roo.enableFx){
18745             this.el.highlight(this.hlColor || "c3daf9");
18746         }
18747         this.dragging = false;
18748     },
18749
18750     /**
18751      * An empty function by default, but provided so that you can perform a custom action after an invalid
18752      * drop has occurred.
18753      * @param {Roo.dd.DragDrop} target The drop target
18754      * @param {Event} e The event object
18755      * @param {String} id The id of the dragged element
18756      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18757      */
18758     beforeInvalidDrop : function(target, e, id){
18759         return true;
18760     },
18761
18762     // private
18763     handleMouseDown : function(e){
18764         if(this.dragging) {
18765             return;
18766         }
18767         var data = this.getDragData(e);
18768         if(data && this.onBeforeDrag(data, e) !== false){
18769             this.dragData = data;
18770             this.proxy.stop();
18771             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18772         } 
18773     },
18774
18775     /**
18776      * An empty function by default, but provided so that you can perform a custom action before the initial
18777      * drag event begins and optionally cancel it.
18778      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18779      * @param {Event} e The event object
18780      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18781      */
18782     onBeforeDrag : function(data, e){
18783         return true;
18784     },
18785
18786     /**
18787      * An empty function by default, but provided so that you can perform a custom action once the initial
18788      * drag event has begun.  The drag cannot be canceled from this function.
18789      * @param {Number} x The x position of the click on the dragged object
18790      * @param {Number} y The y position of the click on the dragged object
18791      */
18792     onStartDrag : Roo.emptyFn,
18793
18794     // private - YUI override
18795     startDrag : function(x, y){
18796         this.proxy.reset();
18797         this.dragging = true;
18798         this.proxy.update("");
18799         this.onInitDrag(x, y);
18800         this.proxy.show();
18801     },
18802
18803     // private
18804     onInitDrag : function(x, y){
18805         var clone = this.el.dom.cloneNode(true);
18806         clone.id = Roo.id(); // prevent duplicate ids
18807         this.proxy.update(clone);
18808         this.onStartDrag(x, y);
18809         return true;
18810     },
18811
18812     /**
18813      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18814      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18815      */
18816     getProxy : function(){
18817         return this.proxy;  
18818     },
18819
18820     /**
18821      * Hides the drag source's {@link Roo.dd.StatusProxy}
18822      */
18823     hideProxy : function(){
18824         this.proxy.hide();  
18825         this.proxy.reset(true);
18826         this.dragging = false;
18827     },
18828
18829     // private
18830     triggerCacheRefresh : function(){
18831         Roo.dd.DDM.refreshCache(this.groups);
18832     },
18833
18834     // private - override to prevent hiding
18835     b4EndDrag: function(e) {
18836     },
18837
18838     // private - override to prevent moving
18839     endDrag : function(e){
18840         this.onEndDrag(this.dragData, e);
18841     },
18842
18843     // private
18844     onEndDrag : function(data, e){
18845     },
18846     
18847     // private - pin to cursor
18848     autoOffset : function(x, y) {
18849         this.setDelta(-12, -20);
18850     }    
18851 });/*
18852  * Based on:
18853  * Ext JS Library 1.1.1
18854  * Copyright(c) 2006-2007, Ext JS, LLC.
18855  *
18856  * Originally Released Under LGPL - original licence link has changed is not relivant.
18857  *
18858  * Fork - LGPL
18859  * <script type="text/javascript">
18860  */
18861
18862
18863 /**
18864  * @class Roo.dd.DropTarget
18865  * @extends Roo.dd.DDTarget
18866  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18867  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18868  * @constructor
18869  * @param {String/HTMLElement/Element} el The container element
18870  * @param {Object} config
18871  */
18872 Roo.dd.DropTarget = function(el, config){
18873     this.el = Roo.get(el);
18874     
18875     var listeners = false; ;
18876     if (config && config.listeners) {
18877         listeners= config.listeners;
18878         delete config.listeners;
18879     }
18880     Roo.apply(this, config);
18881     
18882     if(this.containerScroll){
18883         Roo.dd.ScrollManager.register(this.el);
18884     }
18885     this.addEvents( {
18886          /**
18887          * @scope Roo.dd.DropTarget
18888          */
18889          
18890          /**
18891          * @event enter
18892          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18893          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18894          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18895          * 
18896          * IMPORTANT : it should set this.overClass and this.dropAllowed
18897          * 
18898          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18899          * @param {Event} e The event
18900          * @param {Object} data An object containing arbitrary data supplied by the drag source
18901          */
18902         "enter" : true,
18903         
18904          /**
18905          * @event over
18906          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18907          * This method will be called on every mouse movement while the drag source is over the drop target.
18908          * This default implementation simply returns the dropAllowed config value.
18909          * 
18910          * IMPORTANT : it should set this.dropAllowed
18911          * 
18912          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18913          * @param {Event} e The event
18914          * @param {Object} data An object containing arbitrary data supplied by the drag source
18915          
18916          */
18917         "over" : true,
18918         /**
18919          * @event out
18920          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18921          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18922          * overClass (if any) from the drop element.
18923          * 
18924          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18925          * @param {Event} e The event
18926          * @param {Object} data An object containing arbitrary data supplied by the drag source
18927          */
18928          "out" : true,
18929          
18930         /**
18931          * @event drop
18932          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18933          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18934          * implementation that does something to process the drop event and returns true so that the drag source's
18935          * repair action does not run.
18936          * 
18937          * IMPORTANT : it should set this.success
18938          * 
18939          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18940          * @param {Event} e The event
18941          * @param {Object} data An object containing arbitrary data supplied by the drag source
18942         */
18943          "drop" : true
18944     });
18945             
18946      
18947     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18948         this.el.dom, 
18949         this.ddGroup || this.group,
18950         {
18951             isTarget: true,
18952             listeners : listeners || {} 
18953            
18954         
18955         }
18956     );
18957
18958 };
18959
18960 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18961     /**
18962      * @cfg {String} overClass
18963      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18964      */
18965      /**
18966      * @cfg {String} ddGroup
18967      * The drag drop group to handle drop events for
18968      */
18969      
18970     /**
18971      * @cfg {String} dropAllowed
18972      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18973      */
18974     dropAllowed : "x-dd-drop-ok",
18975     /**
18976      * @cfg {String} dropNotAllowed
18977      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18978      */
18979     dropNotAllowed : "x-dd-drop-nodrop",
18980     /**
18981      * @cfg {boolean} success
18982      * set this after drop listener.. 
18983      */
18984     success : false,
18985     /**
18986      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18987      * if the drop point is valid for over/enter..
18988      */
18989     valid : false,
18990     // private
18991     isTarget : true,
18992
18993     // private
18994     isNotifyTarget : true,
18995     
18996     /**
18997      * @hide
18998      */
18999     notifyEnter : function(dd, e, data)
19000     {
19001         this.valid = true;
19002         this.fireEvent('enter', dd, e, data);
19003         if(this.overClass){
19004             this.el.addClass(this.overClass);
19005         }
19006         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
19007             this.valid ? this.dropAllowed : this.dropNotAllowed
19008         );
19009     },
19010
19011     /**
19012      * @hide
19013      */
19014     notifyOver : function(dd, e, data)
19015     {
19016         this.valid = true;
19017         this.fireEvent('over', dd, e, data);
19018         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
19019             this.valid ? this.dropAllowed : this.dropNotAllowed
19020         );
19021     },
19022
19023     /**
19024      * @hide
19025      */
19026     notifyOut : function(dd, e, data)
19027     {
19028         this.fireEvent('out', dd, e, data);
19029         if(this.overClass){
19030             this.el.removeClass(this.overClass);
19031         }
19032     },
19033
19034     /**
19035      * @hide
19036      */
19037     notifyDrop : function(dd, e, data)
19038     {
19039         this.success = false;
19040         this.fireEvent('drop', dd, e, data);
19041         return this.success;
19042     }
19043 });/*
19044  * Based on:
19045  * Ext JS Library 1.1.1
19046  * Copyright(c) 2006-2007, Ext JS, LLC.
19047  *
19048  * Originally Released Under LGPL - original licence link has changed is not relivant.
19049  *
19050  * Fork - LGPL
19051  * <script type="text/javascript">
19052  */
19053
19054
19055 /**
19056  * @class Roo.dd.DragZone
19057  * @extends Roo.dd.DragSource
19058  * This class provides a container DD instance that proxies for multiple child node sources.<br />
19059  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
19060  * @constructor
19061  * @param {String/HTMLElement/Element} el The container element
19062  * @param {Object} config
19063  */
19064 Roo.dd.DragZone = function(el, config){
19065     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
19066     if(this.containerScroll){
19067         Roo.dd.ScrollManager.register(this.el);
19068     }
19069 };
19070
19071 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
19072     /**
19073      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
19074      * for auto scrolling during drag operations.
19075      */
19076     /**
19077      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
19078      * method after a failed drop (defaults to "c3daf9" - light blue)
19079      */
19080
19081     /**
19082      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
19083      * for a valid target to drag based on the mouse down. Override this method
19084      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
19085      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
19086      * @param {EventObject} e The mouse down event
19087      * @return {Object} The dragData
19088      */
19089     getDragData : function(e){
19090         return Roo.dd.Registry.getHandleFromEvent(e);
19091     },
19092     
19093     /**
19094      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
19095      * this.dragData.ddel
19096      * @param {Number} x The x position of the click on the dragged object
19097      * @param {Number} y The y position of the click on the dragged object
19098      * @return {Boolean} true to continue the drag, false to cancel
19099      */
19100     onInitDrag : function(x, y){
19101         this.proxy.update(this.dragData.ddel.cloneNode(true));
19102         this.onStartDrag(x, y);
19103         return true;
19104     },
19105     
19106     /**
19107      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
19108      */
19109     afterRepair : function(){
19110         if(Roo.enableFx){
19111             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
19112         }
19113         this.dragging = false;
19114     },
19115
19116     /**
19117      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
19118      * the XY of this.dragData.ddel
19119      * @param {EventObject} e The mouse up event
19120      * @return {Array} The xy location (e.g. [100, 200])
19121      */
19122     getRepairXY : function(e){
19123         return Roo.Element.fly(this.dragData.ddel).getXY();  
19124     }
19125 });/*
19126  * Based on:
19127  * Ext JS Library 1.1.1
19128  * Copyright(c) 2006-2007, Ext JS, LLC.
19129  *
19130  * Originally Released Under LGPL - original licence link has changed is not relivant.
19131  *
19132  * Fork - LGPL
19133  * <script type="text/javascript">
19134  */
19135 /**
19136  * @class Roo.dd.DropZone
19137  * @extends Roo.dd.DropTarget
19138  * This class provides a container DD instance that proxies for multiple child node targets.<br />
19139  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
19140  * @constructor
19141  * @param {String/HTMLElement/Element} el The container element
19142  * @param {Object} config
19143  */
19144 Roo.dd.DropZone = function(el, config){
19145     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
19146 };
19147
19148 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
19149     /**
19150      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
19151      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
19152      * provide your own custom lookup.
19153      * @param {Event} e The event
19154      * @return {Object} data The custom data
19155      */
19156     getTargetFromEvent : function(e){
19157         return Roo.dd.Registry.getTargetFromEvent(e);
19158     },
19159
19160     /**
19161      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
19162      * that it has registered.  This method has no default implementation and should be overridden to provide
19163      * node-specific processing if necessary.
19164      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
19165      * {@link #getTargetFromEvent} for this node)
19166      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19167      * @param {Event} e The event
19168      * @param {Object} data An object containing arbitrary data supplied by the drag source
19169      */
19170     onNodeEnter : function(n, dd, e, data){
19171         
19172     },
19173
19174     /**
19175      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
19176      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
19177      * overridden to provide the proper feedback.
19178      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19179      * {@link #getTargetFromEvent} for this node)
19180      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19181      * @param {Event} e The event
19182      * @param {Object} data An object containing arbitrary data supplied by the drag source
19183      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19184      * underlying {@link Roo.dd.StatusProxy} can be updated
19185      */
19186     onNodeOver : function(n, dd, e, data){
19187         return this.dropAllowed;
19188     },
19189
19190     /**
19191      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
19192      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
19193      * node-specific processing if necessary.
19194      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19195      * {@link #getTargetFromEvent} for this node)
19196      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19197      * @param {Event} e The event
19198      * @param {Object} data An object containing arbitrary data supplied by the drag source
19199      */
19200     onNodeOut : function(n, dd, e, data){
19201         
19202     },
19203
19204     /**
19205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
19206      * the drop node.  The default implementation returns false, so it should be overridden to provide the
19207      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
19208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19209      * {@link #getTargetFromEvent} for this node)
19210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19211      * @param {Event} e The event
19212      * @param {Object} data An object containing arbitrary data supplied by the drag source
19213      * @return {Boolean} True if the drop was valid, else false
19214      */
19215     onNodeDrop : function(n, dd, e, data){
19216         return false;
19217     },
19218
19219     /**
19220      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
19221      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
19222      * it should be overridden to provide the proper feedback if necessary.
19223      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19224      * @param {Event} e The event
19225      * @param {Object} data An object containing arbitrary data supplied by the drag source
19226      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19227      * underlying {@link Roo.dd.StatusProxy} can be updated
19228      */
19229     onContainerOver : function(dd, e, data){
19230         return this.dropNotAllowed;
19231     },
19232
19233     /**
19234      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
19235      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
19236      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
19237      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
19238      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19239      * @param {Event} e The event
19240      * @param {Object} data An object containing arbitrary data supplied by the drag source
19241      * @return {Boolean} True if the drop was valid, else false
19242      */
19243     onContainerDrop : function(dd, e, data){
19244         return false;
19245     },
19246
19247     /**
19248      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
19249      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
19250      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
19251      * you should override this method and provide a custom implementation.
19252      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19253      * @param {Event} e The event
19254      * @param {Object} data An object containing arbitrary data supplied by the drag source
19255      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19256      * underlying {@link Roo.dd.StatusProxy} can be updated
19257      */
19258     notifyEnter : function(dd, e, data){
19259         return this.dropNotAllowed;
19260     },
19261
19262     /**
19263      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
19264      * This method will be called on every mouse movement while the drag source is over the drop zone.
19265      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
19266      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
19267      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
19268      * registered node, it will call {@link #onContainerOver}.
19269      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19270      * @param {Event} e The event
19271      * @param {Object} data An object containing arbitrary data supplied by the drag source
19272      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19273      * underlying {@link Roo.dd.StatusProxy} can be updated
19274      */
19275     notifyOver : function(dd, e, data){
19276         var n = this.getTargetFromEvent(e);
19277         if(!n){ // not over valid drop target
19278             if(this.lastOverNode){
19279                 this.onNodeOut(this.lastOverNode, dd, e, data);
19280                 this.lastOverNode = null;
19281             }
19282             return this.onContainerOver(dd, e, data);
19283         }
19284         if(this.lastOverNode != n){
19285             if(this.lastOverNode){
19286                 this.onNodeOut(this.lastOverNode, dd, e, data);
19287             }
19288             this.onNodeEnter(n, dd, e, data);
19289             this.lastOverNode = n;
19290         }
19291         return this.onNodeOver(n, dd, e, data);
19292     },
19293
19294     /**
19295      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
19296      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
19297      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
19298      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19299      * @param {Event} e The event
19300      * @param {Object} data An object containing arbitrary data supplied by the drag zone
19301      */
19302     notifyOut : function(dd, e, data){
19303         if(this.lastOverNode){
19304             this.onNodeOut(this.lastOverNode, dd, e, data);
19305             this.lastOverNode = null;
19306         }
19307     },
19308
19309     /**
19310      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
19311      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
19312      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
19313      * otherwise it will call {@link #onContainerDrop}.
19314      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19315      * @param {Event} e The event
19316      * @param {Object} data An object containing arbitrary data supplied by the drag source
19317      * @return {Boolean} True if the drop was valid, else false
19318      */
19319     notifyDrop : function(dd, e, data){
19320         if(this.lastOverNode){
19321             this.onNodeOut(this.lastOverNode, dd, e, data);
19322             this.lastOverNode = null;
19323         }
19324         var n = this.getTargetFromEvent(e);
19325         return n ?
19326             this.onNodeDrop(n, dd, e, data) :
19327             this.onContainerDrop(dd, e, data);
19328     },
19329
19330     // private
19331     triggerCacheRefresh : function(){
19332         Roo.dd.DDM.refreshCache(this.groups);
19333     }  
19334 });/*
19335  * Based on:
19336  * Ext JS Library 1.1.1
19337  * Copyright(c) 2006-2007, Ext JS, LLC.
19338  *
19339  * Originally Released Under LGPL - original licence link has changed is not relivant.
19340  *
19341  * Fork - LGPL
19342  * <script type="text/javascript">
19343  */
19344
19345
19346 /**
19347  * @class Roo.data.SortTypes
19348  * @singleton
19349  * Defines the default sorting (casting?) comparison functions used when sorting data.
19350  */
19351 Roo.data.SortTypes = {
19352     /**
19353      * Default sort that does nothing
19354      * @param {Mixed} s The value being converted
19355      * @return {Mixed} The comparison value
19356      */
19357     none : function(s){
19358         return s;
19359     },
19360     
19361     /**
19362      * The regular expression used to strip tags
19363      * @type {RegExp}
19364      * @property
19365      */
19366     stripTagsRE : /<\/?[^>]+>/gi,
19367     
19368     /**
19369      * Strips all HTML tags to sort on text only
19370      * @param {Mixed} s The value being converted
19371      * @return {String} The comparison value
19372      */
19373     asText : function(s){
19374         return String(s).replace(this.stripTagsRE, "");
19375     },
19376     
19377     /**
19378      * Strips all HTML tags to sort on text only - Case insensitive
19379      * @param {Mixed} s The value being converted
19380      * @return {String} The comparison value
19381      */
19382     asUCText : function(s){
19383         return String(s).toUpperCase().replace(this.stripTagsRE, "");
19384     },
19385     
19386     /**
19387      * Case insensitive string
19388      * @param {Mixed} s The value being converted
19389      * @return {String} The comparison value
19390      */
19391     asUCString : function(s) {
19392         return String(s).toUpperCase();
19393     },
19394     
19395     /**
19396      * Date sorting
19397      * @param {Mixed} s The value being converted
19398      * @return {Number} The comparison value
19399      */
19400     asDate : function(s) {
19401         if(!s){
19402             return 0;
19403         }
19404         if(s instanceof Date){
19405             return s.getTime();
19406         }
19407         return Date.parse(String(s));
19408     },
19409     
19410     /**
19411      * Float sorting
19412      * @param {Mixed} s The value being converted
19413      * @return {Float} The comparison value
19414      */
19415     asFloat : function(s) {
19416         var val = parseFloat(String(s).replace(/,/g, ""));
19417         if(isNaN(val)) val = 0;
19418         return val;
19419     },
19420     
19421     /**
19422      * Integer sorting
19423      * @param {Mixed} s The value being converted
19424      * @return {Number} The comparison value
19425      */
19426     asInt : function(s) {
19427         var val = parseInt(String(s).replace(/,/g, ""));
19428         if(isNaN(val)) val = 0;
19429         return val;
19430     }
19431 };/*
19432  * Based on:
19433  * Ext JS Library 1.1.1
19434  * Copyright(c) 2006-2007, Ext JS, LLC.
19435  *
19436  * Originally Released Under LGPL - original licence link has changed is not relivant.
19437  *
19438  * Fork - LGPL
19439  * <script type="text/javascript">
19440  */
19441
19442 /**
19443 * @class Roo.data.Record
19444  * Instances of this class encapsulate both record <em>definition</em> information, and record
19445  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
19446  * to access Records cached in an {@link Roo.data.Store} object.<br>
19447  * <p>
19448  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
19449  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
19450  * objects.<br>
19451  * <p>
19452  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
19453  * @constructor
19454  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
19455  * {@link #create}. The parameters are the same.
19456  * @param {Array} data An associative Array of data values keyed by the field name.
19457  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
19458  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
19459  * not specified an integer id is generated.
19460  */
19461 Roo.data.Record = function(data, id){
19462     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
19463     this.data = data;
19464 };
19465
19466 /**
19467  * Generate a constructor for a specific record layout.
19468  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
19469  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
19470  * Each field definition object may contain the following properties: <ul>
19471  * <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,
19472  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
19473  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
19474  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
19475  * is being used, then this is a string containing the javascript expression to reference the data relative to 
19476  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
19477  * to the data item relative to the record element. If the mapping expression is the same as the field name,
19478  * this may be omitted.</p></li>
19479  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19480  * <ul><li>auto (Default, implies no conversion)</li>
19481  * <li>string</li>
19482  * <li>int</li>
19483  * <li>float</li>
19484  * <li>boolean</li>
19485  * <li>date</li></ul></p></li>
19486  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19487  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19488  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19489  * by the Reader into an object that will be stored in the Record. It is passed the
19490  * following parameters:<ul>
19491  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19492  * </ul></p></li>
19493  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19494  * </ul>
19495  * <br>usage:<br><pre><code>
19496 var TopicRecord = Roo.data.Record.create(
19497     {name: 'title', mapping: 'topic_title'},
19498     {name: 'author', mapping: 'username'},
19499     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19500     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19501     {name: 'lastPoster', mapping: 'user2'},
19502     {name: 'excerpt', mapping: 'post_text'}
19503 );
19504
19505 var myNewRecord = new TopicRecord({
19506     title: 'Do my job please',
19507     author: 'noobie',
19508     totalPosts: 1,
19509     lastPost: new Date(),
19510     lastPoster: 'Animal',
19511     excerpt: 'No way dude!'
19512 });
19513 myStore.add(myNewRecord);
19514 </code></pre>
19515  * @method create
19516  * @static
19517  */
19518 Roo.data.Record.create = function(o){
19519     var f = function(){
19520         f.superclass.constructor.apply(this, arguments);
19521     };
19522     Roo.extend(f, Roo.data.Record);
19523     var p = f.prototype;
19524     p.fields = new Roo.util.MixedCollection(false, function(field){
19525         return field.name;
19526     });
19527     for(var i = 0, len = o.length; i < len; i++){
19528         p.fields.add(new Roo.data.Field(o[i]));
19529     }
19530     f.getField = function(name){
19531         return p.fields.get(name);  
19532     };
19533     return f;
19534 };
19535
19536 Roo.data.Record.AUTO_ID = 1000;
19537 Roo.data.Record.EDIT = 'edit';
19538 Roo.data.Record.REJECT = 'reject';
19539 Roo.data.Record.COMMIT = 'commit';
19540
19541 Roo.data.Record.prototype = {
19542     /**
19543      * Readonly flag - true if this record has been modified.
19544      * @type Boolean
19545      */
19546     dirty : false,
19547     editing : false,
19548     error: null,
19549     modified: null,
19550
19551     // private
19552     join : function(store){
19553         this.store = store;
19554     },
19555
19556     /**
19557      * Set the named field to the specified value.
19558      * @param {String} name The name of the field to set.
19559      * @param {Object} value The value to set the field to.
19560      */
19561     set : function(name, value){
19562         if(this.data[name] == value){
19563             return;
19564         }
19565         this.dirty = true;
19566         if(!this.modified){
19567             this.modified = {};
19568         }
19569         if(typeof this.modified[name] == 'undefined'){
19570             this.modified[name] = this.data[name];
19571         }
19572         this.data[name] = value;
19573         if(!this.editing && this.store){
19574             this.store.afterEdit(this);
19575         }       
19576     },
19577
19578     /**
19579      * Get the value of the named field.
19580      * @param {String} name The name of the field to get the value of.
19581      * @return {Object} The value of the field.
19582      */
19583     get : function(name){
19584         return this.data[name]; 
19585     },
19586
19587     // private
19588     beginEdit : function(){
19589         this.editing = true;
19590         this.modified = {}; 
19591     },
19592
19593     // private
19594     cancelEdit : function(){
19595         this.editing = false;
19596         delete this.modified;
19597     },
19598
19599     // private
19600     endEdit : function(){
19601         this.editing = false;
19602         if(this.dirty && this.store){
19603             this.store.afterEdit(this);
19604         }
19605     },
19606
19607     /**
19608      * Usually called by the {@link Roo.data.Store} which owns the Record.
19609      * Rejects all changes made to the Record since either creation, or the last commit operation.
19610      * Modified fields are reverted to their original values.
19611      * <p>
19612      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19613      * of reject operations.
19614      */
19615     reject : function(){
19616         var m = this.modified;
19617         for(var n in m){
19618             if(typeof m[n] != "function"){
19619                 this.data[n] = m[n];
19620             }
19621         }
19622         this.dirty = false;
19623         delete this.modified;
19624         this.editing = false;
19625         if(this.store){
19626             this.store.afterReject(this);
19627         }
19628     },
19629
19630     /**
19631      * Usually called by the {@link Roo.data.Store} which owns the Record.
19632      * Commits all changes made to the Record since either creation, or the last commit operation.
19633      * <p>
19634      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19635      * of commit operations.
19636      */
19637     commit : function(){
19638         this.dirty = false;
19639         delete this.modified;
19640         this.editing = false;
19641         if(this.store){
19642             this.store.afterCommit(this);
19643         }
19644     },
19645
19646     // private
19647     hasError : function(){
19648         return this.error != null;
19649     },
19650
19651     // private
19652     clearError : function(){
19653         this.error = null;
19654     },
19655
19656     /**
19657      * Creates a copy of this record.
19658      * @param {String} id (optional) A new record id if you don't want to use this record's id
19659      * @return {Record}
19660      */
19661     copy : function(newId) {
19662         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19663     }
19664 };/*
19665  * Based on:
19666  * Ext JS Library 1.1.1
19667  * Copyright(c) 2006-2007, Ext JS, LLC.
19668  *
19669  * Originally Released Under LGPL - original licence link has changed is not relivant.
19670  *
19671  * Fork - LGPL
19672  * <script type="text/javascript">
19673  */
19674
19675
19676
19677 /**
19678  * @class Roo.data.Store
19679  * @extends Roo.util.Observable
19680  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19681  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19682  * <p>
19683  * 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
19684  * has no knowledge of the format of the data returned by the Proxy.<br>
19685  * <p>
19686  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19687  * instances from the data object. These records are cached and made available through accessor functions.
19688  * @constructor
19689  * Creates a new Store.
19690  * @param {Object} config A config object containing the objects needed for the Store to access data,
19691  * and read the data into Records.
19692  */
19693 Roo.data.Store = function(config){
19694     this.data = new Roo.util.MixedCollection(false);
19695     this.data.getKey = function(o){
19696         return o.id;
19697     };
19698     this.baseParams = {};
19699     // private
19700     this.paramNames = {
19701         "start" : "start",
19702         "limit" : "limit",
19703         "sort" : "sort",
19704         "dir" : "dir",
19705         "multisort" : "_multisort"
19706     };
19707
19708     if(config && config.data){
19709         this.inlineData = config.data;
19710         delete config.data;
19711     }
19712
19713     Roo.apply(this, config);
19714     
19715     if(this.reader){ // reader passed
19716         this.reader = Roo.factory(this.reader, Roo.data);
19717         this.reader.xmodule = this.xmodule || false;
19718         if(!this.recordType){
19719             this.recordType = this.reader.recordType;
19720         }
19721         if(this.reader.onMetaChange){
19722             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19723         }
19724     }
19725
19726     if(this.recordType){
19727         this.fields = this.recordType.prototype.fields;
19728     }
19729     this.modified = [];
19730
19731     this.addEvents({
19732         /**
19733          * @event datachanged
19734          * Fires when the data cache has changed, and a widget which is using this Store
19735          * as a Record cache should refresh its view.
19736          * @param {Store} this
19737          */
19738         datachanged : true,
19739         /**
19740          * @event metachange
19741          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19742          * @param {Store} this
19743          * @param {Object} meta The JSON metadata
19744          */
19745         metachange : true,
19746         /**
19747          * @event add
19748          * Fires when Records have been added to the Store
19749          * @param {Store} this
19750          * @param {Roo.data.Record[]} records The array of Records added
19751          * @param {Number} index The index at which the record(s) were added
19752          */
19753         add : true,
19754         /**
19755          * @event remove
19756          * Fires when a Record has been removed from the Store
19757          * @param {Store} this
19758          * @param {Roo.data.Record} record The Record that was removed
19759          * @param {Number} index The index at which the record was removed
19760          */
19761         remove : true,
19762         /**
19763          * @event update
19764          * Fires when a Record has been updated
19765          * @param {Store} this
19766          * @param {Roo.data.Record} record The Record that was updated
19767          * @param {String} operation The update operation being performed.  Value may be one of:
19768          * <pre><code>
19769  Roo.data.Record.EDIT
19770  Roo.data.Record.REJECT
19771  Roo.data.Record.COMMIT
19772          * </code></pre>
19773          */
19774         update : true,
19775         /**
19776          * @event clear
19777          * Fires when the data cache has been cleared.
19778          * @param {Store} this
19779          */
19780         clear : true,
19781         /**
19782          * @event beforeload
19783          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19784          * the load action will be canceled.
19785          * @param {Store} this
19786          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19787          */
19788         beforeload : true,
19789         /**
19790          * @event beforeloadadd
19791          * Fires after a new set of Records has been loaded.
19792          * @param {Store} this
19793          * @param {Roo.data.Record[]} records The Records that were loaded
19794          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19795          */
19796         beforeloadadd : true,
19797         /**
19798          * @event load
19799          * Fires after a new set of Records has been loaded, before they are added to the store.
19800          * @param {Store} this
19801          * @param {Roo.data.Record[]} records The Records that were loaded
19802          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19803          * @params {Object} return from reader
19804          */
19805         load : true,
19806         /**
19807          * @event loadexception
19808          * Fires if an exception occurs in the Proxy during loading.
19809          * Called with the signature of the Proxy's "loadexception" event.
19810          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19811          * 
19812          * @param {Proxy} 
19813          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19814          * @param {Object} load options 
19815          * @param {Object} jsonData from your request (normally this contains the Exception)
19816          */
19817         loadexception : true
19818     });
19819     
19820     if(this.proxy){
19821         this.proxy = Roo.factory(this.proxy, Roo.data);
19822         this.proxy.xmodule = this.xmodule || false;
19823         this.relayEvents(this.proxy,  ["loadexception"]);
19824     }
19825     this.sortToggle = {};
19826     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19827
19828     Roo.data.Store.superclass.constructor.call(this);
19829
19830     if(this.inlineData){
19831         this.loadData(this.inlineData);
19832         delete this.inlineData;
19833     }
19834 };
19835
19836 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19837      /**
19838     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19839     * without a remote query - used by combo/forms at present.
19840     */
19841     
19842     /**
19843     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19844     */
19845     /**
19846     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19847     */
19848     /**
19849     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19850     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19851     */
19852     /**
19853     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19854     * on any HTTP request
19855     */
19856     /**
19857     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19858     */
19859     /**
19860     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19861     */
19862     multiSort: false,
19863     /**
19864     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19865     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19866     */
19867     remoteSort : false,
19868
19869     /**
19870     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19871      * loaded or when a record is removed. (defaults to false).
19872     */
19873     pruneModifiedRecords : false,
19874
19875     // private
19876     lastOptions : null,
19877
19878     /**
19879      * Add Records to the Store and fires the add event.
19880      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19881      */
19882     add : function(records){
19883         records = [].concat(records);
19884         for(var i = 0, len = records.length; i < len; i++){
19885             records[i].join(this);
19886         }
19887         var index = this.data.length;
19888         this.data.addAll(records);
19889         this.fireEvent("add", this, records, index);
19890     },
19891
19892     /**
19893      * Remove a Record from the Store and fires the remove event.
19894      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19895      */
19896     remove : function(record){
19897         var index = this.data.indexOf(record);
19898         this.data.removeAt(index);
19899         if(this.pruneModifiedRecords){
19900             this.modified.remove(record);
19901         }
19902         this.fireEvent("remove", this, record, index);
19903     },
19904
19905     /**
19906      * Remove all Records from the Store and fires the clear event.
19907      */
19908     removeAll : function(){
19909         this.data.clear();
19910         if(this.pruneModifiedRecords){
19911             this.modified = [];
19912         }
19913         this.fireEvent("clear", this);
19914     },
19915
19916     /**
19917      * Inserts Records to the Store at the given index and fires the add event.
19918      * @param {Number} index The start index at which to insert the passed Records.
19919      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19920      */
19921     insert : function(index, records){
19922         records = [].concat(records);
19923         for(var i = 0, len = records.length; i < len; i++){
19924             this.data.insert(index, records[i]);
19925             records[i].join(this);
19926         }
19927         this.fireEvent("add", this, records, index);
19928     },
19929
19930     /**
19931      * Get the index within the cache of the passed Record.
19932      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19933      * @return {Number} The index of the passed Record. Returns -1 if not found.
19934      */
19935     indexOf : function(record){
19936         return this.data.indexOf(record);
19937     },
19938
19939     /**
19940      * Get the index within the cache of the Record with the passed id.
19941      * @param {String} id The id of the Record to find.
19942      * @return {Number} The index of the Record. Returns -1 if not found.
19943      */
19944     indexOfId : function(id){
19945         return this.data.indexOfKey(id);
19946     },
19947
19948     /**
19949      * Get the Record with the specified id.
19950      * @param {String} id The id of the Record to find.
19951      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19952      */
19953     getById : function(id){
19954         return this.data.key(id);
19955     },
19956
19957     /**
19958      * Get the Record at the specified index.
19959      * @param {Number} index The index of the Record to find.
19960      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19961      */
19962     getAt : function(index){
19963         return this.data.itemAt(index);
19964     },
19965
19966     /**
19967      * Returns a range of Records between specified indices.
19968      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19969      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19970      * @return {Roo.data.Record[]} An array of Records
19971      */
19972     getRange : function(start, end){
19973         return this.data.getRange(start, end);
19974     },
19975
19976     // private
19977     storeOptions : function(o){
19978         o = Roo.apply({}, o);
19979         delete o.callback;
19980         delete o.scope;
19981         this.lastOptions = o;
19982     },
19983
19984     /**
19985      * Loads the Record cache from the configured Proxy using the configured Reader.
19986      * <p>
19987      * If using remote paging, then the first load call must specify the <em>start</em>
19988      * and <em>limit</em> properties in the options.params property to establish the initial
19989      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19990      * <p>
19991      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19992      * and this call will return before the new data has been loaded. Perform any post-processing
19993      * in a callback function, or in a "load" event handler.</strong>
19994      * <p>
19995      * @param {Object} options An object containing properties which control loading options:<ul>
19996      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19997      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19998      * passed the following arguments:<ul>
19999      * <li>r : Roo.data.Record[]</li>
20000      * <li>options: Options object from the load call</li>
20001      * <li>success: Boolean success indicator</li></ul></li>
20002      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
20003      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
20004      * </ul>
20005      */
20006     load : function(options){
20007         options = options || {};
20008         if(this.fireEvent("beforeload", this, options) !== false){
20009             this.storeOptions(options);
20010             var p = Roo.apply(options.params || {}, this.baseParams);
20011             // if meta was not loaded from remote source.. try requesting it.
20012             if (!this.reader.metaFromRemote) {
20013                 p._requestMeta = 1;
20014             }
20015             if(this.sortInfo && this.remoteSort){
20016                 var pn = this.paramNames;
20017                 p[pn["sort"]] = this.sortInfo.field;
20018                 p[pn["dir"]] = this.sortInfo.direction;
20019             }
20020             if (this.multiSort) {
20021                 var pn = this.paramNames;
20022                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
20023             }
20024             
20025             this.proxy.load(p, this.reader, this.loadRecords, this, options);
20026         }
20027     },
20028
20029     /**
20030      * Reloads the Record cache from the configured Proxy using the configured Reader and
20031      * the options from the last load operation performed.
20032      * @param {Object} options (optional) An object containing properties which may override the options
20033      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
20034      * the most recently used options are reused).
20035      */
20036     reload : function(options){
20037         this.load(Roo.applyIf(options||{}, this.lastOptions));
20038     },
20039
20040     // private
20041     // Called as a callback by the Reader during a load operation.
20042     loadRecords : function(o, options, success){
20043         if(!o || success === false){
20044             if(success !== false){
20045                 this.fireEvent("load", this, [], options, o);
20046             }
20047             if(options.callback){
20048                 options.callback.call(options.scope || this, [], options, false);
20049             }
20050             return;
20051         }
20052         // if data returned failure - throw an exception.
20053         if (o.success === false) {
20054             // show a message if no listener is registered.
20055             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
20056                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
20057             }
20058             // loadmask wil be hooked into this..
20059             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
20060             return;
20061         }
20062         var r = o.records, t = o.totalRecords || r.length;
20063         
20064         this.fireEvent("beforeloadadd", this, r, options, o);
20065         
20066         if(!options || options.add !== true){
20067             if(this.pruneModifiedRecords){
20068                 this.modified = [];
20069             }
20070             for(var i = 0, len = r.length; i < len; i++){
20071                 r[i].join(this);
20072             }
20073             if(this.snapshot){
20074                 this.data = this.snapshot;
20075                 delete this.snapshot;
20076             }
20077             this.data.clear();
20078             this.data.addAll(r);
20079             this.totalLength = t;
20080             this.applySort();
20081             this.fireEvent("datachanged", this);
20082         }else{
20083             this.totalLength = Math.max(t, this.data.length+r.length);
20084             this.add(r);
20085         }
20086         this.fireEvent("load", this, r, options, o);
20087         if(options.callback){
20088             options.callback.call(options.scope || this, r, options, true);
20089         }
20090     },
20091
20092
20093     /**
20094      * Loads data from a passed data block. A Reader which understands the format of the data
20095      * must have been configured in the constructor.
20096      * @param {Object} data The data block from which to read the Records.  The format of the data expected
20097      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
20098      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
20099      */
20100     loadData : function(o, append){
20101         var r = this.reader.readRecords(o);
20102         this.loadRecords(r, {add: append}, true);
20103     },
20104
20105     /**
20106      * Gets the number of cached records.
20107      * <p>
20108      * <em>If using paging, this may not be the total size of the dataset. If the data object
20109      * used by the Reader contains the dataset size, then the getTotalCount() function returns
20110      * the data set size</em>
20111      */
20112     getCount : function(){
20113         return this.data.length || 0;
20114     },
20115
20116     /**
20117      * Gets the total number of records in the dataset as returned by the server.
20118      * <p>
20119      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
20120      * the dataset size</em>
20121      */
20122     getTotalCount : function(){
20123         return this.totalLength || 0;
20124     },
20125
20126     /**
20127      * Returns the sort state of the Store as an object with two properties:
20128      * <pre><code>
20129  field {String} The name of the field by which the Records are sorted
20130  direction {String} The sort order, "ASC" or "DESC"
20131      * </code></pre>
20132      */
20133     getSortState : function(){
20134         return this.sortInfo;
20135     },
20136
20137     // private
20138     applySort : function(){
20139         if(this.sortInfo && !this.remoteSort){
20140             var s = this.sortInfo, f = s.field;
20141             var st = this.fields.get(f).sortType;
20142             var fn = function(r1, r2){
20143                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
20144                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
20145             };
20146             this.data.sort(s.direction, fn);
20147             if(this.snapshot && this.snapshot != this.data){
20148                 this.snapshot.sort(s.direction, fn);
20149             }
20150         }
20151     },
20152
20153     /**
20154      * Sets the default sort column and order to be used by the next load operation.
20155      * @param {String} fieldName The name of the field to sort by.
20156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20157      */
20158     setDefaultSort : function(field, dir){
20159         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
20160     },
20161
20162     /**
20163      * Sort the Records.
20164      * If remote sorting is used, the sort is performed on the server, and the cache is
20165      * reloaded. If local sorting is used, the cache is sorted internally.
20166      * @param {String} fieldName The name of the field to sort by.
20167      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20168      */
20169     sort : function(fieldName, dir){
20170         var f = this.fields.get(fieldName);
20171         if(!dir){
20172             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
20173             
20174             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
20175                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
20176             }else{
20177                 dir = f.sortDir;
20178             }
20179         }
20180         this.sortToggle[f.name] = dir;
20181         this.sortInfo = {field: f.name, direction: dir};
20182         if(!this.remoteSort){
20183             this.applySort();
20184             this.fireEvent("datachanged", this);
20185         }else{
20186             this.load(this.lastOptions);
20187         }
20188     },
20189
20190     /**
20191      * Calls the specified function for each of the Records in the cache.
20192      * @param {Function} fn The function to call. The Record is passed as the first parameter.
20193      * Returning <em>false</em> aborts and exits the iteration.
20194      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
20195      */
20196     each : function(fn, scope){
20197         this.data.each(fn, scope);
20198     },
20199
20200     /**
20201      * Gets all records modified since the last commit.  Modified records are persisted across load operations
20202      * (e.g., during paging).
20203      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
20204      */
20205     getModifiedRecords : function(){
20206         return this.modified;
20207     },
20208
20209     // private
20210     createFilterFn : function(property, value, anyMatch){
20211         if(!value.exec){ // not a regex
20212             value = String(value);
20213             if(value.length == 0){
20214                 return false;
20215             }
20216             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
20217         }
20218         return function(r){
20219             return value.test(r.data[property]);
20220         };
20221     },
20222
20223     /**
20224      * Sums the value of <i>property</i> for each record between start and end and returns the result.
20225      * @param {String} property A field on your records
20226      * @param {Number} start The record index to start at (defaults to 0)
20227      * @param {Number} end The last record index to include (defaults to length - 1)
20228      * @return {Number} The sum
20229      */
20230     sum : function(property, start, end){
20231         var rs = this.data.items, v = 0;
20232         start = start || 0;
20233         end = (end || end === 0) ? end : rs.length-1;
20234
20235         for(var i = start; i <= end; i++){
20236             v += (rs[i].data[property] || 0);
20237         }
20238         return v;
20239     },
20240
20241     /**
20242      * Filter the records by a specified property.
20243      * @param {String} field A field on your records
20244      * @param {String/RegExp} value Either a string that the field
20245      * should start with or a RegExp to test against the field
20246      * @param {Boolean} anyMatch True to match any part not just the beginning
20247      */
20248     filter : function(property, value, anyMatch){
20249         var fn = this.createFilterFn(property, value, anyMatch);
20250         return fn ? this.filterBy(fn) : this.clearFilter();
20251     },
20252
20253     /**
20254      * Filter by a function. The specified function will be called with each
20255      * record in this data source. If the function returns true the record is included,
20256      * otherwise it is filtered.
20257      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20258      * @param {Object} scope (optional) The scope of the function (defaults to this)
20259      */
20260     filterBy : function(fn, scope){
20261         this.snapshot = this.snapshot || this.data;
20262         this.data = this.queryBy(fn, scope||this);
20263         this.fireEvent("datachanged", this);
20264     },
20265
20266     /**
20267      * Query the records by a specified property.
20268      * @param {String} field A field on your records
20269      * @param {String/RegExp} value Either a string that the field
20270      * should start with or a RegExp to test against the field
20271      * @param {Boolean} anyMatch True to match any part not just the beginning
20272      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20273      */
20274     query : function(property, value, anyMatch){
20275         var fn = this.createFilterFn(property, value, anyMatch);
20276         return fn ? this.queryBy(fn) : this.data.clone();
20277     },
20278
20279     /**
20280      * Query by a function. The specified function will be called with each
20281      * record in this data source. If the function returns true the record is included
20282      * in the results.
20283      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20284      * @param {Object} scope (optional) The scope of the function (defaults to this)
20285       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20286      **/
20287     queryBy : function(fn, scope){
20288         var data = this.snapshot || this.data;
20289         return data.filterBy(fn, scope||this);
20290     },
20291
20292     /**
20293      * Collects unique values for a particular dataIndex from this store.
20294      * @param {String} dataIndex The property to collect
20295      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
20296      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
20297      * @return {Array} An array of the unique values
20298      **/
20299     collect : function(dataIndex, allowNull, bypassFilter){
20300         var d = (bypassFilter === true && this.snapshot) ?
20301                 this.snapshot.items : this.data.items;
20302         var v, sv, r = [], l = {};
20303         for(var i = 0, len = d.length; i < len; i++){
20304             v = d[i].data[dataIndex];
20305             sv = String(v);
20306             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
20307                 l[sv] = true;
20308                 r[r.length] = v;
20309             }
20310         }
20311         return r;
20312     },
20313
20314     /**
20315      * Revert to a view of the Record cache with no filtering applied.
20316      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
20317      */
20318     clearFilter : function(suppressEvent){
20319         if(this.snapshot && this.snapshot != this.data){
20320             this.data = this.snapshot;
20321             delete this.snapshot;
20322             if(suppressEvent !== true){
20323                 this.fireEvent("datachanged", this);
20324             }
20325         }
20326     },
20327
20328     // private
20329     afterEdit : function(record){
20330         if(this.modified.indexOf(record) == -1){
20331             this.modified.push(record);
20332         }
20333         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
20334     },
20335     
20336     // private
20337     afterReject : function(record){
20338         this.modified.remove(record);
20339         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
20340     },
20341
20342     // private
20343     afterCommit : function(record){
20344         this.modified.remove(record);
20345         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
20346     },
20347
20348     /**
20349      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
20350      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
20351      */
20352     commitChanges : function(){
20353         var m = this.modified.slice(0);
20354         this.modified = [];
20355         for(var i = 0, len = m.length; i < len; i++){
20356             m[i].commit();
20357         }
20358     },
20359
20360     /**
20361      * Cancel outstanding changes on all changed records.
20362      */
20363     rejectChanges : function(){
20364         var m = this.modified.slice(0);
20365         this.modified = [];
20366         for(var i = 0, len = m.length; i < len; i++){
20367             m[i].reject();
20368         }
20369     },
20370
20371     onMetaChange : function(meta, rtype, o){
20372         this.recordType = rtype;
20373         this.fields = rtype.prototype.fields;
20374         delete this.snapshot;
20375         this.sortInfo = meta.sortInfo || this.sortInfo;
20376         this.modified = [];
20377         this.fireEvent('metachange', this, this.reader.meta);
20378     }
20379 });/*
20380  * Based on:
20381  * Ext JS Library 1.1.1
20382  * Copyright(c) 2006-2007, Ext JS, LLC.
20383  *
20384  * Originally Released Under LGPL - original licence link has changed is not relivant.
20385  *
20386  * Fork - LGPL
20387  * <script type="text/javascript">
20388  */
20389
20390 /**
20391  * @class Roo.data.SimpleStore
20392  * @extends Roo.data.Store
20393  * Small helper class to make creating Stores from Array data easier.
20394  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
20395  * @cfg {Array} fields An array of field definition objects, or field name strings.
20396  * @cfg {Array} data The multi-dimensional array of data
20397  * @constructor
20398  * @param {Object} config
20399  */
20400 Roo.data.SimpleStore = function(config){
20401     Roo.data.SimpleStore.superclass.constructor.call(this, {
20402         isLocal : true,
20403         reader: new Roo.data.ArrayReader({
20404                 id: config.id
20405             },
20406             Roo.data.Record.create(config.fields)
20407         ),
20408         proxy : new Roo.data.MemoryProxy(config.data)
20409     });
20410     this.load();
20411 };
20412 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
20413  * Based on:
20414  * Ext JS Library 1.1.1
20415  * Copyright(c) 2006-2007, Ext JS, LLC.
20416  *
20417  * Originally Released Under LGPL - original licence link has changed is not relivant.
20418  *
20419  * Fork - LGPL
20420  * <script type="text/javascript">
20421  */
20422
20423 /**
20424 /**
20425  * @extends Roo.data.Store
20426  * @class Roo.data.JsonStore
20427  * Small helper class to make creating Stores for JSON data easier. <br/>
20428 <pre><code>
20429 var store = new Roo.data.JsonStore({
20430     url: 'get-images.php',
20431     root: 'images',
20432     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
20433 });
20434 </code></pre>
20435  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
20436  * JsonReader and HttpProxy (unless inline data is provided).</b>
20437  * @cfg {Array} fields An array of field definition objects, or field name strings.
20438  * @constructor
20439  * @param {Object} config
20440  */
20441 Roo.data.JsonStore = function(c){
20442     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
20443         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
20444         reader: new Roo.data.JsonReader(c, c.fields)
20445     }));
20446 };
20447 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
20448  * Based on:
20449  * Ext JS Library 1.1.1
20450  * Copyright(c) 2006-2007, Ext JS, LLC.
20451  *
20452  * Originally Released Under LGPL - original licence link has changed is not relivant.
20453  *
20454  * Fork - LGPL
20455  * <script type="text/javascript">
20456  */
20457
20458  
20459 Roo.data.Field = function(config){
20460     if(typeof config == "string"){
20461         config = {name: config};
20462     }
20463     Roo.apply(this, config);
20464     
20465     if(!this.type){
20466         this.type = "auto";
20467     }
20468     
20469     var st = Roo.data.SortTypes;
20470     // named sortTypes are supported, here we look them up
20471     if(typeof this.sortType == "string"){
20472         this.sortType = st[this.sortType];
20473     }
20474     
20475     // set default sortType for strings and dates
20476     if(!this.sortType){
20477         switch(this.type){
20478             case "string":
20479                 this.sortType = st.asUCString;
20480                 break;
20481             case "date":
20482                 this.sortType = st.asDate;
20483                 break;
20484             default:
20485                 this.sortType = st.none;
20486         }
20487     }
20488
20489     // define once
20490     var stripRe = /[\$,%]/g;
20491
20492     // prebuilt conversion function for this field, instead of
20493     // switching every time we're reading a value
20494     if(!this.convert){
20495         var cv, dateFormat = this.dateFormat;
20496         switch(this.type){
20497             case "":
20498             case "auto":
20499             case undefined:
20500                 cv = function(v){ return v; };
20501                 break;
20502             case "string":
20503                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20504                 break;
20505             case "int":
20506                 cv = function(v){
20507                     return v !== undefined && v !== null && v !== '' ?
20508                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20509                     };
20510                 break;
20511             case "float":
20512                 cv = function(v){
20513                     return v !== undefined && v !== null && v !== '' ?
20514                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20515                     };
20516                 break;
20517             case "bool":
20518             case "boolean":
20519                 cv = function(v){ return v === true || v === "true" || v == 1; };
20520                 break;
20521             case "date":
20522                 cv = function(v){
20523                     if(!v){
20524                         return '';
20525                     }
20526                     if(v instanceof Date){
20527                         return v;
20528                     }
20529                     if(dateFormat){
20530                         if(dateFormat == "timestamp"){
20531                             return new Date(v*1000);
20532                         }
20533                         return Date.parseDate(v, dateFormat);
20534                     }
20535                     var parsed = Date.parse(v);
20536                     return parsed ? new Date(parsed) : null;
20537                 };
20538              break;
20539             
20540         }
20541         this.convert = cv;
20542     }
20543 };
20544
20545 Roo.data.Field.prototype = {
20546     dateFormat: null,
20547     defaultValue: "",
20548     mapping: null,
20549     sortType : null,
20550     sortDir : "ASC"
20551 };/*
20552  * Based on:
20553  * Ext JS Library 1.1.1
20554  * Copyright(c) 2006-2007, Ext JS, LLC.
20555  *
20556  * Originally Released Under LGPL - original licence link has changed is not relivant.
20557  *
20558  * Fork - LGPL
20559  * <script type="text/javascript">
20560  */
20561  
20562 // Base class for reading structured data from a data source.  This class is intended to be
20563 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20564
20565 /**
20566  * @class Roo.data.DataReader
20567  * Base class for reading structured data from a data source.  This class is intended to be
20568  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20569  */
20570
20571 Roo.data.DataReader = function(meta, recordType){
20572     
20573     this.meta = meta;
20574     
20575     this.recordType = recordType instanceof Array ? 
20576         Roo.data.Record.create(recordType) : recordType;
20577 };
20578
20579 Roo.data.DataReader.prototype = {
20580      /**
20581      * Create an empty record
20582      * @param {Object} data (optional) - overlay some values
20583      * @return {Roo.data.Record} record created.
20584      */
20585     newRow :  function(d) {
20586         var da =  {};
20587         this.recordType.prototype.fields.each(function(c) {
20588             switch( c.type) {
20589                 case 'int' : da[c.name] = 0; break;
20590                 case 'date' : da[c.name] = new Date(); break;
20591                 case 'float' : da[c.name] = 0.0; break;
20592                 case 'boolean' : da[c.name] = false; break;
20593                 default : da[c.name] = ""; break;
20594             }
20595             
20596         });
20597         return new this.recordType(Roo.apply(da, d));
20598     }
20599     
20600 };/*
20601  * Based on:
20602  * Ext JS Library 1.1.1
20603  * Copyright(c) 2006-2007, Ext JS, LLC.
20604  *
20605  * Originally Released Under LGPL - original licence link has changed is not relivant.
20606  *
20607  * Fork - LGPL
20608  * <script type="text/javascript">
20609  */
20610
20611 /**
20612  * @class Roo.data.DataProxy
20613  * @extends Roo.data.Observable
20614  * This class is an abstract base class for implementations which provide retrieval of
20615  * unformatted data objects.<br>
20616  * <p>
20617  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20618  * (of the appropriate type which knows how to parse the data object) to provide a block of
20619  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20620  * <p>
20621  * Custom implementations must implement the load method as described in
20622  * {@link Roo.data.HttpProxy#load}.
20623  */
20624 Roo.data.DataProxy = function(){
20625     this.addEvents({
20626         /**
20627          * @event beforeload
20628          * Fires before a network request is made to retrieve a data object.
20629          * @param {Object} This DataProxy object.
20630          * @param {Object} params The params parameter to the load function.
20631          */
20632         beforeload : true,
20633         /**
20634          * @event load
20635          * Fires before the load method's callback is called.
20636          * @param {Object} This DataProxy object.
20637          * @param {Object} o The data object.
20638          * @param {Object} arg The callback argument object passed to the load function.
20639          */
20640         load : true,
20641         /**
20642          * @event loadexception
20643          * Fires if an Exception occurs during data retrieval.
20644          * @param {Object} This DataProxy object.
20645          * @param {Object} o The data object.
20646          * @param {Object} arg The callback argument object passed to the load function.
20647          * @param {Object} e The Exception.
20648          */
20649         loadexception : true
20650     });
20651     Roo.data.DataProxy.superclass.constructor.call(this);
20652 };
20653
20654 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20655
20656     /**
20657      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20658      */
20659 /*
20660  * Based on:
20661  * Ext JS Library 1.1.1
20662  * Copyright(c) 2006-2007, Ext JS, LLC.
20663  *
20664  * Originally Released Under LGPL - original licence link has changed is not relivant.
20665  *
20666  * Fork - LGPL
20667  * <script type="text/javascript">
20668  */
20669 /**
20670  * @class Roo.data.MemoryProxy
20671  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20672  * to the Reader when its load method is called.
20673  * @constructor
20674  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20675  */
20676 Roo.data.MemoryProxy = function(data){
20677     if (data.data) {
20678         data = data.data;
20679     }
20680     Roo.data.MemoryProxy.superclass.constructor.call(this);
20681     this.data = data;
20682 };
20683
20684 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20685     /**
20686      * Load data from the requested source (in this case an in-memory
20687      * data object passed to the constructor), read the data object into
20688      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20689      * process that block using the passed callback.
20690      * @param {Object} params This parameter is not used by the MemoryProxy class.
20691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20692      * object into a block of Roo.data.Records.
20693      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20694      * The function must be passed <ul>
20695      * <li>The Record block object</li>
20696      * <li>The "arg" argument from the load function</li>
20697      * <li>A boolean success indicator</li>
20698      * </ul>
20699      * @param {Object} scope The scope in which to call the callback
20700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20701      */
20702     load : function(params, reader, callback, scope, arg){
20703         params = params || {};
20704         var result;
20705         try {
20706             result = reader.readRecords(this.data);
20707         }catch(e){
20708             this.fireEvent("loadexception", this, arg, null, e);
20709             callback.call(scope, null, arg, false);
20710             return;
20711         }
20712         callback.call(scope, result, arg, true);
20713     },
20714     
20715     // private
20716     update : function(params, records){
20717         
20718     }
20719 });/*
20720  * Based on:
20721  * Ext JS Library 1.1.1
20722  * Copyright(c) 2006-2007, Ext JS, LLC.
20723  *
20724  * Originally Released Under LGPL - original licence link has changed is not relivant.
20725  *
20726  * Fork - LGPL
20727  * <script type="text/javascript">
20728  */
20729 /**
20730  * @class Roo.data.HttpProxy
20731  * @extends Roo.data.DataProxy
20732  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20733  * configured to reference a certain URL.<br><br>
20734  * <p>
20735  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20736  * from which the running page was served.<br><br>
20737  * <p>
20738  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20739  * <p>
20740  * Be aware that to enable the browser to parse an XML document, the server must set
20741  * the Content-Type header in the HTTP response to "text/xml".
20742  * @constructor
20743  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20744  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20745  * will be used to make the request.
20746  */
20747 Roo.data.HttpProxy = function(conn){
20748     Roo.data.HttpProxy.superclass.constructor.call(this);
20749     // is conn a conn config or a real conn?
20750     this.conn = conn;
20751     this.useAjax = !conn || !conn.events;
20752   
20753 };
20754
20755 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20756     // thse are take from connection...
20757     
20758     /**
20759      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20760      */
20761     /**
20762      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20763      * extra parameters to each request made by this object. (defaults to undefined)
20764      */
20765     /**
20766      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20767      *  to each request made by this object. (defaults to undefined)
20768      */
20769     /**
20770      * @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)
20771      */
20772     /**
20773      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20774      */
20775      /**
20776      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20777      * @type Boolean
20778      */
20779   
20780
20781     /**
20782      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20783      * @type Boolean
20784      */
20785     /**
20786      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20787      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20788      * a finer-grained basis than the DataProxy events.
20789      */
20790     getConnection : function(){
20791         return this.useAjax ? Roo.Ajax : this.conn;
20792     },
20793
20794     /**
20795      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20796      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20797      * process that block using the passed callback.
20798      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20799      * for the request to the remote server.
20800      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20801      * object into a block of Roo.data.Records.
20802      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20803      * The function must be passed <ul>
20804      * <li>The Record block object</li>
20805      * <li>The "arg" argument from the load function</li>
20806      * <li>A boolean success indicator</li>
20807      * </ul>
20808      * @param {Object} scope The scope in which to call the callback
20809      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20810      */
20811     load : function(params, reader, callback, scope, arg){
20812         if(this.fireEvent("beforeload", this, params) !== false){
20813             var  o = {
20814                 params : params || {},
20815                 request: {
20816                     callback : callback,
20817                     scope : scope,
20818                     arg : arg
20819                 },
20820                 reader: reader,
20821                 callback : this.loadResponse,
20822                 scope: this
20823             };
20824             if(this.useAjax){
20825                 Roo.applyIf(o, this.conn);
20826                 if(this.activeRequest){
20827                     Roo.Ajax.abort(this.activeRequest);
20828                 }
20829                 this.activeRequest = Roo.Ajax.request(o);
20830             }else{
20831                 this.conn.request(o);
20832             }
20833         }else{
20834             callback.call(scope||this, null, arg, false);
20835         }
20836     },
20837
20838     // private
20839     loadResponse : function(o, success, response){
20840         delete this.activeRequest;
20841         if(!success){
20842             this.fireEvent("loadexception", this, o, response);
20843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20844             return;
20845         }
20846         var result;
20847         try {
20848             result = o.reader.read(response);
20849         }catch(e){
20850             this.fireEvent("loadexception", this, o, response, e);
20851             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20852             return;
20853         }
20854         
20855         this.fireEvent("load", this, o, o.request.arg);
20856         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20857     },
20858
20859     // private
20860     update : function(dataSet){
20861
20862     },
20863
20864     // private
20865     updateResponse : function(dataSet){
20866
20867     }
20868 });/*
20869  * Based on:
20870  * Ext JS Library 1.1.1
20871  * Copyright(c) 2006-2007, Ext JS, LLC.
20872  *
20873  * Originally Released Under LGPL - original licence link has changed is not relivant.
20874  *
20875  * Fork - LGPL
20876  * <script type="text/javascript">
20877  */
20878
20879 /**
20880  * @class Roo.data.ScriptTagProxy
20881  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20882  * other than the originating domain of the running page.<br><br>
20883  * <p>
20884  * <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
20885  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20886  * <p>
20887  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20888  * source code that is used as the source inside a &lt;script> tag.<br><br>
20889  * <p>
20890  * In order for the browser to process the returned data, the server must wrap the data object
20891  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20892  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20893  * depending on whether the callback name was passed:
20894  * <p>
20895  * <pre><code>
20896 boolean scriptTag = false;
20897 String cb = request.getParameter("callback");
20898 if (cb != null) {
20899     scriptTag = true;
20900     response.setContentType("text/javascript");
20901 } else {
20902     response.setContentType("application/x-json");
20903 }
20904 Writer out = response.getWriter();
20905 if (scriptTag) {
20906     out.write(cb + "(");
20907 }
20908 out.print(dataBlock.toJsonString());
20909 if (scriptTag) {
20910     out.write(");");
20911 }
20912 </pre></code>
20913  *
20914  * @constructor
20915  * @param {Object} config A configuration object.
20916  */
20917 Roo.data.ScriptTagProxy = function(config){
20918     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20919     Roo.apply(this, config);
20920     this.head = document.getElementsByTagName("head")[0];
20921 };
20922
20923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20924
20925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20926     /**
20927      * @cfg {String} url The URL from which to request the data object.
20928      */
20929     /**
20930      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20931      */
20932     timeout : 30000,
20933     /**
20934      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20935      * the server the name of the callback function set up by the load call to process the returned data object.
20936      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20937      * javascript output which calls this named function passing the data object as its only parameter.
20938      */
20939     callbackParam : "callback",
20940     /**
20941      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20942      * name to the request.
20943      */
20944     nocache : true,
20945
20946     /**
20947      * Load data from the configured URL, read the data object into
20948      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20949      * process that block using the passed callback.
20950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20951      * for the request to the remote server.
20952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20953      * object into a block of Roo.data.Records.
20954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20955      * The function must be passed <ul>
20956      * <li>The Record block object</li>
20957      * <li>The "arg" argument from the load function</li>
20958      * <li>A boolean success indicator</li>
20959      * </ul>
20960      * @param {Object} scope The scope in which to call the callback
20961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20962      */
20963     load : function(params, reader, callback, scope, arg){
20964         if(this.fireEvent("beforeload", this, params) !== false){
20965
20966             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20967
20968             var url = this.url;
20969             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20970             if(this.nocache){
20971                 url += "&_dc=" + (new Date().getTime());
20972             }
20973             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20974             var trans = {
20975                 id : transId,
20976                 cb : "stcCallback"+transId,
20977                 scriptId : "stcScript"+transId,
20978                 params : params,
20979                 arg : arg,
20980                 url : url,
20981                 callback : callback,
20982                 scope : scope,
20983                 reader : reader
20984             };
20985             var conn = this;
20986
20987             window[trans.cb] = function(o){
20988                 conn.handleResponse(o, trans);
20989             };
20990
20991             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20992
20993             if(this.autoAbort !== false){
20994                 this.abort();
20995             }
20996
20997             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20998
20999             var script = document.createElement("script");
21000             script.setAttribute("src", url);
21001             script.setAttribute("type", "text/javascript");
21002             script.setAttribute("id", trans.scriptId);
21003             this.head.appendChild(script);
21004
21005             this.trans = trans;
21006         }else{
21007             callback.call(scope||this, null, arg, false);
21008         }
21009     },
21010
21011     // private
21012     isLoading : function(){
21013         return this.trans ? true : false;
21014     },
21015
21016     /**
21017      * Abort the current server request.
21018      */
21019     abort : function(){
21020         if(this.isLoading()){
21021             this.destroyTrans(this.trans);
21022         }
21023     },
21024
21025     // private
21026     destroyTrans : function(trans, isLoaded){
21027         this.head.removeChild(document.getElementById(trans.scriptId));
21028         clearTimeout(trans.timeoutId);
21029         if(isLoaded){
21030             window[trans.cb] = undefined;
21031             try{
21032                 delete window[trans.cb];
21033             }catch(e){}
21034         }else{
21035             // if hasn't been loaded, wait for load to remove it to prevent script error
21036             window[trans.cb] = function(){
21037                 window[trans.cb] = undefined;
21038                 try{
21039                     delete window[trans.cb];
21040                 }catch(e){}
21041             };
21042         }
21043     },
21044
21045     // private
21046     handleResponse : function(o, trans){
21047         this.trans = false;
21048         this.destroyTrans(trans, true);
21049         var result;
21050         try {
21051             result = trans.reader.readRecords(o);
21052         }catch(e){
21053             this.fireEvent("loadexception", this, o, trans.arg, e);
21054             trans.callback.call(trans.scope||window, null, trans.arg, false);
21055             return;
21056         }
21057         this.fireEvent("load", this, o, trans.arg);
21058         trans.callback.call(trans.scope||window, result, trans.arg, true);
21059     },
21060
21061     // private
21062     handleFailure : function(trans){
21063         this.trans = false;
21064         this.destroyTrans(trans, false);
21065         this.fireEvent("loadexception", this, null, trans.arg);
21066         trans.callback.call(trans.scope||window, null, trans.arg, false);
21067     }
21068 });/*
21069  * Based on:
21070  * Ext JS Library 1.1.1
21071  * Copyright(c) 2006-2007, Ext JS, LLC.
21072  *
21073  * Originally Released Under LGPL - original licence link has changed is not relivant.
21074  *
21075  * Fork - LGPL
21076  * <script type="text/javascript">
21077  */
21078
21079 /**
21080  * @class Roo.data.JsonReader
21081  * @extends Roo.data.DataReader
21082  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
21083  * based on mappings in a provided Roo.data.Record constructor.
21084  * 
21085  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
21086  * in the reply previously. 
21087  * 
21088  * <p>
21089  * Example code:
21090  * <pre><code>
21091 var RecordDef = Roo.data.Record.create([
21092     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21093     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21094 ]);
21095 var myReader = new Roo.data.JsonReader({
21096     totalProperty: "results",    // The property which contains the total dataset size (optional)
21097     root: "rows",                // The property which contains an Array of row objects
21098     id: "id"                     // The property within each row object that provides an ID for the record (optional)
21099 }, RecordDef);
21100 </code></pre>
21101  * <p>
21102  * This would consume a JSON file like this:
21103  * <pre><code>
21104 { 'results': 2, 'rows': [
21105     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
21106     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
21107 }
21108 </code></pre>
21109  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
21110  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21111  * paged from the remote server.
21112  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
21113  * @cfg {String} root name of the property which contains the Array of row objects.
21114  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
21115  * @constructor
21116  * Create a new JsonReader
21117  * @param {Object} meta Metadata configuration options
21118  * @param {Object} recordType Either an Array of field definition objects,
21119  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
21120  */
21121 Roo.data.JsonReader = function(meta, recordType){
21122     
21123     meta = meta || {};
21124     // set some defaults:
21125     Roo.applyIf(meta, {
21126         totalProperty: 'total',
21127         successProperty : 'success',
21128         root : 'data',
21129         id : 'id'
21130     });
21131     
21132     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21133 };
21134 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
21135     
21136     /**
21137      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
21138      * Used by Store query builder to append _requestMeta to params.
21139      * 
21140      */
21141     metaFromRemote : false,
21142     /**
21143      * This method is only used by a DataProxy which has retrieved data from a remote server.
21144      * @param {Object} response The XHR object which contains the JSON data in its responseText.
21145      * @return {Object} data A data block which is used by an Roo.data.Store object as
21146      * a cache of Roo.data.Records.
21147      */
21148     read : function(response){
21149         var json = response.responseText;
21150        
21151         var o = /* eval:var:o */ eval("("+json+")");
21152         if(!o) {
21153             throw {message: "JsonReader.read: Json object not found"};
21154         }
21155         
21156         if(o.metaData){
21157             
21158             delete this.ef;
21159             this.metaFromRemote = true;
21160             this.meta = o.metaData;
21161             this.recordType = Roo.data.Record.create(o.metaData.fields);
21162             this.onMetaChange(this.meta, this.recordType, o);
21163         }
21164         return this.readRecords(o);
21165     },
21166
21167     // private function a store will implement
21168     onMetaChange : function(meta, recordType, o){
21169
21170     },
21171
21172     /**
21173          * @ignore
21174          */
21175     simpleAccess: function(obj, subsc) {
21176         return obj[subsc];
21177     },
21178
21179         /**
21180          * @ignore
21181          */
21182     getJsonAccessor: function(){
21183         var re = /[\[\.]/;
21184         return function(expr) {
21185             try {
21186                 return(re.test(expr))
21187                     ? new Function("obj", "return obj." + expr)
21188                     : function(obj){
21189                         return obj[expr];
21190                     };
21191             } catch(e){}
21192             return Roo.emptyFn;
21193         };
21194     }(),
21195
21196     /**
21197      * Create a data block containing Roo.data.Records from an XML document.
21198      * @param {Object} o An object which contains an Array of row objects in the property specified
21199      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
21200      * which contains the total size of the dataset.
21201      * @return {Object} data A data block which is used by an Roo.data.Store object as
21202      * a cache of Roo.data.Records.
21203      */
21204     readRecords : function(o){
21205         /**
21206          * After any data loads, the raw JSON data is available for further custom processing.
21207          * @type Object
21208          */
21209         this.o = o;
21210         var s = this.meta, Record = this.recordType,
21211             f = Record.prototype.fields, fi = f.items, fl = f.length;
21212
21213 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
21214         if (!this.ef) {
21215             if(s.totalProperty) {
21216                     this.getTotal = this.getJsonAccessor(s.totalProperty);
21217                 }
21218                 if(s.successProperty) {
21219                     this.getSuccess = this.getJsonAccessor(s.successProperty);
21220                 }
21221                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
21222                 if (s.id) {
21223                         var g = this.getJsonAccessor(s.id);
21224                         this.getId = function(rec) {
21225                                 var r = g(rec);
21226                                 return (r === undefined || r === "") ? null : r;
21227                         };
21228                 } else {
21229                         this.getId = function(){return null;};
21230                 }
21231             this.ef = [];
21232             for(var jj = 0; jj < fl; jj++){
21233                 f = fi[jj];
21234                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
21235                 this.ef[jj] = this.getJsonAccessor(map);
21236             }
21237         }
21238
21239         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
21240         if(s.totalProperty){
21241             var vt = parseInt(this.getTotal(o), 10);
21242             if(!isNaN(vt)){
21243                 totalRecords = vt;
21244             }
21245         }
21246         if(s.successProperty){
21247             var vs = this.getSuccess(o);
21248             if(vs === false || vs === 'false'){
21249                 success = false;
21250             }
21251         }
21252         var records = [];
21253             for(var i = 0; i < c; i++){
21254                     var n = root[i];
21255                 var values = {};
21256                 var id = this.getId(n);
21257                 for(var j = 0; j < fl; j++){
21258                     f = fi[j];
21259                 var v = this.ef[j](n);
21260                 if (!f.convert) {
21261                     Roo.log('missing convert for ' + f.name);
21262                     Roo.log(f);
21263                     continue;
21264                 }
21265                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
21266                 }
21267                 var record = new Record(values, id);
21268                 record.json = n;
21269                 records[i] = record;
21270             }
21271             return {
21272             raw : o,
21273                 success : success,
21274                 records : records,
21275                 totalRecords : totalRecords
21276             };
21277     }
21278 });/*
21279  * Based on:
21280  * Ext JS Library 1.1.1
21281  * Copyright(c) 2006-2007, Ext JS, LLC.
21282  *
21283  * Originally Released Under LGPL - original licence link has changed is not relivant.
21284  *
21285  * Fork - LGPL
21286  * <script type="text/javascript">
21287  */
21288
21289 /**
21290  * @class Roo.data.XmlReader
21291  * @extends Roo.data.DataReader
21292  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
21293  * based on mappings in a provided Roo.data.Record constructor.<br><br>
21294  * <p>
21295  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
21296  * header in the HTTP response must be set to "text/xml".</em>
21297  * <p>
21298  * Example code:
21299  * <pre><code>
21300 var RecordDef = Roo.data.Record.create([
21301    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21302    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21303 ]);
21304 var myReader = new Roo.data.XmlReader({
21305    totalRecords: "results", // The element which contains the total dataset size (optional)
21306    record: "row",           // The repeated element which contains row information
21307    id: "id"                 // The element within the row that provides an ID for the record (optional)
21308 }, RecordDef);
21309 </code></pre>
21310  * <p>
21311  * This would consume an XML file like this:
21312  * <pre><code>
21313 &lt;?xml?>
21314 &lt;dataset>
21315  &lt;results>2&lt;/results>
21316  &lt;row>
21317    &lt;id>1&lt;/id>
21318    &lt;name>Bill&lt;/name>
21319    &lt;occupation>Gardener&lt;/occupation>
21320  &lt;/row>
21321  &lt;row>
21322    &lt;id>2&lt;/id>
21323    &lt;name>Ben&lt;/name>
21324    &lt;occupation>Horticulturalist&lt;/occupation>
21325  &lt;/row>
21326 &lt;/dataset>
21327 </code></pre>
21328  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
21329  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21330  * paged from the remote server.
21331  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
21332  * @cfg {String} success The DomQuery path to the success attribute used by forms.
21333  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
21334  * a record identifier value.
21335  * @constructor
21336  * Create a new XmlReader
21337  * @param {Object} meta Metadata configuration options
21338  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
21339  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
21340  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
21341  */
21342 Roo.data.XmlReader = function(meta, recordType){
21343     meta = meta || {};
21344     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21345 };
21346 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
21347     /**
21348      * This method is only used by a DataProxy which has retrieved data from a remote server.
21349          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
21350          * to contain a method called 'responseXML' that returns an XML document object.
21351      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21352      * a cache of Roo.data.Records.
21353      */
21354     read : function(response){
21355         var doc = response.responseXML;
21356         if(!doc) {
21357             throw {message: "XmlReader.read: XML Document not available"};
21358         }
21359         return this.readRecords(doc);
21360     },
21361
21362     /**
21363      * Create a data block containing Roo.data.Records from an XML document.
21364          * @param {Object} doc A parsed XML document.
21365      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21366      * a cache of Roo.data.Records.
21367      */
21368     readRecords : function(doc){
21369         /**
21370          * After any data loads/reads, the raw XML Document is available for further custom processing.
21371          * @type XMLDocument
21372          */
21373         this.xmlData = doc;
21374         var root = doc.documentElement || doc;
21375         var q = Roo.DomQuery;
21376         var recordType = this.recordType, fields = recordType.prototype.fields;
21377         var sid = this.meta.id;
21378         var totalRecords = 0, success = true;
21379         if(this.meta.totalRecords){
21380             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
21381         }
21382         
21383         if(this.meta.success){
21384             var sv = q.selectValue(this.meta.success, root, true);
21385             success = sv !== false && sv !== 'false';
21386         }
21387         var records = [];
21388         var ns = q.select(this.meta.record, root);
21389         for(var i = 0, len = ns.length; i < len; i++) {
21390                 var n = ns[i];
21391                 var values = {};
21392                 var id = sid ? q.selectValue(sid, n) : undefined;
21393                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21394                     var f = fields.items[j];
21395                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
21396                     v = f.convert(v);
21397                     values[f.name] = v;
21398                 }
21399                 var record = new recordType(values, id);
21400                 record.node = n;
21401                 records[records.length] = record;
21402             }
21403
21404             return {
21405                 success : success,
21406                 records : records,
21407                 totalRecords : totalRecords || records.length
21408             };
21409     }
21410 });/*
21411  * Based on:
21412  * Ext JS Library 1.1.1
21413  * Copyright(c) 2006-2007, Ext JS, LLC.
21414  *
21415  * Originally Released Under LGPL - original licence link has changed is not relivant.
21416  *
21417  * Fork - LGPL
21418  * <script type="text/javascript">
21419  */
21420
21421 /**
21422  * @class Roo.data.ArrayReader
21423  * @extends Roo.data.DataReader
21424  * Data reader class to create an Array of Roo.data.Record objects from an Array.
21425  * Each element of that Array represents a row of data fields. The
21426  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
21427  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
21428  * <p>
21429  * Example code:.
21430  * <pre><code>
21431 var RecordDef = Roo.data.Record.create([
21432     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
21433     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
21434 ]);
21435 var myReader = new Roo.data.ArrayReader({
21436     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
21437 }, RecordDef);
21438 </code></pre>
21439  * <p>
21440  * This would consume an Array like this:
21441  * <pre><code>
21442 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
21443   </code></pre>
21444  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
21445  * @constructor
21446  * Create a new JsonReader
21447  * @param {Object} meta Metadata configuration options.
21448  * @param {Object} recordType Either an Array of field definition objects
21449  * as specified to {@link Roo.data.Record#create},
21450  * or an {@link Roo.data.Record} object
21451  * created using {@link Roo.data.Record#create}.
21452  */
21453 Roo.data.ArrayReader = function(meta, recordType){
21454     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
21455 };
21456
21457 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
21458     /**
21459      * Create a data block containing Roo.data.Records from an XML document.
21460      * @param {Object} o An Array of row objects which represents the dataset.
21461      * @return {Object} data A data block which is used by an Roo.data.Store object as
21462      * a cache of Roo.data.Records.
21463      */
21464     readRecords : function(o){
21465         var sid = this.meta ? this.meta.id : null;
21466         var recordType = this.recordType, fields = recordType.prototype.fields;
21467         var records = [];
21468         var root = o;
21469             for(var i = 0; i < root.length; i++){
21470                     var n = root[i];
21471                 var values = {};
21472                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
21473                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21474                 var f = fields.items[j];
21475                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
21476                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
21477                 v = f.convert(v);
21478                 values[f.name] = v;
21479             }
21480                 var record = new recordType(values, id);
21481                 record.json = n;
21482                 records[records.length] = record;
21483             }
21484             return {
21485                 records : records,
21486                 totalRecords : records.length
21487             };
21488     }
21489 });/*
21490  * Based on:
21491  * Ext JS Library 1.1.1
21492  * Copyright(c) 2006-2007, Ext JS, LLC.
21493  *
21494  * Originally Released Under LGPL - original licence link has changed is not relivant.
21495  *
21496  * Fork - LGPL
21497  * <script type="text/javascript">
21498  */
21499
21500
21501 /**
21502  * @class Roo.data.Tree
21503  * @extends Roo.util.Observable
21504  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21505  * in the tree have most standard DOM functionality.
21506  * @constructor
21507  * @param {Node} root (optional) The root node
21508  */
21509 Roo.data.Tree = function(root){
21510    this.nodeHash = {};
21511    /**
21512     * The root node for this tree
21513     * @type Node
21514     */
21515    this.root = null;
21516    if(root){
21517        this.setRootNode(root);
21518    }
21519    this.addEvents({
21520        /**
21521         * @event append
21522         * Fires when a new child node is appended to a node in this tree.
21523         * @param {Tree} tree The owner tree
21524         * @param {Node} parent The parent node
21525         * @param {Node} node The newly appended node
21526         * @param {Number} index The index of the newly appended node
21527         */
21528        "append" : true,
21529        /**
21530         * @event remove
21531         * Fires when a child node is removed from a node in this tree.
21532         * @param {Tree} tree The owner tree
21533         * @param {Node} parent The parent node
21534         * @param {Node} node The child node removed
21535         */
21536        "remove" : true,
21537        /**
21538         * @event move
21539         * Fires when a node is moved to a new location in the tree
21540         * @param {Tree} tree The owner tree
21541         * @param {Node} node The node moved
21542         * @param {Node} oldParent The old parent of this node
21543         * @param {Node} newParent The new parent of this node
21544         * @param {Number} index The index it was moved to
21545         */
21546        "move" : true,
21547        /**
21548         * @event insert
21549         * Fires when a new child node is inserted in a node in this tree.
21550         * @param {Tree} tree The owner tree
21551         * @param {Node} parent The parent node
21552         * @param {Node} node The child node inserted
21553         * @param {Node} refNode The child node the node was inserted before
21554         */
21555        "insert" : true,
21556        /**
21557         * @event beforeappend
21558         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21559         * @param {Tree} tree The owner tree
21560         * @param {Node} parent The parent node
21561         * @param {Node} node The child node to be appended
21562         */
21563        "beforeappend" : true,
21564        /**
21565         * @event beforeremove
21566         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21567         * @param {Tree} tree The owner tree
21568         * @param {Node} parent The parent node
21569         * @param {Node} node The child node to be removed
21570         */
21571        "beforeremove" : true,
21572        /**
21573         * @event beforemove
21574         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21575         * @param {Tree} tree The owner tree
21576         * @param {Node} node The node being moved
21577         * @param {Node} oldParent The parent of the node
21578         * @param {Node} newParent The new parent the node is moving to
21579         * @param {Number} index The index it is being moved to
21580         */
21581        "beforemove" : true,
21582        /**
21583         * @event beforeinsert
21584         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21585         * @param {Tree} tree The owner tree
21586         * @param {Node} parent The parent node
21587         * @param {Node} node The child node to be inserted
21588         * @param {Node} refNode The child node the node is being inserted before
21589         */
21590        "beforeinsert" : true
21591    });
21592
21593     Roo.data.Tree.superclass.constructor.call(this);
21594 };
21595
21596 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21597     pathSeparator: "/",
21598
21599     proxyNodeEvent : function(){
21600         return this.fireEvent.apply(this, arguments);
21601     },
21602
21603     /**
21604      * Returns the root node for this tree.
21605      * @return {Node}
21606      */
21607     getRootNode : function(){
21608         return this.root;
21609     },
21610
21611     /**
21612      * Sets the root node for this tree.
21613      * @param {Node} node
21614      * @return {Node}
21615      */
21616     setRootNode : function(node){
21617         this.root = node;
21618         node.ownerTree = this;
21619         node.isRoot = true;
21620         this.registerNode(node);
21621         return node;
21622     },
21623
21624     /**
21625      * Gets a node in this tree by its id.
21626      * @param {String} id
21627      * @return {Node}
21628      */
21629     getNodeById : function(id){
21630         return this.nodeHash[id];
21631     },
21632
21633     registerNode : function(node){
21634         this.nodeHash[node.id] = node;
21635     },
21636
21637     unregisterNode : function(node){
21638         delete this.nodeHash[node.id];
21639     },
21640
21641     toString : function(){
21642         return "[Tree"+(this.id?" "+this.id:"")+"]";
21643     }
21644 });
21645
21646 /**
21647  * @class Roo.data.Node
21648  * @extends Roo.util.Observable
21649  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21650  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21651  * @constructor
21652  * @param {Object} attributes The attributes/config for the node
21653  */
21654 Roo.data.Node = function(attributes){
21655     /**
21656      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21657      * @type {Object}
21658      */
21659     this.attributes = attributes || {};
21660     this.leaf = this.attributes.leaf;
21661     /**
21662      * The node id. @type String
21663      */
21664     this.id = this.attributes.id;
21665     if(!this.id){
21666         this.id = Roo.id(null, "ynode-");
21667         this.attributes.id = this.id;
21668     }
21669      
21670     
21671     /**
21672      * All child nodes of this node. @type Array
21673      */
21674     this.childNodes = [];
21675     if(!this.childNodes.indexOf){ // indexOf is a must
21676         this.childNodes.indexOf = function(o){
21677             for(var i = 0, len = this.length; i < len; i++){
21678                 if(this[i] == o) {
21679                     return i;
21680                 }
21681             }
21682             return -1;
21683         };
21684     }
21685     /**
21686      * The parent node for this node. @type Node
21687      */
21688     this.parentNode = null;
21689     /**
21690      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21691      */
21692     this.firstChild = null;
21693     /**
21694      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21695      */
21696     this.lastChild = null;
21697     /**
21698      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21699      */
21700     this.previousSibling = null;
21701     /**
21702      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21703      */
21704     this.nextSibling = null;
21705
21706     this.addEvents({
21707        /**
21708         * @event append
21709         * Fires when a new child node is appended
21710         * @param {Tree} tree The owner tree
21711         * @param {Node} this This node
21712         * @param {Node} node The newly appended node
21713         * @param {Number} index The index of the newly appended node
21714         */
21715        "append" : true,
21716        /**
21717         * @event remove
21718         * Fires when a child node is removed
21719         * @param {Tree} tree The owner tree
21720         * @param {Node} this This node
21721         * @param {Node} node The removed node
21722         */
21723        "remove" : true,
21724        /**
21725         * @event move
21726         * Fires when this node is moved to a new location in the tree
21727         * @param {Tree} tree The owner tree
21728         * @param {Node} this This node
21729         * @param {Node} oldParent The old parent of this node
21730         * @param {Node} newParent The new parent of this node
21731         * @param {Number} index The index it was moved to
21732         */
21733        "move" : true,
21734        /**
21735         * @event insert
21736         * Fires when a new child node is inserted.
21737         * @param {Tree} tree The owner tree
21738         * @param {Node} this This node
21739         * @param {Node} node The child node inserted
21740         * @param {Node} refNode The child node the node was inserted before
21741         */
21742        "insert" : true,
21743        /**
21744         * @event beforeappend
21745         * Fires before a new child is appended, return false to cancel the append.
21746         * @param {Tree} tree The owner tree
21747         * @param {Node} this This node
21748         * @param {Node} node The child node to be appended
21749         */
21750        "beforeappend" : true,
21751        /**
21752         * @event beforeremove
21753         * Fires before a child is removed, return false to cancel the remove.
21754         * @param {Tree} tree The owner tree
21755         * @param {Node} this This node
21756         * @param {Node} node The child node to be removed
21757         */
21758        "beforeremove" : true,
21759        /**
21760         * @event beforemove
21761         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21762         * @param {Tree} tree The owner tree
21763         * @param {Node} this This node
21764         * @param {Node} oldParent The parent of this node
21765         * @param {Node} newParent The new parent this node is moving to
21766         * @param {Number} index The index it is being moved to
21767         */
21768        "beforemove" : true,
21769        /**
21770         * @event beforeinsert
21771         * Fires before a new child is inserted, return false to cancel the insert.
21772         * @param {Tree} tree The owner tree
21773         * @param {Node} this This node
21774         * @param {Node} node The child node to be inserted
21775         * @param {Node} refNode The child node the node is being inserted before
21776         */
21777        "beforeinsert" : true
21778    });
21779     this.listeners = this.attributes.listeners;
21780     Roo.data.Node.superclass.constructor.call(this);
21781 };
21782
21783 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21784     fireEvent : function(evtName){
21785         // first do standard event for this node
21786         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21787             return false;
21788         }
21789         // then bubble it up to the tree if the event wasn't cancelled
21790         var ot = this.getOwnerTree();
21791         if(ot){
21792             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21793                 return false;
21794             }
21795         }
21796         return true;
21797     },
21798
21799     /**
21800      * Returns true if this node is a leaf
21801      * @return {Boolean}
21802      */
21803     isLeaf : function(){
21804         return this.leaf === true;
21805     },
21806
21807     // private
21808     setFirstChild : function(node){
21809         this.firstChild = node;
21810     },
21811
21812     //private
21813     setLastChild : function(node){
21814         this.lastChild = node;
21815     },
21816
21817
21818     /**
21819      * Returns true if this node is the last child of its parent
21820      * @return {Boolean}
21821      */
21822     isLast : function(){
21823        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21824     },
21825
21826     /**
21827      * Returns true if this node is the first child of its parent
21828      * @return {Boolean}
21829      */
21830     isFirst : function(){
21831        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21832     },
21833
21834     hasChildNodes : function(){
21835         return !this.isLeaf() && this.childNodes.length > 0;
21836     },
21837
21838     /**
21839      * Insert node(s) as the last child node of this node.
21840      * @param {Node/Array} node The node or Array of nodes to append
21841      * @return {Node} The appended node if single append, or null if an array was passed
21842      */
21843     appendChild : function(node){
21844         var multi = false;
21845         if(node instanceof Array){
21846             multi = node;
21847         }else if(arguments.length > 1){
21848             multi = arguments;
21849         }
21850         // if passed an array or multiple args do them one by one
21851         if(multi){
21852             for(var i = 0, len = multi.length; i < len; i++) {
21853                 this.appendChild(multi[i]);
21854             }
21855         }else{
21856             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21857                 return false;
21858             }
21859             var index = this.childNodes.length;
21860             var oldParent = node.parentNode;
21861             // it's a move, make sure we move it cleanly
21862             if(oldParent){
21863                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21864                     return false;
21865                 }
21866                 oldParent.removeChild(node);
21867             }
21868             index = this.childNodes.length;
21869             if(index == 0){
21870                 this.setFirstChild(node);
21871             }
21872             this.childNodes.push(node);
21873             node.parentNode = this;
21874             var ps = this.childNodes[index-1];
21875             if(ps){
21876                 node.previousSibling = ps;
21877                 ps.nextSibling = node;
21878             }else{
21879                 node.previousSibling = null;
21880             }
21881             node.nextSibling = null;
21882             this.setLastChild(node);
21883             node.setOwnerTree(this.getOwnerTree());
21884             this.fireEvent("append", this.ownerTree, this, node, index);
21885             if(oldParent){
21886                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21887             }
21888             return node;
21889         }
21890     },
21891
21892     /**
21893      * Removes a child node from this node.
21894      * @param {Node} node The node to remove
21895      * @return {Node} The removed node
21896      */
21897     removeChild : function(node){
21898         var index = this.childNodes.indexOf(node);
21899         if(index == -1){
21900             return false;
21901         }
21902         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21903             return false;
21904         }
21905
21906         // remove it from childNodes collection
21907         this.childNodes.splice(index, 1);
21908
21909         // update siblings
21910         if(node.previousSibling){
21911             node.previousSibling.nextSibling = node.nextSibling;
21912         }
21913         if(node.nextSibling){
21914             node.nextSibling.previousSibling = node.previousSibling;
21915         }
21916
21917         // update child refs
21918         if(this.firstChild == node){
21919             this.setFirstChild(node.nextSibling);
21920         }
21921         if(this.lastChild == node){
21922             this.setLastChild(node.previousSibling);
21923         }
21924
21925         node.setOwnerTree(null);
21926         // clear any references from the node
21927         node.parentNode = null;
21928         node.previousSibling = null;
21929         node.nextSibling = null;
21930         this.fireEvent("remove", this.ownerTree, this, node);
21931         return node;
21932     },
21933
21934     /**
21935      * Inserts the first node before the second node in this nodes childNodes collection.
21936      * @param {Node} node The node to insert
21937      * @param {Node} refNode The node to insert before (if null the node is appended)
21938      * @return {Node} The inserted node
21939      */
21940     insertBefore : function(node, refNode){
21941         if(!refNode){ // like standard Dom, refNode can be null for append
21942             return this.appendChild(node);
21943         }
21944         // nothing to do
21945         if(node == refNode){
21946             return false;
21947         }
21948
21949         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21950             return false;
21951         }
21952         var index = this.childNodes.indexOf(refNode);
21953         var oldParent = node.parentNode;
21954         var refIndex = index;
21955
21956         // when moving internally, indexes will change after remove
21957         if(oldParent == this && this.childNodes.indexOf(node) < index){
21958             refIndex--;
21959         }
21960
21961         // it's a move, make sure we move it cleanly
21962         if(oldParent){
21963             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21964                 return false;
21965             }
21966             oldParent.removeChild(node);
21967         }
21968         if(refIndex == 0){
21969             this.setFirstChild(node);
21970         }
21971         this.childNodes.splice(refIndex, 0, node);
21972         node.parentNode = this;
21973         var ps = this.childNodes[refIndex-1];
21974         if(ps){
21975             node.previousSibling = ps;
21976             ps.nextSibling = node;
21977         }else{
21978             node.previousSibling = null;
21979         }
21980         node.nextSibling = refNode;
21981         refNode.previousSibling = node;
21982         node.setOwnerTree(this.getOwnerTree());
21983         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21984         if(oldParent){
21985             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21986         }
21987         return node;
21988     },
21989
21990     /**
21991      * Returns the child node at the specified index.
21992      * @param {Number} index
21993      * @return {Node}
21994      */
21995     item : function(index){
21996         return this.childNodes[index];
21997     },
21998
21999     /**
22000      * Replaces one child node in this node with another.
22001      * @param {Node} newChild The replacement node
22002      * @param {Node} oldChild The node to replace
22003      * @return {Node} The replaced node
22004      */
22005     replaceChild : function(newChild, oldChild){
22006         this.insertBefore(newChild, oldChild);
22007         this.removeChild(oldChild);
22008         return oldChild;
22009     },
22010
22011     /**
22012      * Returns the index of a child node
22013      * @param {Node} node
22014      * @return {Number} The index of the node or -1 if it was not found
22015      */
22016     indexOf : function(child){
22017         return this.childNodes.indexOf(child);
22018     },
22019
22020     /**
22021      * Returns the tree this node is in.
22022      * @return {Tree}
22023      */
22024     getOwnerTree : function(){
22025         // if it doesn't have one, look for one
22026         if(!this.ownerTree){
22027             var p = this;
22028             while(p){
22029                 if(p.ownerTree){
22030                     this.ownerTree = p.ownerTree;
22031                     break;
22032                 }
22033                 p = p.parentNode;
22034             }
22035         }
22036         return this.ownerTree;
22037     },
22038
22039     /**
22040      * Returns depth of this node (the root node has a depth of 0)
22041      * @return {Number}
22042      */
22043     getDepth : function(){
22044         var depth = 0;
22045         var p = this;
22046         while(p.parentNode){
22047             ++depth;
22048             p = p.parentNode;
22049         }
22050         return depth;
22051     },
22052
22053     // private
22054     setOwnerTree : function(tree){
22055         // if it's move, we need to update everyone
22056         if(tree != this.ownerTree){
22057             if(this.ownerTree){
22058                 this.ownerTree.unregisterNode(this);
22059             }
22060             this.ownerTree = tree;
22061             var cs = this.childNodes;
22062             for(var i = 0, len = cs.length; i < len; i++) {
22063                 cs[i].setOwnerTree(tree);
22064             }
22065             if(tree){
22066                 tree.registerNode(this);
22067             }
22068         }
22069     },
22070
22071     /**
22072      * Returns the path for this node. The path can be used to expand or select this node programmatically.
22073      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
22074      * @return {String} The path
22075      */
22076     getPath : function(attr){
22077         attr = attr || "id";
22078         var p = this.parentNode;
22079         var b = [this.attributes[attr]];
22080         while(p){
22081             b.unshift(p.attributes[attr]);
22082             p = p.parentNode;
22083         }
22084         var sep = this.getOwnerTree().pathSeparator;
22085         return sep + b.join(sep);
22086     },
22087
22088     /**
22089      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22090      * function call will be the scope provided or the current node. The arguments to the function
22091      * will be the args provided or the current node. If the function returns false at any point,
22092      * the bubble is stopped.
22093      * @param {Function} fn The function to call
22094      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22095      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22096      */
22097     bubble : function(fn, scope, args){
22098         var p = this;
22099         while(p){
22100             if(fn.call(scope || p, args || p) === false){
22101                 break;
22102             }
22103             p = p.parentNode;
22104         }
22105     },
22106
22107     /**
22108      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22109      * function call will be the scope provided or the current node. The arguments to the function
22110      * will be the args provided or the current node. If the function returns false at any point,
22111      * the cascade is stopped on that branch.
22112      * @param {Function} fn The function to call
22113      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22114      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22115      */
22116     cascade : function(fn, scope, args){
22117         if(fn.call(scope || this, args || this) !== false){
22118             var cs = this.childNodes;
22119             for(var i = 0, len = cs.length; i < len; i++) {
22120                 cs[i].cascade(fn, scope, args);
22121             }
22122         }
22123     },
22124
22125     /**
22126      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
22127      * function call will be the scope provided or the current node. The arguments to the function
22128      * will be the args provided or the current node. If the function returns false at any point,
22129      * the iteration stops.
22130      * @param {Function} fn The function to call
22131      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22132      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22133      */
22134     eachChild : function(fn, scope, args){
22135         var cs = this.childNodes;
22136         for(var i = 0, len = cs.length; i < len; i++) {
22137                 if(fn.call(scope || this, args || cs[i]) === false){
22138                     break;
22139                 }
22140         }
22141     },
22142
22143     /**
22144      * Finds the first child that has the attribute with the specified value.
22145      * @param {String} attribute The attribute name
22146      * @param {Mixed} value The value to search for
22147      * @return {Node} The found child or null if none was found
22148      */
22149     findChild : function(attribute, value){
22150         var cs = this.childNodes;
22151         for(var i = 0, len = cs.length; i < len; i++) {
22152                 if(cs[i].attributes[attribute] == value){
22153                     return cs[i];
22154                 }
22155         }
22156         return null;
22157     },
22158
22159     /**
22160      * Finds the first child by a custom function. The child matches if the function passed
22161      * returns true.
22162      * @param {Function} fn
22163      * @param {Object} scope (optional)
22164      * @return {Node} The found child or null if none was found
22165      */
22166     findChildBy : function(fn, scope){
22167         var cs = this.childNodes;
22168         for(var i = 0, len = cs.length; i < len; i++) {
22169                 if(fn.call(scope||cs[i], cs[i]) === true){
22170                     return cs[i];
22171                 }
22172         }
22173         return null;
22174     },
22175
22176     /**
22177      * Sorts this nodes children using the supplied sort function
22178      * @param {Function} fn
22179      * @param {Object} scope (optional)
22180      */
22181     sort : function(fn, scope){
22182         var cs = this.childNodes;
22183         var len = cs.length;
22184         if(len > 0){
22185             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
22186             cs.sort(sortFn);
22187             for(var i = 0; i < len; i++){
22188                 var n = cs[i];
22189                 n.previousSibling = cs[i-1];
22190                 n.nextSibling = cs[i+1];
22191                 if(i == 0){
22192                     this.setFirstChild(n);
22193                 }
22194                 if(i == len-1){
22195                     this.setLastChild(n);
22196                 }
22197             }
22198         }
22199     },
22200
22201     /**
22202      * Returns true if this node is an ancestor (at any point) of the passed node.
22203      * @param {Node} node
22204      * @return {Boolean}
22205      */
22206     contains : function(node){
22207         return node.isAncestor(this);
22208     },
22209
22210     /**
22211      * Returns true if the passed node is an ancestor (at any point) of this node.
22212      * @param {Node} node
22213      * @return {Boolean}
22214      */
22215     isAncestor : function(node){
22216         var p = this.parentNode;
22217         while(p){
22218             if(p == node){
22219                 return true;
22220             }
22221             p = p.parentNode;
22222         }
22223         return false;
22224     },
22225
22226     toString : function(){
22227         return "[Node"+(this.id?" "+this.id:"")+"]";
22228     }
22229 });/*
22230  * Based on:
22231  * Ext JS Library 1.1.1
22232  * Copyright(c) 2006-2007, Ext JS, LLC.
22233  *
22234  * Originally Released Under LGPL - original licence link has changed is not relivant.
22235  *
22236  * Fork - LGPL
22237  * <script type="text/javascript">
22238  */
22239  
22240
22241 /**
22242  * @class Roo.ComponentMgr
22243  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
22244  * @singleton
22245  */
22246 Roo.ComponentMgr = function(){
22247     var all = new Roo.util.MixedCollection();
22248
22249     return {
22250         /**
22251          * Registers a component.
22252          * @param {Roo.Component} c The component
22253          */
22254         register : function(c){
22255             all.add(c);
22256         },
22257
22258         /**
22259          * Unregisters a component.
22260          * @param {Roo.Component} c The component
22261          */
22262         unregister : function(c){
22263             all.remove(c);
22264         },
22265
22266         /**
22267          * Returns a component by id
22268          * @param {String} id The component id
22269          */
22270         get : function(id){
22271             return all.get(id);
22272         },
22273
22274         /**
22275          * Registers a function that will be called when a specified component is added to ComponentMgr
22276          * @param {String} id The component id
22277          * @param {Funtction} fn The callback function
22278          * @param {Object} scope The scope of the callback
22279          */
22280         onAvailable : function(id, fn, scope){
22281             all.on("add", function(index, o){
22282                 if(o.id == id){
22283                     fn.call(scope || o, o);
22284                     all.un("add", fn, scope);
22285                 }
22286             });
22287         }
22288     };
22289 }();/*
22290  * Based on:
22291  * Ext JS Library 1.1.1
22292  * Copyright(c) 2006-2007, Ext JS, LLC.
22293  *
22294  * Originally Released Under LGPL - original licence link has changed is not relivant.
22295  *
22296  * Fork - LGPL
22297  * <script type="text/javascript">
22298  */
22299  
22300 /**
22301  * @class Roo.Component
22302  * @extends Roo.util.Observable
22303  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
22304  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
22305  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
22306  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
22307  * All visual components (widgets) that require rendering into a layout should subclass Component.
22308  * @constructor
22309  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
22310  * 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
22311  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
22312  */
22313 Roo.Component = function(config){
22314     config = config || {};
22315     if(config.tagName || config.dom || typeof config == "string"){ // element object
22316         config = {el: config, id: config.id || config};
22317     }
22318     this.initialConfig = config;
22319
22320     Roo.apply(this, config);
22321     this.addEvents({
22322         /**
22323          * @event disable
22324          * Fires after the component is disabled.
22325              * @param {Roo.Component} this
22326              */
22327         disable : true,
22328         /**
22329          * @event enable
22330          * Fires after the component is enabled.
22331              * @param {Roo.Component} this
22332              */
22333         enable : true,
22334         /**
22335          * @event beforeshow
22336          * Fires before the component is shown.  Return false to stop the show.
22337              * @param {Roo.Component} this
22338              */
22339         beforeshow : true,
22340         /**
22341          * @event show
22342          * Fires after the component is shown.
22343              * @param {Roo.Component} this
22344              */
22345         show : true,
22346         /**
22347          * @event beforehide
22348          * Fires before the component is hidden. Return false to stop the hide.
22349              * @param {Roo.Component} this
22350              */
22351         beforehide : true,
22352         /**
22353          * @event hide
22354          * Fires after the component is hidden.
22355              * @param {Roo.Component} this
22356              */
22357         hide : true,
22358         /**
22359          * @event beforerender
22360          * Fires before the component is rendered. Return false to stop the render.
22361              * @param {Roo.Component} this
22362              */
22363         beforerender : true,
22364         /**
22365          * @event render
22366          * Fires after the component is rendered.
22367              * @param {Roo.Component} this
22368              */
22369         render : true,
22370         /**
22371          * @event beforedestroy
22372          * Fires before the component is destroyed. Return false to stop the destroy.
22373              * @param {Roo.Component} this
22374              */
22375         beforedestroy : true,
22376         /**
22377          * @event destroy
22378          * Fires after the component is destroyed.
22379              * @param {Roo.Component} this
22380              */
22381         destroy : true
22382     });
22383     if(!this.id){
22384         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
22385     }
22386     Roo.ComponentMgr.register(this);
22387     Roo.Component.superclass.constructor.call(this);
22388     this.initComponent();
22389     if(this.renderTo){ // not supported by all components yet. use at your own risk!
22390         this.render(this.renderTo);
22391         delete this.renderTo;
22392     }
22393 };
22394
22395 /** @private */
22396 Roo.Component.AUTO_ID = 1000;
22397
22398 Roo.extend(Roo.Component, Roo.util.Observable, {
22399     /**
22400      * @scope Roo.Component.prototype
22401      * @type {Boolean}
22402      * true if this component is hidden. Read-only.
22403      */
22404     hidden : false,
22405     /**
22406      * @type {Boolean}
22407      * true if this component is disabled. Read-only.
22408      */
22409     disabled : false,
22410     /**
22411      * @type {Boolean}
22412      * true if this component has been rendered. Read-only.
22413      */
22414     rendered : false,
22415     
22416     /** @cfg {String} disableClass
22417      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
22418      */
22419     disabledClass : "x-item-disabled",
22420         /** @cfg {Boolean} allowDomMove
22421          * Whether the component can move the Dom node when rendering (defaults to true).
22422          */
22423     allowDomMove : true,
22424     /** @cfg {String} hideMode
22425      * How this component should hidden. Supported values are
22426      * "visibility" (css visibility), "offsets" (negative offset position) and
22427      * "display" (css display) - defaults to "display".
22428      */
22429     hideMode: 'display',
22430
22431     /** @private */
22432     ctype : "Roo.Component",
22433
22434     /**
22435      * @cfg {String} actionMode 
22436      * which property holds the element that used for  hide() / show() / disable() / enable()
22437      * default is 'el' 
22438      */
22439     actionMode : "el",
22440
22441     /** @private */
22442     getActionEl : function(){
22443         return this[this.actionMode];
22444     },
22445
22446     initComponent : Roo.emptyFn,
22447     /**
22448      * If this is a lazy rendering component, render it to its container element.
22449      * @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.
22450      */
22451     render : function(container, position){
22452         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
22453             if(!container && this.el){
22454                 this.el = Roo.get(this.el);
22455                 container = this.el.dom.parentNode;
22456                 this.allowDomMove = false;
22457             }
22458             this.container = Roo.get(container);
22459             this.rendered = true;
22460             if(position !== undefined){
22461                 if(typeof position == 'number'){
22462                     position = this.container.dom.childNodes[position];
22463                 }else{
22464                     position = Roo.getDom(position);
22465                 }
22466             }
22467             this.onRender(this.container, position || null);
22468             if(this.cls){
22469                 this.el.addClass(this.cls);
22470                 delete this.cls;
22471             }
22472             if(this.style){
22473                 this.el.applyStyles(this.style);
22474                 delete this.style;
22475             }
22476             this.fireEvent("render", this);
22477             this.afterRender(this.container);
22478             if(this.hidden){
22479                 this.hide();
22480             }
22481             if(this.disabled){
22482                 this.disable();
22483             }
22484         }
22485         return this;
22486     },
22487
22488     /** @private */
22489     // default function is not really useful
22490     onRender : function(ct, position){
22491         if(this.el){
22492             this.el = Roo.get(this.el);
22493             if(this.allowDomMove !== false){
22494                 ct.dom.insertBefore(this.el.dom, position);
22495             }
22496         }
22497     },
22498
22499     /** @private */
22500     getAutoCreate : function(){
22501         var cfg = typeof this.autoCreate == "object" ?
22502                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22503         if(this.id && !cfg.id){
22504             cfg.id = this.id;
22505         }
22506         return cfg;
22507     },
22508
22509     /** @private */
22510     afterRender : Roo.emptyFn,
22511
22512     /**
22513      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22514      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22515      */
22516     destroy : function(){
22517         if(this.fireEvent("beforedestroy", this) !== false){
22518             this.purgeListeners();
22519             this.beforeDestroy();
22520             if(this.rendered){
22521                 this.el.removeAllListeners();
22522                 this.el.remove();
22523                 if(this.actionMode == "container"){
22524                     this.container.remove();
22525                 }
22526             }
22527             this.onDestroy();
22528             Roo.ComponentMgr.unregister(this);
22529             this.fireEvent("destroy", this);
22530         }
22531     },
22532
22533         /** @private */
22534     beforeDestroy : function(){
22535
22536     },
22537
22538         /** @private */
22539         onDestroy : function(){
22540
22541     },
22542
22543     /**
22544      * Returns the underlying {@link Roo.Element}.
22545      * @return {Roo.Element} The element
22546      */
22547     getEl : function(){
22548         return this.el;
22549     },
22550
22551     /**
22552      * Returns the id of this component.
22553      * @return {String}
22554      */
22555     getId : function(){
22556         return this.id;
22557     },
22558
22559     /**
22560      * Try to focus this component.
22561      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22562      * @return {Roo.Component} this
22563      */
22564     focus : function(selectText){
22565         if(this.rendered){
22566             this.el.focus();
22567             if(selectText === true){
22568                 this.el.dom.select();
22569             }
22570         }
22571         return this;
22572     },
22573
22574     /** @private */
22575     blur : function(){
22576         if(this.rendered){
22577             this.el.blur();
22578         }
22579         return this;
22580     },
22581
22582     /**
22583      * Disable this component.
22584      * @return {Roo.Component} this
22585      */
22586     disable : function(){
22587         if(this.rendered){
22588             this.onDisable();
22589         }
22590         this.disabled = true;
22591         this.fireEvent("disable", this);
22592         return this;
22593     },
22594
22595         // private
22596     onDisable : function(){
22597         this.getActionEl().addClass(this.disabledClass);
22598         this.el.dom.disabled = true;
22599     },
22600
22601     /**
22602      * Enable this component.
22603      * @return {Roo.Component} this
22604      */
22605     enable : function(){
22606         if(this.rendered){
22607             this.onEnable();
22608         }
22609         this.disabled = false;
22610         this.fireEvent("enable", this);
22611         return this;
22612     },
22613
22614         // private
22615     onEnable : function(){
22616         this.getActionEl().removeClass(this.disabledClass);
22617         this.el.dom.disabled = false;
22618     },
22619
22620     /**
22621      * Convenience function for setting disabled/enabled by boolean.
22622      * @param {Boolean} disabled
22623      */
22624     setDisabled : function(disabled){
22625         this[disabled ? "disable" : "enable"]();
22626     },
22627
22628     /**
22629      * Show this component.
22630      * @return {Roo.Component} this
22631      */
22632     show: function(){
22633         if(this.fireEvent("beforeshow", this) !== false){
22634             this.hidden = false;
22635             if(this.rendered){
22636                 this.onShow();
22637             }
22638             this.fireEvent("show", this);
22639         }
22640         return this;
22641     },
22642
22643     // private
22644     onShow : function(){
22645         var ae = this.getActionEl();
22646         if(this.hideMode == 'visibility'){
22647             ae.dom.style.visibility = "visible";
22648         }else if(this.hideMode == 'offsets'){
22649             ae.removeClass('x-hidden');
22650         }else{
22651             ae.dom.style.display = "";
22652         }
22653     },
22654
22655     /**
22656      * Hide this component.
22657      * @return {Roo.Component} this
22658      */
22659     hide: function(){
22660         if(this.fireEvent("beforehide", this) !== false){
22661             this.hidden = true;
22662             if(this.rendered){
22663                 this.onHide();
22664             }
22665             this.fireEvent("hide", this);
22666         }
22667         return this;
22668     },
22669
22670     // private
22671     onHide : function(){
22672         var ae = this.getActionEl();
22673         if(this.hideMode == 'visibility'){
22674             ae.dom.style.visibility = "hidden";
22675         }else if(this.hideMode == 'offsets'){
22676             ae.addClass('x-hidden');
22677         }else{
22678             ae.dom.style.display = "none";
22679         }
22680     },
22681
22682     /**
22683      * Convenience function to hide or show this component by boolean.
22684      * @param {Boolean} visible True to show, false to hide
22685      * @return {Roo.Component} this
22686      */
22687     setVisible: function(visible){
22688         if(visible) {
22689             this.show();
22690         }else{
22691             this.hide();
22692         }
22693         return this;
22694     },
22695
22696     /**
22697      * Returns true if this component is visible.
22698      */
22699     isVisible : function(){
22700         return this.getActionEl().isVisible();
22701     },
22702
22703     cloneConfig : function(overrides){
22704         overrides = overrides || {};
22705         var id = overrides.id || Roo.id();
22706         var cfg = Roo.applyIf(overrides, this.initialConfig);
22707         cfg.id = id; // prevent dup id
22708         return new this.constructor(cfg);
22709     }
22710 });/*
22711  * Based on:
22712  * Ext JS Library 1.1.1
22713  * Copyright(c) 2006-2007, Ext JS, LLC.
22714  *
22715  * Originally Released Under LGPL - original licence link has changed is not relivant.
22716  *
22717  * Fork - LGPL
22718  * <script type="text/javascript">
22719  */
22720  (function(){ 
22721 /**
22722  * @class Roo.Layer
22723  * @extends Roo.Element
22724  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22725  * automatic maintaining of shadow/shim positions.
22726  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22727  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22728  * you can pass a string with a CSS class name. False turns off the shadow.
22729  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22730  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22731  * @cfg {String} cls CSS class to add to the element
22732  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22733  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22734  * @constructor
22735  * @param {Object} config An object with config options.
22736  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22737  */
22738
22739 Roo.Layer = function(config, existingEl){
22740     config = config || {};
22741     var dh = Roo.DomHelper;
22742     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22743     if(existingEl){
22744         this.dom = Roo.getDom(existingEl);
22745     }
22746     if(!this.dom){
22747         var o = config.dh || {tag: "div", cls: "x-layer"};
22748         this.dom = dh.append(pel, o);
22749     }
22750     if(config.cls){
22751         this.addClass(config.cls);
22752     }
22753     this.constrain = config.constrain !== false;
22754     this.visibilityMode = Roo.Element.VISIBILITY;
22755     if(config.id){
22756         this.id = this.dom.id = config.id;
22757     }else{
22758         this.id = Roo.id(this.dom);
22759     }
22760     this.zindex = config.zindex || this.getZIndex();
22761     this.position("absolute", this.zindex);
22762     if(config.shadow){
22763         this.shadowOffset = config.shadowOffset || 4;
22764         this.shadow = new Roo.Shadow({
22765             offset : this.shadowOffset,
22766             mode : config.shadow
22767         });
22768     }else{
22769         this.shadowOffset = 0;
22770     }
22771     this.useShim = config.shim !== false && Roo.useShims;
22772     this.useDisplay = config.useDisplay;
22773     this.hide();
22774 };
22775
22776 var supr = Roo.Element.prototype;
22777
22778 // shims are shared among layer to keep from having 100 iframes
22779 var shims = [];
22780
22781 Roo.extend(Roo.Layer, Roo.Element, {
22782
22783     getZIndex : function(){
22784         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22785     },
22786
22787     getShim : function(){
22788         if(!this.useShim){
22789             return null;
22790         }
22791         if(this.shim){
22792             return this.shim;
22793         }
22794         var shim = shims.shift();
22795         if(!shim){
22796             shim = this.createShim();
22797             shim.enableDisplayMode('block');
22798             shim.dom.style.display = 'none';
22799             shim.dom.style.visibility = 'visible';
22800         }
22801         var pn = this.dom.parentNode;
22802         if(shim.dom.parentNode != pn){
22803             pn.insertBefore(shim.dom, this.dom);
22804         }
22805         shim.setStyle('z-index', this.getZIndex()-2);
22806         this.shim = shim;
22807         return shim;
22808     },
22809
22810     hideShim : function(){
22811         if(this.shim){
22812             this.shim.setDisplayed(false);
22813             shims.push(this.shim);
22814             delete this.shim;
22815         }
22816     },
22817
22818     disableShadow : function(){
22819         if(this.shadow){
22820             this.shadowDisabled = true;
22821             this.shadow.hide();
22822             this.lastShadowOffset = this.shadowOffset;
22823             this.shadowOffset = 0;
22824         }
22825     },
22826
22827     enableShadow : function(show){
22828         if(this.shadow){
22829             this.shadowDisabled = false;
22830             this.shadowOffset = this.lastShadowOffset;
22831             delete this.lastShadowOffset;
22832             if(show){
22833                 this.sync(true);
22834             }
22835         }
22836     },
22837
22838     // private
22839     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22840     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22841     sync : function(doShow){
22842         var sw = this.shadow;
22843         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22844             var sh = this.getShim();
22845
22846             var w = this.getWidth(),
22847                 h = this.getHeight();
22848
22849             var l = this.getLeft(true),
22850                 t = this.getTop(true);
22851
22852             if(sw && !this.shadowDisabled){
22853                 if(doShow && !sw.isVisible()){
22854                     sw.show(this);
22855                 }else{
22856                     sw.realign(l, t, w, h);
22857                 }
22858                 if(sh){
22859                     if(doShow){
22860                        sh.show();
22861                     }
22862                     // fit the shim behind the shadow, so it is shimmed too
22863                     var a = sw.adjusts, s = sh.dom.style;
22864                     s.left = (Math.min(l, l+a.l))+"px";
22865                     s.top = (Math.min(t, t+a.t))+"px";
22866                     s.width = (w+a.w)+"px";
22867                     s.height = (h+a.h)+"px";
22868                 }
22869             }else if(sh){
22870                 if(doShow){
22871                    sh.show();
22872                 }
22873                 sh.setSize(w, h);
22874                 sh.setLeftTop(l, t);
22875             }
22876             
22877         }
22878     },
22879
22880     // private
22881     destroy : function(){
22882         this.hideShim();
22883         if(this.shadow){
22884             this.shadow.hide();
22885         }
22886         this.removeAllListeners();
22887         var pn = this.dom.parentNode;
22888         if(pn){
22889             pn.removeChild(this.dom);
22890         }
22891         Roo.Element.uncache(this.id);
22892     },
22893
22894     remove : function(){
22895         this.destroy();
22896     },
22897
22898     // private
22899     beginUpdate : function(){
22900         this.updating = true;
22901     },
22902
22903     // private
22904     endUpdate : function(){
22905         this.updating = false;
22906         this.sync(true);
22907     },
22908
22909     // private
22910     hideUnders : function(negOffset){
22911         if(this.shadow){
22912             this.shadow.hide();
22913         }
22914         this.hideShim();
22915     },
22916
22917     // private
22918     constrainXY : function(){
22919         if(this.constrain){
22920             var vw = Roo.lib.Dom.getViewWidth(),
22921                 vh = Roo.lib.Dom.getViewHeight();
22922             var s = Roo.get(document).getScroll();
22923
22924             var xy = this.getXY();
22925             var x = xy[0], y = xy[1];   
22926             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22927             // only move it if it needs it
22928             var moved = false;
22929             // first validate right/bottom
22930             if((x + w) > vw+s.left){
22931                 x = vw - w - this.shadowOffset;
22932                 moved = true;
22933             }
22934             if((y + h) > vh+s.top){
22935                 y = vh - h - this.shadowOffset;
22936                 moved = true;
22937             }
22938             // then make sure top/left isn't negative
22939             if(x < s.left){
22940                 x = s.left;
22941                 moved = true;
22942             }
22943             if(y < s.top){
22944                 y = s.top;
22945                 moved = true;
22946             }
22947             if(moved){
22948                 if(this.avoidY){
22949                     var ay = this.avoidY;
22950                     if(y <= ay && (y+h) >= ay){
22951                         y = ay-h-5;   
22952                     }
22953                 }
22954                 xy = [x, y];
22955                 this.storeXY(xy);
22956                 supr.setXY.call(this, xy);
22957                 this.sync();
22958             }
22959         }
22960     },
22961
22962     isVisible : function(){
22963         return this.visible;    
22964     },
22965
22966     // private
22967     showAction : function(){
22968         this.visible = true; // track visibility to prevent getStyle calls
22969         if(this.useDisplay === true){
22970             this.setDisplayed("");
22971         }else if(this.lastXY){
22972             supr.setXY.call(this, this.lastXY);
22973         }else if(this.lastLT){
22974             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22975         }
22976     },
22977
22978     // private
22979     hideAction : function(){
22980         this.visible = false;
22981         if(this.useDisplay === true){
22982             this.setDisplayed(false);
22983         }else{
22984             this.setLeftTop(-10000,-10000);
22985         }
22986     },
22987
22988     // overridden Element method
22989     setVisible : function(v, a, d, c, e){
22990         if(v){
22991             this.showAction();
22992         }
22993         if(a && v){
22994             var cb = function(){
22995                 this.sync(true);
22996                 if(c){
22997                     c();
22998                 }
22999             }.createDelegate(this);
23000             supr.setVisible.call(this, true, true, d, cb, e);
23001         }else{
23002             if(!v){
23003                 this.hideUnders(true);
23004             }
23005             var cb = c;
23006             if(a){
23007                 cb = function(){
23008                     this.hideAction();
23009                     if(c){
23010                         c();
23011                     }
23012                 }.createDelegate(this);
23013             }
23014             supr.setVisible.call(this, v, a, d, cb, e);
23015             if(v){
23016                 this.sync(true);
23017             }else if(!a){
23018                 this.hideAction();
23019             }
23020         }
23021     },
23022
23023     storeXY : function(xy){
23024         delete this.lastLT;
23025         this.lastXY = xy;
23026     },
23027
23028     storeLeftTop : function(left, top){
23029         delete this.lastXY;
23030         this.lastLT = [left, top];
23031     },
23032
23033     // private
23034     beforeFx : function(){
23035         this.beforeAction();
23036         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23037     },
23038
23039     // private
23040     afterFx : function(){
23041         Roo.Layer.superclass.afterFx.apply(this, arguments);
23042         this.sync(this.isVisible());
23043     },
23044
23045     // private
23046     beforeAction : function(){
23047         if(!this.updating && this.shadow){
23048             this.shadow.hide();
23049         }
23050     },
23051
23052     // overridden Element method
23053     setLeft : function(left){
23054         this.storeLeftTop(left, this.getTop(true));
23055         supr.setLeft.apply(this, arguments);
23056         this.sync();
23057     },
23058
23059     setTop : function(top){
23060         this.storeLeftTop(this.getLeft(true), top);
23061         supr.setTop.apply(this, arguments);
23062         this.sync();
23063     },
23064
23065     setLeftTop : function(left, top){
23066         this.storeLeftTop(left, top);
23067         supr.setLeftTop.apply(this, arguments);
23068         this.sync();
23069     },
23070
23071     setXY : function(xy, a, d, c, e){
23072         this.fixDisplay();
23073         this.beforeAction();
23074         this.storeXY(xy);
23075         var cb = this.createCB(c);
23076         supr.setXY.call(this, xy, a, d, cb, e);
23077         if(!a){
23078             cb();
23079         }
23080     },
23081
23082     // private
23083     createCB : function(c){
23084         var el = this;
23085         return function(){
23086             el.constrainXY();
23087             el.sync(true);
23088             if(c){
23089                 c();
23090             }
23091         };
23092     },
23093
23094     // overridden Element method
23095     setX : function(x, a, d, c, e){
23096         this.setXY([x, this.getY()], a, d, c, e);
23097     },
23098
23099     // overridden Element method
23100     setY : function(y, a, d, c, e){
23101         this.setXY([this.getX(), y], a, d, c, e);
23102     },
23103
23104     // overridden Element method
23105     setSize : function(w, h, a, d, c, e){
23106         this.beforeAction();
23107         var cb = this.createCB(c);
23108         supr.setSize.call(this, w, h, a, d, cb, e);
23109         if(!a){
23110             cb();
23111         }
23112     },
23113
23114     // overridden Element method
23115     setWidth : function(w, a, d, c, e){
23116         this.beforeAction();
23117         var cb = this.createCB(c);
23118         supr.setWidth.call(this, w, a, d, cb, e);
23119         if(!a){
23120             cb();
23121         }
23122     },
23123
23124     // overridden Element method
23125     setHeight : function(h, a, d, c, e){
23126         this.beforeAction();
23127         var cb = this.createCB(c);
23128         supr.setHeight.call(this, h, a, d, cb, e);
23129         if(!a){
23130             cb();
23131         }
23132     },
23133
23134     // overridden Element method
23135     setBounds : function(x, y, w, h, a, d, c, e){
23136         this.beforeAction();
23137         var cb = this.createCB(c);
23138         if(!a){
23139             this.storeXY([x, y]);
23140             supr.setXY.call(this, [x, y]);
23141             supr.setSize.call(this, w, h, a, d, cb, e);
23142             cb();
23143         }else{
23144             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23145         }
23146         return this;
23147     },
23148     
23149     /**
23150      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
23151      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
23152      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
23153      * @param {Number} zindex The new z-index to set
23154      * @return {this} The Layer
23155      */
23156     setZIndex : function(zindex){
23157         this.zindex = zindex;
23158         this.setStyle("z-index", zindex + 2);
23159         if(this.shadow){
23160             this.shadow.setZIndex(zindex + 1);
23161         }
23162         if(this.shim){
23163             this.shim.setStyle("z-index", zindex);
23164         }
23165     }
23166 });
23167 })();/*
23168  * Based on:
23169  * Ext JS Library 1.1.1
23170  * Copyright(c) 2006-2007, Ext JS, LLC.
23171  *
23172  * Originally Released Under LGPL - original licence link has changed is not relivant.
23173  *
23174  * Fork - LGPL
23175  * <script type="text/javascript">
23176  */
23177
23178
23179 /**
23180  * @class Roo.Shadow
23181  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
23182  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
23183  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
23184  * @constructor
23185  * Create a new Shadow
23186  * @param {Object} config The config object
23187  */
23188 Roo.Shadow = function(config){
23189     Roo.apply(this, config);
23190     if(typeof this.mode != "string"){
23191         this.mode = this.defaultMode;
23192     }
23193     var o = this.offset, a = {h: 0};
23194     var rad = Math.floor(this.offset/2);
23195     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
23196         case "drop":
23197             a.w = 0;
23198             a.l = a.t = o;
23199             a.t -= 1;
23200             if(Roo.isIE){
23201                 a.l -= this.offset + rad;
23202                 a.t -= this.offset + rad;
23203                 a.w -= rad;
23204                 a.h -= rad;
23205                 a.t += 1;
23206             }
23207         break;
23208         case "sides":
23209             a.w = (o*2);
23210             a.l = -o;
23211             a.t = o-1;
23212             if(Roo.isIE){
23213                 a.l -= (this.offset - rad);
23214                 a.t -= this.offset + rad;
23215                 a.l += 1;
23216                 a.w -= (this.offset - rad)*2;
23217                 a.w -= rad + 1;
23218                 a.h -= 1;
23219             }
23220         break;
23221         case "frame":
23222             a.w = a.h = (o*2);
23223             a.l = a.t = -o;
23224             a.t += 1;
23225             a.h -= 2;
23226             if(Roo.isIE){
23227                 a.l -= (this.offset - rad);
23228                 a.t -= (this.offset - rad);
23229                 a.l += 1;
23230                 a.w -= (this.offset + rad + 1);
23231                 a.h -= (this.offset + rad);
23232                 a.h += 1;
23233             }
23234         break;
23235     };
23236
23237     this.adjusts = a;
23238 };
23239
23240 Roo.Shadow.prototype = {
23241     /**
23242      * @cfg {String} mode
23243      * The shadow display mode.  Supports the following options:<br />
23244      * sides: Shadow displays on both sides and bottom only<br />
23245      * frame: Shadow displays equally on all four sides<br />
23246      * drop: Traditional bottom-right drop shadow (default)
23247      */
23248     /**
23249      * @cfg {String} offset
23250      * The number of pixels to offset the shadow from the element (defaults to 4)
23251      */
23252     offset: 4,
23253
23254     // private
23255     defaultMode: "drop",
23256
23257     /**
23258      * Displays the shadow under the target element
23259      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
23260      */
23261     show : function(target){
23262         target = Roo.get(target);
23263         if(!this.el){
23264             this.el = Roo.Shadow.Pool.pull();
23265             if(this.el.dom.nextSibling != target.dom){
23266                 this.el.insertBefore(target);
23267             }
23268         }
23269         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
23270         if(Roo.isIE){
23271             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
23272         }
23273         this.realign(
23274             target.getLeft(true),
23275             target.getTop(true),
23276             target.getWidth(),
23277             target.getHeight()
23278         );
23279         this.el.dom.style.display = "block";
23280     },
23281
23282     /**
23283      * Returns true if the shadow is visible, else false
23284      */
23285     isVisible : function(){
23286         return this.el ? true : false;  
23287     },
23288
23289     /**
23290      * Direct alignment when values are already available. Show must be called at least once before
23291      * calling this method to ensure it is initialized.
23292      * @param {Number} left The target element left position
23293      * @param {Number} top The target element top position
23294      * @param {Number} width The target element width
23295      * @param {Number} height The target element height
23296      */
23297     realign : function(l, t, w, h){
23298         if(!this.el){
23299             return;
23300         }
23301         var a = this.adjusts, d = this.el.dom, s = d.style;
23302         var iea = 0;
23303         s.left = (l+a.l)+"px";
23304         s.top = (t+a.t)+"px";
23305         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
23306  
23307         if(s.width != sws || s.height != shs){
23308             s.width = sws;
23309             s.height = shs;
23310             if(!Roo.isIE){
23311                 var cn = d.childNodes;
23312                 var sww = Math.max(0, (sw-12))+"px";
23313                 cn[0].childNodes[1].style.width = sww;
23314                 cn[1].childNodes[1].style.width = sww;
23315                 cn[2].childNodes[1].style.width = sww;
23316                 cn[1].style.height = Math.max(0, (sh-12))+"px";
23317             }
23318         }
23319     },
23320
23321     /**
23322      * Hides this shadow
23323      */
23324     hide : function(){
23325         if(this.el){
23326             this.el.dom.style.display = "none";
23327             Roo.Shadow.Pool.push(this.el);
23328             delete this.el;
23329         }
23330     },
23331
23332     /**
23333      * Adjust the z-index of this shadow
23334      * @param {Number} zindex The new z-index
23335      */
23336     setZIndex : function(z){
23337         this.zIndex = z;
23338         if(this.el){
23339             this.el.setStyle("z-index", z);
23340         }
23341     }
23342 };
23343
23344 // Private utility class that manages the internal Shadow cache
23345 Roo.Shadow.Pool = function(){
23346     var p = [];
23347     var markup = Roo.isIE ?
23348                  '<div class="x-ie-shadow"></div>' :
23349                  '<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>';
23350     return {
23351         pull : function(){
23352             var sh = p.shift();
23353             if(!sh){
23354                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
23355                 sh.autoBoxAdjust = false;
23356             }
23357             return sh;
23358         },
23359
23360         push : function(sh){
23361             p.push(sh);
23362         }
23363     };
23364 }();/*
23365  * Based on:
23366  * Ext JS Library 1.1.1
23367  * Copyright(c) 2006-2007, Ext JS, LLC.
23368  *
23369  * Originally Released Under LGPL - original licence link has changed is not relivant.
23370  *
23371  * Fork - LGPL
23372  * <script type="text/javascript">
23373  */
23374
23375 /**
23376  * @class Roo.BoxComponent
23377  * @extends Roo.Component
23378  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
23379  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
23380  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
23381  * layout containers.
23382  * @constructor
23383  * @param {Roo.Element/String/Object} config The configuration options.
23384  */
23385 Roo.BoxComponent = function(config){
23386     Roo.Component.call(this, config);
23387     this.addEvents({
23388         /**
23389          * @event resize
23390          * Fires after the component is resized.
23391              * @param {Roo.Component} this
23392              * @param {Number} adjWidth The box-adjusted width that was set
23393              * @param {Number} adjHeight The box-adjusted height that was set
23394              * @param {Number} rawWidth The width that was originally specified
23395              * @param {Number} rawHeight The height that was originally specified
23396              */
23397         resize : true,
23398         /**
23399          * @event move
23400          * Fires after the component is moved.
23401              * @param {Roo.Component} this
23402              * @param {Number} x The new x position
23403              * @param {Number} y The new y position
23404              */
23405         move : true
23406     });
23407 };
23408
23409 Roo.extend(Roo.BoxComponent, Roo.Component, {
23410     // private, set in afterRender to signify that the component has been rendered
23411     boxReady : false,
23412     // private, used to defer height settings to subclasses
23413     deferHeight: false,
23414     /** @cfg {Number} width
23415      * width (optional) size of component
23416      */
23417      /** @cfg {Number} height
23418      * height (optional) size of component
23419      */
23420      
23421     /**
23422      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
23423      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
23424      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
23425      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
23426      * @return {Roo.BoxComponent} this
23427      */
23428     setSize : function(w, h){
23429         // support for standard size objects
23430         if(typeof w == 'object'){
23431             h = w.height;
23432             w = w.width;
23433         }
23434         // not rendered
23435         if(!this.boxReady){
23436             this.width = w;
23437             this.height = h;
23438             return this;
23439         }
23440
23441         // prevent recalcs when not needed
23442         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
23443             return this;
23444         }
23445         this.lastSize = {width: w, height: h};
23446
23447         var adj = this.adjustSize(w, h);
23448         var aw = adj.width, ah = adj.height;
23449         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
23450             var rz = this.getResizeEl();
23451             if(!this.deferHeight && aw !== undefined && ah !== undefined){
23452                 rz.setSize(aw, ah);
23453             }else if(!this.deferHeight && ah !== undefined){
23454                 rz.setHeight(ah);
23455             }else if(aw !== undefined){
23456                 rz.setWidth(aw);
23457             }
23458             this.onResize(aw, ah, w, h);
23459             this.fireEvent('resize', this, aw, ah, w, h);
23460         }
23461         return this;
23462     },
23463
23464     /**
23465      * Gets the current size of the component's underlying element.
23466      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
23467      */
23468     getSize : function(){
23469         return this.el.getSize();
23470     },
23471
23472     /**
23473      * Gets the current XY position of the component's underlying element.
23474      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23475      * @return {Array} The XY position of the element (e.g., [100, 200])
23476      */
23477     getPosition : function(local){
23478         if(local === true){
23479             return [this.el.getLeft(true), this.el.getTop(true)];
23480         }
23481         return this.xy || this.el.getXY();
23482     },
23483
23484     /**
23485      * Gets the current box measurements of the component's underlying element.
23486      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23487      * @returns {Object} box An object in the format {x, y, width, height}
23488      */
23489     getBox : function(local){
23490         var s = this.el.getSize();
23491         if(local){
23492             s.x = this.el.getLeft(true);
23493             s.y = this.el.getTop(true);
23494         }else{
23495             var xy = this.xy || this.el.getXY();
23496             s.x = xy[0];
23497             s.y = xy[1];
23498         }
23499         return s;
23500     },
23501
23502     /**
23503      * Sets the current box measurements of the component's underlying element.
23504      * @param {Object} box An object in the format {x, y, width, height}
23505      * @returns {Roo.BoxComponent} this
23506      */
23507     updateBox : function(box){
23508         this.setSize(box.width, box.height);
23509         this.setPagePosition(box.x, box.y);
23510         return this;
23511     },
23512
23513     // protected
23514     getResizeEl : function(){
23515         return this.resizeEl || this.el;
23516     },
23517
23518     // protected
23519     getPositionEl : function(){
23520         return this.positionEl || this.el;
23521     },
23522
23523     /**
23524      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23525      * This method fires the move event.
23526      * @param {Number} left The new left
23527      * @param {Number} top The new top
23528      * @returns {Roo.BoxComponent} this
23529      */
23530     setPosition : function(x, y){
23531         this.x = x;
23532         this.y = y;
23533         if(!this.boxReady){
23534             return this;
23535         }
23536         var adj = this.adjustPosition(x, y);
23537         var ax = adj.x, ay = adj.y;
23538
23539         var el = this.getPositionEl();
23540         if(ax !== undefined || ay !== undefined){
23541             if(ax !== undefined && ay !== undefined){
23542                 el.setLeftTop(ax, ay);
23543             }else if(ax !== undefined){
23544                 el.setLeft(ax);
23545             }else if(ay !== undefined){
23546                 el.setTop(ay);
23547             }
23548             this.onPosition(ax, ay);
23549             this.fireEvent('move', this, ax, ay);
23550         }
23551         return this;
23552     },
23553
23554     /**
23555      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23556      * This method fires the move event.
23557      * @param {Number} x The new x position
23558      * @param {Number} y The new y position
23559      * @returns {Roo.BoxComponent} this
23560      */
23561     setPagePosition : function(x, y){
23562         this.pageX = x;
23563         this.pageY = y;
23564         if(!this.boxReady){
23565             return;
23566         }
23567         if(x === undefined || y === undefined){ // cannot translate undefined points
23568             return;
23569         }
23570         var p = this.el.translatePoints(x, y);
23571         this.setPosition(p.left, p.top);
23572         return this;
23573     },
23574
23575     // private
23576     onRender : function(ct, position){
23577         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23578         if(this.resizeEl){
23579             this.resizeEl = Roo.get(this.resizeEl);
23580         }
23581         if(this.positionEl){
23582             this.positionEl = Roo.get(this.positionEl);
23583         }
23584     },
23585
23586     // private
23587     afterRender : function(){
23588         Roo.BoxComponent.superclass.afterRender.call(this);
23589         this.boxReady = true;
23590         this.setSize(this.width, this.height);
23591         if(this.x || this.y){
23592             this.setPosition(this.x, this.y);
23593         }
23594         if(this.pageX || this.pageY){
23595             this.setPagePosition(this.pageX, this.pageY);
23596         }
23597     },
23598
23599     /**
23600      * Force the component's size to recalculate based on the underlying element's current height and width.
23601      * @returns {Roo.BoxComponent} this
23602      */
23603     syncSize : function(){
23604         delete this.lastSize;
23605         this.setSize(this.el.getWidth(), this.el.getHeight());
23606         return this;
23607     },
23608
23609     /**
23610      * Called after the component is resized, this method is empty by default but can be implemented by any
23611      * subclass that needs to perform custom logic after a resize occurs.
23612      * @param {Number} adjWidth The box-adjusted width that was set
23613      * @param {Number} adjHeight The box-adjusted height that was set
23614      * @param {Number} rawWidth The width that was originally specified
23615      * @param {Number} rawHeight The height that was originally specified
23616      */
23617     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23618
23619     },
23620
23621     /**
23622      * Called after the component is moved, this method is empty by default but can be implemented by any
23623      * subclass that needs to perform custom logic after a move occurs.
23624      * @param {Number} x The new x position
23625      * @param {Number} y The new y position
23626      */
23627     onPosition : function(x, y){
23628
23629     },
23630
23631     // private
23632     adjustSize : function(w, h){
23633         if(this.autoWidth){
23634             w = 'auto';
23635         }
23636         if(this.autoHeight){
23637             h = 'auto';
23638         }
23639         return {width : w, height: h};
23640     },
23641
23642     // private
23643     adjustPosition : function(x, y){
23644         return {x : x, y: y};
23645     }
23646 });/*
23647  * Based on:
23648  * Ext JS Library 1.1.1
23649  * Copyright(c) 2006-2007, Ext JS, LLC.
23650  *
23651  * Originally Released Under LGPL - original licence link has changed is not relivant.
23652  *
23653  * Fork - LGPL
23654  * <script type="text/javascript">
23655  */
23656
23657
23658 /**
23659  * @class Roo.SplitBar
23660  * @extends Roo.util.Observable
23661  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23662  * <br><br>
23663  * Usage:
23664  * <pre><code>
23665 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23666                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23667 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23668 split.minSize = 100;
23669 split.maxSize = 600;
23670 split.animate = true;
23671 split.on('moved', splitterMoved);
23672 </code></pre>
23673  * @constructor
23674  * Create a new SplitBar
23675  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23676  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23677  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23678  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23679                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23680                         position of the SplitBar).
23681  */
23682 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23683     
23684     /** @private */
23685     this.el = Roo.get(dragElement, true);
23686     this.el.dom.unselectable = "on";
23687     /** @private */
23688     this.resizingEl = Roo.get(resizingElement, true);
23689
23690     /**
23691      * @private
23692      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23693      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23694      * @type Number
23695      */
23696     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23697     
23698     /**
23699      * The minimum size of the resizing element. (Defaults to 0)
23700      * @type Number
23701      */
23702     this.minSize = 0;
23703     
23704     /**
23705      * The maximum size of the resizing element. (Defaults to 2000)
23706      * @type Number
23707      */
23708     this.maxSize = 2000;
23709     
23710     /**
23711      * Whether to animate the transition to the new size
23712      * @type Boolean
23713      */
23714     this.animate = false;
23715     
23716     /**
23717      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23718      * @type Boolean
23719      */
23720     this.useShim = false;
23721     
23722     /** @private */
23723     this.shim = null;
23724     
23725     if(!existingProxy){
23726         /** @private */
23727         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23728     }else{
23729         this.proxy = Roo.get(existingProxy).dom;
23730     }
23731     /** @private */
23732     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23733     
23734     /** @private */
23735     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23736     
23737     /** @private */
23738     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23739     
23740     /** @private */
23741     this.dragSpecs = {};
23742     
23743     /**
23744      * @private The adapter to use to positon and resize elements
23745      */
23746     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23747     this.adapter.init(this);
23748     
23749     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23750         /** @private */
23751         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23752         this.el.addClass("x-splitbar-h");
23753     }else{
23754         /** @private */
23755         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23756         this.el.addClass("x-splitbar-v");
23757     }
23758     
23759     this.addEvents({
23760         /**
23761          * @event resize
23762          * Fires when the splitter is moved (alias for {@link #event-moved})
23763          * @param {Roo.SplitBar} this
23764          * @param {Number} newSize the new width or height
23765          */
23766         "resize" : true,
23767         /**
23768          * @event moved
23769          * Fires when the splitter is moved
23770          * @param {Roo.SplitBar} this
23771          * @param {Number} newSize the new width or height
23772          */
23773         "moved" : true,
23774         /**
23775          * @event beforeresize
23776          * Fires before the splitter is dragged
23777          * @param {Roo.SplitBar} this
23778          */
23779         "beforeresize" : true,
23780
23781         "beforeapply" : true
23782     });
23783
23784     Roo.util.Observable.call(this);
23785 };
23786
23787 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23788     onStartProxyDrag : function(x, y){
23789         this.fireEvent("beforeresize", this);
23790         if(!this.overlay){
23791             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23792             o.unselectable();
23793             o.enableDisplayMode("block");
23794             // all splitbars share the same overlay
23795             Roo.SplitBar.prototype.overlay = o;
23796         }
23797         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23798         this.overlay.show();
23799         Roo.get(this.proxy).setDisplayed("block");
23800         var size = this.adapter.getElementSize(this);
23801         this.activeMinSize = this.getMinimumSize();;
23802         this.activeMaxSize = this.getMaximumSize();;
23803         var c1 = size - this.activeMinSize;
23804         var c2 = Math.max(this.activeMaxSize - size, 0);
23805         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23806             this.dd.resetConstraints();
23807             this.dd.setXConstraint(
23808                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23809                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23810             );
23811             this.dd.setYConstraint(0, 0);
23812         }else{
23813             this.dd.resetConstraints();
23814             this.dd.setXConstraint(0, 0);
23815             this.dd.setYConstraint(
23816                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23817                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23818             );
23819          }
23820         this.dragSpecs.startSize = size;
23821         this.dragSpecs.startPoint = [x, y];
23822         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23823     },
23824     
23825     /** 
23826      * @private Called after the drag operation by the DDProxy
23827      */
23828     onEndProxyDrag : function(e){
23829         Roo.get(this.proxy).setDisplayed(false);
23830         var endPoint = Roo.lib.Event.getXY(e);
23831         if(this.overlay){
23832             this.overlay.hide();
23833         }
23834         var newSize;
23835         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23836             newSize = this.dragSpecs.startSize + 
23837                 (this.placement == Roo.SplitBar.LEFT ?
23838                     endPoint[0] - this.dragSpecs.startPoint[0] :
23839                     this.dragSpecs.startPoint[0] - endPoint[0]
23840                 );
23841         }else{
23842             newSize = this.dragSpecs.startSize + 
23843                 (this.placement == Roo.SplitBar.TOP ?
23844                     endPoint[1] - this.dragSpecs.startPoint[1] :
23845                     this.dragSpecs.startPoint[1] - endPoint[1]
23846                 );
23847         }
23848         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23849         if(newSize != this.dragSpecs.startSize){
23850             if(this.fireEvent('beforeapply', this, newSize) !== false){
23851                 this.adapter.setElementSize(this, newSize);
23852                 this.fireEvent("moved", this, newSize);
23853                 this.fireEvent("resize", this, newSize);
23854             }
23855         }
23856     },
23857     
23858     /**
23859      * Get the adapter this SplitBar uses
23860      * @return The adapter object
23861      */
23862     getAdapter : function(){
23863         return this.adapter;
23864     },
23865     
23866     /**
23867      * Set the adapter this SplitBar uses
23868      * @param {Object} adapter A SplitBar adapter object
23869      */
23870     setAdapter : function(adapter){
23871         this.adapter = adapter;
23872         this.adapter.init(this);
23873     },
23874     
23875     /**
23876      * Gets the minimum size for the resizing element
23877      * @return {Number} The minimum size
23878      */
23879     getMinimumSize : function(){
23880         return this.minSize;
23881     },
23882     
23883     /**
23884      * Sets the minimum size for the resizing element
23885      * @param {Number} minSize The minimum size
23886      */
23887     setMinimumSize : function(minSize){
23888         this.minSize = minSize;
23889     },
23890     
23891     /**
23892      * Gets the maximum size for the resizing element
23893      * @return {Number} The maximum size
23894      */
23895     getMaximumSize : function(){
23896         return this.maxSize;
23897     },
23898     
23899     /**
23900      * Sets the maximum size for the resizing element
23901      * @param {Number} maxSize The maximum size
23902      */
23903     setMaximumSize : function(maxSize){
23904         this.maxSize = maxSize;
23905     },
23906     
23907     /**
23908      * Sets the initialize size for the resizing element
23909      * @param {Number} size The initial size
23910      */
23911     setCurrentSize : function(size){
23912         var oldAnimate = this.animate;
23913         this.animate = false;
23914         this.adapter.setElementSize(this, size);
23915         this.animate = oldAnimate;
23916     },
23917     
23918     /**
23919      * Destroy this splitbar. 
23920      * @param {Boolean} removeEl True to remove the element
23921      */
23922     destroy : function(removeEl){
23923         if(this.shim){
23924             this.shim.remove();
23925         }
23926         this.dd.unreg();
23927         this.proxy.parentNode.removeChild(this.proxy);
23928         if(removeEl){
23929             this.el.remove();
23930         }
23931     }
23932 });
23933
23934 /**
23935  * @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.
23936  */
23937 Roo.SplitBar.createProxy = function(dir){
23938     var proxy = new Roo.Element(document.createElement("div"));
23939     proxy.unselectable();
23940     var cls = 'x-splitbar-proxy';
23941     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23942     document.body.appendChild(proxy.dom);
23943     return proxy.dom;
23944 };
23945
23946 /** 
23947  * @class Roo.SplitBar.BasicLayoutAdapter
23948  * Default Adapter. It assumes the splitter and resizing element are not positioned
23949  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23950  */
23951 Roo.SplitBar.BasicLayoutAdapter = function(){
23952 };
23953
23954 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23955     // do nothing for now
23956     init : function(s){
23957     
23958     },
23959     /**
23960      * Called before drag operations to get the current size of the resizing element. 
23961      * @param {Roo.SplitBar} s The SplitBar using this adapter
23962      */
23963      getElementSize : function(s){
23964         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23965             return s.resizingEl.getWidth();
23966         }else{
23967             return s.resizingEl.getHeight();
23968         }
23969     },
23970     
23971     /**
23972      * Called after drag operations to set the size of the resizing element.
23973      * @param {Roo.SplitBar} s The SplitBar using this adapter
23974      * @param {Number} newSize The new size to set
23975      * @param {Function} onComplete A function to be invoked when resizing is complete
23976      */
23977     setElementSize : function(s, newSize, onComplete){
23978         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23979             if(!s.animate){
23980                 s.resizingEl.setWidth(newSize);
23981                 if(onComplete){
23982                     onComplete(s, newSize);
23983                 }
23984             }else{
23985                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23986             }
23987         }else{
23988             
23989             if(!s.animate){
23990                 s.resizingEl.setHeight(newSize);
23991                 if(onComplete){
23992                     onComplete(s, newSize);
23993                 }
23994             }else{
23995                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23996             }
23997         }
23998     }
23999 };
24000
24001 /** 
24002  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24003  * @extends Roo.SplitBar.BasicLayoutAdapter
24004  * Adapter that  moves the splitter element to align with the resized sizing element. 
24005  * Used with an absolute positioned SplitBar.
24006  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24007  * document.body, make sure you assign an id to the body element.
24008  */
24009 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24010     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24011     this.container = Roo.get(container);
24012 };
24013
24014 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24015     init : function(s){
24016         this.basic.init(s);
24017     },
24018     
24019     getElementSize : function(s){
24020         return this.basic.getElementSize(s);
24021     },
24022     
24023     setElementSize : function(s, newSize, onComplete){
24024         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24025     },
24026     
24027     moveSplitter : function(s){
24028         var yes = Roo.SplitBar;
24029         switch(s.placement){
24030             case yes.LEFT:
24031                 s.el.setX(s.resizingEl.getRight());
24032                 break;
24033             case yes.RIGHT:
24034                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24035                 break;
24036             case yes.TOP:
24037                 s.el.setY(s.resizingEl.getBottom());
24038                 break;
24039             case yes.BOTTOM:
24040                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24041                 break;
24042         }
24043     }
24044 };
24045
24046 /**
24047  * Orientation constant - Create a vertical SplitBar
24048  * @static
24049  * @type Number
24050  */
24051 Roo.SplitBar.VERTICAL = 1;
24052
24053 /**
24054  * Orientation constant - Create a horizontal SplitBar
24055  * @static
24056  * @type Number
24057  */
24058 Roo.SplitBar.HORIZONTAL = 2;
24059
24060 /**
24061  * Placement constant - The resizing element is to the left of the splitter element
24062  * @static
24063  * @type Number
24064  */
24065 Roo.SplitBar.LEFT = 1;
24066
24067 /**
24068  * Placement constant - The resizing element is to the right of the splitter element
24069  * @static
24070  * @type Number
24071  */
24072 Roo.SplitBar.RIGHT = 2;
24073
24074 /**
24075  * Placement constant - The resizing element is positioned above the splitter element
24076  * @static
24077  * @type Number
24078  */
24079 Roo.SplitBar.TOP = 3;
24080
24081 /**
24082  * Placement constant - The resizing element is positioned under splitter element
24083  * @static
24084  * @type Number
24085  */
24086 Roo.SplitBar.BOTTOM = 4;
24087 /*
24088  * Based on:
24089  * Ext JS Library 1.1.1
24090  * Copyright(c) 2006-2007, Ext JS, LLC.
24091  *
24092  * Originally Released Under LGPL - original licence link has changed is not relivant.
24093  *
24094  * Fork - LGPL
24095  * <script type="text/javascript">
24096  */
24097
24098 /**
24099  * @class Roo.View
24100  * @extends Roo.util.Observable
24101  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24102  * This class also supports single and multi selection modes. <br>
24103  * Create a data model bound view:
24104  <pre><code>
24105  var store = new Roo.data.Store(...);
24106
24107  var view = new Roo.View({
24108     el : "my-element",
24109     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24110  
24111     singleSelect: true,
24112     selectedClass: "ydataview-selected",
24113     store: store
24114  });
24115
24116  // listen for node click?
24117  view.on("click", function(vw, index, node, e){
24118  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24119  });
24120
24121  // load XML data
24122  dataModel.load("foobar.xml");
24123  </code></pre>
24124  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24125  * <br><br>
24126  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24127  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24128  * 
24129  * Note: old style constructor is still suported (container, template, config)
24130  * 
24131  * @constructor
24132  * Create a new View
24133  * @param {Object} config The config object
24134  * 
24135  */
24136 Roo.View = function(config, depreciated_tpl, depreciated_config){
24137     
24138     if (typeof(depreciated_tpl) == 'undefined') {
24139         // new way.. - universal constructor.
24140         Roo.apply(this, config);
24141         this.el  = Roo.get(this.el);
24142     } else {
24143         // old format..
24144         this.el  = Roo.get(config);
24145         this.tpl = depreciated_tpl;
24146         Roo.apply(this, depreciated_config);
24147     }
24148     this.wrapEl  = this.el.wrap().wrap();
24149     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24150     
24151     
24152     if(typeof(this.tpl) == "string"){
24153         this.tpl = new Roo.Template(this.tpl);
24154     } else {
24155         // support xtype ctors..
24156         this.tpl = new Roo.factory(this.tpl, Roo);
24157     }
24158     
24159     
24160     this.tpl.compile();
24161    
24162   
24163     
24164      
24165     /** @private */
24166     this.addEvents({
24167         /**
24168          * @event beforeclick
24169          * Fires before a click is processed. Returns false to cancel the default action.
24170          * @param {Roo.View} this
24171          * @param {Number} index The index of the target node
24172          * @param {HTMLElement} node The target node
24173          * @param {Roo.EventObject} e The raw event object
24174          */
24175             "beforeclick" : true,
24176         /**
24177          * @event click
24178          * Fires when a template node is clicked.
24179          * @param {Roo.View} this
24180          * @param {Number} index The index of the target node
24181          * @param {HTMLElement} node The target node
24182          * @param {Roo.EventObject} e The raw event object
24183          */
24184             "click" : true,
24185         /**
24186          * @event dblclick
24187          * Fires when a template node is double clicked.
24188          * @param {Roo.View} this
24189          * @param {Number} index The index of the target node
24190          * @param {HTMLElement} node The target node
24191          * @param {Roo.EventObject} e The raw event object
24192          */
24193             "dblclick" : true,
24194         /**
24195          * @event contextmenu
24196          * Fires when a template node is right clicked.
24197          * @param {Roo.View} this
24198          * @param {Number} index The index of the target node
24199          * @param {HTMLElement} node The target node
24200          * @param {Roo.EventObject} e The raw event object
24201          */
24202             "contextmenu" : true,
24203         /**
24204          * @event selectionchange
24205          * Fires when the selected nodes change.
24206          * @param {Roo.View} this
24207          * @param {Array} selections Array of the selected nodes
24208          */
24209             "selectionchange" : true,
24210     
24211         /**
24212          * @event beforeselect
24213          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24214          * @param {Roo.View} this
24215          * @param {HTMLElement} node The node to be selected
24216          * @param {Array} selections Array of currently selected nodes
24217          */
24218             "beforeselect" : true,
24219         /**
24220          * @event preparedata
24221          * Fires on every row to render, to allow you to change the data.
24222          * @param {Roo.View} this
24223          * @param {Object} data to be rendered (change this)
24224          */
24225           "preparedata" : true
24226           
24227           
24228         });
24229
24230
24231
24232     this.el.on({
24233         "click": this.onClick,
24234         "dblclick": this.onDblClick,
24235         "contextmenu": this.onContextMenu,
24236         scope:this
24237     });
24238
24239     this.selections = [];
24240     this.nodes = [];
24241     this.cmp = new Roo.CompositeElementLite([]);
24242     if(this.store){
24243         this.store = Roo.factory(this.store, Roo.data);
24244         this.setStore(this.store, true);
24245     }
24246     
24247     if ( this.footer && this.footer.xtype) {
24248            
24249          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24250         
24251         this.footer.dataSource = this.store
24252         this.footer.container = fctr;
24253         this.footer = Roo.factory(this.footer, Roo);
24254         fctr.insertFirst(this.el);
24255         
24256         // this is a bit insane - as the paging toolbar seems to detach the el..
24257 //        dom.parentNode.parentNode.parentNode
24258          // they get detached?
24259     }
24260     
24261     
24262     Roo.View.superclass.constructor.call(this);
24263     
24264     
24265 };
24266
24267 Roo.extend(Roo.View, Roo.util.Observable, {
24268     
24269      /**
24270      * @cfg {Roo.data.Store} store Data store to load data from.
24271      */
24272     store : false,
24273     
24274     /**
24275      * @cfg {String|Roo.Element} el The container element.
24276      */
24277     el : '',
24278     
24279     /**
24280      * @cfg {String|Roo.Template} tpl The template used by this View 
24281      */
24282     tpl : false,
24283     /**
24284      * @cfg {String} dataName the named area of the template to use as the data area
24285      *                          Works with domtemplates roo-name="name"
24286      */
24287     dataName: false,
24288     /**
24289      * @cfg {String} selectedClass The css class to add to selected nodes
24290      */
24291     selectedClass : "x-view-selected",
24292      /**
24293      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24294      */
24295     emptyText : "",
24296     
24297     /**
24298      * @cfg {String} text to display on mask (default Loading)
24299      */
24300     mask : false,
24301     /**
24302      * @cfg {Boolean} multiSelect Allow multiple selection
24303      */
24304     multiSelect : false,
24305     /**
24306      * @cfg {Boolean} singleSelect Allow single selection
24307      */
24308     singleSelect:  false,
24309     
24310     /**
24311      * @cfg {Boolean} toggleSelect - selecting 
24312      */
24313     toggleSelect : false,
24314     
24315     /**
24316      * Returns the element this view is bound to.
24317      * @return {Roo.Element}
24318      */
24319     getEl : function(){
24320         return this.wrapEl;
24321     },
24322     
24323     
24324
24325     /**
24326      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24327      */
24328     refresh : function(){
24329         var t = this.tpl;
24330         
24331         // if we are using something like 'domtemplate', then
24332         // the what gets used is:
24333         // t.applySubtemplate(NAME, data, wrapping data..)
24334         // the outer template then get' applied with
24335         //     the store 'extra data'
24336         // and the body get's added to the
24337         //      roo-name="data" node?
24338         //      <span class='roo-tpl-{name}'></span> ?????
24339         
24340         
24341         
24342         this.clearSelections();
24343         this.el.update("");
24344         var html = [];
24345         var records = this.store.getRange();
24346         if(records.length < 1) {
24347             
24348             // is this valid??  = should it render a template??
24349             
24350             this.el.update(this.emptyText);
24351             return;
24352         }
24353         var el = this.el;
24354         if (this.dataName) {
24355             this.el.update(t.apply(this.store.meta)); //????
24356             el = this.el.child('.roo-tpl-' + this.dataName);
24357         }
24358         
24359         for(var i = 0, len = records.length; i < len; i++){
24360             var data = this.prepareData(records[i].data, i, records[i]);
24361             this.fireEvent("preparedata", this, data, i, records[i]);
24362             html[html.length] = Roo.util.Format.trim(
24363                 this.dataName ?
24364                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24365                     t.apply(data)
24366             );
24367         }
24368         
24369         
24370         
24371         el.update(html.join(""));
24372         this.nodes = el.dom.childNodes;
24373         this.updateIndexes(0);
24374     },
24375
24376     /**
24377      * Function to override to reformat the data that is sent to
24378      * the template for each node.
24379      * DEPRICATED - use the preparedata event handler.
24380      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24381      * a JSON object for an UpdateManager bound view).
24382      */
24383     prepareData : function(data, index, record)
24384     {
24385         this.fireEvent("preparedata", this, data, index, record);
24386         return data;
24387     },
24388
24389     onUpdate : function(ds, record){
24390         this.clearSelections();
24391         var index = this.store.indexOf(record);
24392         var n = this.nodes[index];
24393         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24394         n.parentNode.removeChild(n);
24395         this.updateIndexes(index, index);
24396     },
24397
24398     
24399     
24400 // --------- FIXME     
24401     onAdd : function(ds, records, index)
24402     {
24403         this.clearSelections();
24404         if(this.nodes.length == 0){
24405             this.refresh();
24406             return;
24407         }
24408         var n = this.nodes[index];
24409         for(var i = 0, len = records.length; i < len; i++){
24410             var d = this.prepareData(records[i].data, i, records[i]);
24411             if(n){
24412                 this.tpl.insertBefore(n, d);
24413             }else{
24414                 
24415                 this.tpl.append(this.el, d);
24416             }
24417         }
24418         this.updateIndexes(index);
24419     },
24420
24421     onRemove : function(ds, record, index){
24422         this.clearSelections();
24423         var el = this.dataName  ?
24424             this.el.child('.roo-tpl-' + this.dataName) :
24425             this.el; 
24426         el.dom.removeChild(this.nodes[index]);
24427         this.updateIndexes(index);
24428     },
24429
24430     /**
24431      * Refresh an individual node.
24432      * @param {Number} index
24433      */
24434     refreshNode : function(index){
24435         this.onUpdate(this.store, this.store.getAt(index));
24436     },
24437
24438     updateIndexes : function(startIndex, endIndex){
24439         var ns = this.nodes;
24440         startIndex = startIndex || 0;
24441         endIndex = endIndex || ns.length - 1;
24442         for(var i = startIndex; i <= endIndex; i++){
24443             ns[i].nodeIndex = i;
24444         }
24445     },
24446
24447     /**
24448      * Changes the data store this view uses and refresh the view.
24449      * @param {Store} store
24450      */
24451     setStore : function(store, initial){
24452         if(!initial && this.store){
24453             this.store.un("datachanged", this.refresh);
24454             this.store.un("add", this.onAdd);
24455             this.store.un("remove", this.onRemove);
24456             this.store.un("update", this.onUpdate);
24457             this.store.un("clear", this.refresh);
24458             this.store.un("beforeload", this.onBeforeLoad);
24459             this.store.un("load", this.onLoad);
24460             this.store.un("loadexception", this.onLoad);
24461         }
24462         if(store){
24463           
24464             store.on("datachanged", this.refresh, this);
24465             store.on("add", this.onAdd, this);
24466             store.on("remove", this.onRemove, this);
24467             store.on("update", this.onUpdate, this);
24468             store.on("clear", this.refresh, this);
24469             store.on("beforeload", this.onBeforeLoad, this);
24470             store.on("load", this.onLoad, this);
24471             store.on("loadexception", this.onLoad, this);
24472         }
24473         
24474         if(store){
24475             this.refresh();
24476         }
24477     },
24478     /**
24479      * onbeforeLoad - masks the loading area.
24480      *
24481      */
24482     onBeforeLoad : function()
24483     {
24484         this.el.update("");
24485         this.el.mask(this.mask ? this.mask : "Loading" ); 
24486     },
24487     onLoad : function ()
24488     {
24489         this.el.unmask();
24490     },
24491     
24492
24493     /**
24494      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
24495      * @param {HTMLElement} node
24496      * @return {HTMLElement} The template node
24497      */
24498     findItemFromChild : function(node){
24499         var el = this.dataName  ?
24500             this.el.child('.roo-tpl-' + this.dataName,true) :
24501             this.el.dom; 
24502         
24503         if(!node || node.parentNode == el){
24504                     return node;
24505             }
24506             var p = node.parentNode;
24507             while(p && p != el){
24508             if(p.parentNode == el){
24509                 return p;
24510             }
24511             p = p.parentNode;
24512         }
24513             return null;
24514     },
24515
24516     /** @ignore */
24517     onClick : function(e){
24518         var item = this.findItemFromChild(e.getTarget());
24519         if(item){
24520             var index = this.indexOf(item);
24521             if(this.onItemClick(item, index, e) !== false){
24522                 this.fireEvent("click", this, index, item, e);
24523             }
24524         }else{
24525             this.clearSelections();
24526         }
24527     },
24528
24529     /** @ignore */
24530     onContextMenu : function(e){
24531         var item = this.findItemFromChild(e.getTarget());
24532         if(item){
24533             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24534         }
24535     },
24536
24537     /** @ignore */
24538     onDblClick : function(e){
24539         var item = this.findItemFromChild(e.getTarget());
24540         if(item){
24541             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24542         }
24543     },
24544
24545     onItemClick : function(item, index, e)
24546     {
24547         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24548             return false;
24549         }
24550         if (this.toggleSelect) {
24551             var m = this.isSelected(item) ? 'unselect' : 'select';
24552             Roo.log(m);
24553             var _t = this;
24554             _t[m](item, true, false);
24555             return true;
24556         }
24557         if(this.multiSelect || this.singleSelect){
24558             if(this.multiSelect && e.shiftKey && this.lastSelection){
24559                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24560             }else{
24561                 this.select(item, this.multiSelect && e.ctrlKey);
24562                 this.lastSelection = item;
24563             }
24564             e.preventDefault();
24565         }
24566         return true;
24567     },
24568
24569     /**
24570      * Get the number of selected nodes.
24571      * @return {Number}
24572      */
24573     getSelectionCount : function(){
24574         return this.selections.length;
24575     },
24576
24577     /**
24578      * Get the currently selected nodes.
24579      * @return {Array} An array of HTMLElements
24580      */
24581     getSelectedNodes : function(){
24582         return this.selections;
24583     },
24584
24585     /**
24586      * Get the indexes of the selected nodes.
24587      * @return {Array}
24588      */
24589     getSelectedIndexes : function(){
24590         var indexes = [], s = this.selections;
24591         for(var i = 0, len = s.length; i < len; i++){
24592             indexes.push(s[i].nodeIndex);
24593         }
24594         return indexes;
24595     },
24596
24597     /**
24598      * Clear all selections
24599      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24600      */
24601     clearSelections : function(suppressEvent){
24602         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24603             this.cmp.elements = this.selections;
24604             this.cmp.removeClass(this.selectedClass);
24605             this.selections = [];
24606             if(!suppressEvent){
24607                 this.fireEvent("selectionchange", this, this.selections);
24608             }
24609         }
24610     },
24611
24612     /**
24613      * Returns true if the passed node is selected
24614      * @param {HTMLElement/Number} node The node or node index
24615      * @return {Boolean}
24616      */
24617     isSelected : function(node){
24618         var s = this.selections;
24619         if(s.length < 1){
24620             return false;
24621         }
24622         node = this.getNode(node);
24623         return s.indexOf(node) !== -1;
24624     },
24625
24626     /**
24627      * Selects nodes.
24628      * @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
24629      * @param {Boolean} keepExisting (optional) true to keep existing selections
24630      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24631      */
24632     select : function(nodeInfo, keepExisting, suppressEvent){
24633         if(nodeInfo instanceof Array){
24634             if(!keepExisting){
24635                 this.clearSelections(true);
24636             }
24637             for(var i = 0, len = nodeInfo.length; i < len; i++){
24638                 this.select(nodeInfo[i], true, true);
24639             }
24640             return;
24641         } 
24642         var node = this.getNode(nodeInfo);
24643         if(!node || this.isSelected(node)){
24644             return; // already selected.
24645         }
24646         if(!keepExisting){
24647             this.clearSelections(true);
24648         }
24649         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24650             Roo.fly(node).addClass(this.selectedClass);
24651             this.selections.push(node);
24652             if(!suppressEvent){
24653                 this.fireEvent("selectionchange", this, this.selections);
24654             }
24655         }
24656         
24657         
24658     },
24659       /**
24660      * Unselects nodes.
24661      * @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
24662      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24663      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24664      */
24665     unselect : function(nodeInfo, keepExisting, suppressEvent)
24666     {
24667         if(nodeInfo instanceof Array){
24668             Roo.each(this.selections, function(s) {
24669                 this.unselect(s, nodeInfo);
24670             }, this);
24671             return;
24672         }
24673         var node = this.getNode(nodeInfo);
24674         if(!node || !this.isSelected(node)){
24675             Roo.log("not selected");
24676             return; // not selected.
24677         }
24678         // fireevent???
24679         var ns = [];
24680         Roo.each(this.selections, function(s) {
24681             if (s == node ) {
24682                 Roo.fly(node).removeClass(this.selectedClass);
24683
24684                 return;
24685             }
24686             ns.push(s);
24687         },this);
24688         
24689         this.selections= ns;
24690         this.fireEvent("selectionchange", this, this.selections);
24691     },
24692
24693     /**
24694      * Gets a template node.
24695      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24696      * @return {HTMLElement} The node or null if it wasn't found
24697      */
24698     getNode : function(nodeInfo){
24699         if(typeof nodeInfo == "string"){
24700             return document.getElementById(nodeInfo);
24701         }else if(typeof nodeInfo == "number"){
24702             return this.nodes[nodeInfo];
24703         }
24704         return nodeInfo;
24705     },
24706
24707     /**
24708      * Gets a range template nodes.
24709      * @param {Number} startIndex
24710      * @param {Number} endIndex
24711      * @return {Array} An array of nodes
24712      */
24713     getNodes : function(start, end){
24714         var ns = this.nodes;
24715         start = start || 0;
24716         end = typeof end == "undefined" ? ns.length - 1 : end;
24717         var nodes = [];
24718         if(start <= end){
24719             for(var i = start; i <= end; i++){
24720                 nodes.push(ns[i]);
24721             }
24722         } else{
24723             for(var i = start; i >= end; i--){
24724                 nodes.push(ns[i]);
24725             }
24726         }
24727         return nodes;
24728     },
24729
24730     /**
24731      * Finds the index of the passed node
24732      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24733      * @return {Number} The index of the node or -1
24734      */
24735     indexOf : function(node){
24736         node = this.getNode(node);
24737         if(typeof node.nodeIndex == "number"){
24738             return node.nodeIndex;
24739         }
24740         var ns = this.nodes;
24741         for(var i = 0, len = ns.length; i < len; i++){
24742             if(ns[i] == node){
24743                 return i;
24744             }
24745         }
24746         return -1;
24747     }
24748 });
24749 /*
24750  * Based on:
24751  * Ext JS Library 1.1.1
24752  * Copyright(c) 2006-2007, Ext JS, LLC.
24753  *
24754  * Originally Released Under LGPL - original licence link has changed is not relivant.
24755  *
24756  * Fork - LGPL
24757  * <script type="text/javascript">
24758  */
24759
24760 /**
24761  * @class Roo.JsonView
24762  * @extends Roo.View
24763  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24764 <pre><code>
24765 var view = new Roo.JsonView({
24766     container: "my-element",
24767     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24768     multiSelect: true, 
24769     jsonRoot: "data" 
24770 });
24771
24772 // listen for node click?
24773 view.on("click", function(vw, index, node, e){
24774     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24775 });
24776
24777 // direct load of JSON data
24778 view.load("foobar.php");
24779
24780 // Example from my blog list
24781 var tpl = new Roo.Template(
24782     '&lt;div class="entry"&gt;' +
24783     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24784     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24785     "&lt;/div&gt;&lt;hr /&gt;"
24786 );
24787
24788 var moreView = new Roo.JsonView({
24789     container :  "entry-list", 
24790     template : tpl,
24791     jsonRoot: "posts"
24792 });
24793 moreView.on("beforerender", this.sortEntries, this);
24794 moreView.load({
24795     url: "/blog/get-posts.php",
24796     params: "allposts=true",
24797     text: "Loading Blog Entries..."
24798 });
24799 </code></pre>
24800
24801 * Note: old code is supported with arguments : (container, template, config)
24802
24803
24804  * @constructor
24805  * Create a new JsonView
24806  * 
24807  * @param {Object} config The config object
24808  * 
24809  */
24810 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24811     
24812     
24813     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24814
24815     var um = this.el.getUpdateManager();
24816     um.setRenderer(this);
24817     um.on("update", this.onLoad, this);
24818     um.on("failure", this.onLoadException, this);
24819
24820     /**
24821      * @event beforerender
24822      * Fires before rendering of the downloaded JSON data.
24823      * @param {Roo.JsonView} this
24824      * @param {Object} data The JSON data loaded
24825      */
24826     /**
24827      * @event load
24828      * Fires when data is loaded.
24829      * @param {Roo.JsonView} this
24830      * @param {Object} data The JSON data loaded
24831      * @param {Object} response The raw Connect response object
24832      */
24833     /**
24834      * @event loadexception
24835      * Fires when loading fails.
24836      * @param {Roo.JsonView} this
24837      * @param {Object} response The raw Connect response object
24838      */
24839     this.addEvents({
24840         'beforerender' : true,
24841         'load' : true,
24842         'loadexception' : true
24843     });
24844 };
24845 Roo.extend(Roo.JsonView, Roo.View, {
24846     /**
24847      * @type {String} The root property in the loaded JSON object that contains the data
24848      */
24849     jsonRoot : "",
24850
24851     /**
24852      * Refreshes the view.
24853      */
24854     refresh : function(){
24855         this.clearSelections();
24856         this.el.update("");
24857         var html = [];
24858         var o = this.jsonData;
24859         if(o && o.length > 0){
24860             for(var i = 0, len = o.length; i < len; i++){
24861                 var data = this.prepareData(o[i], i, o);
24862                 html[html.length] = this.tpl.apply(data);
24863             }
24864         }else{
24865             html.push(this.emptyText);
24866         }
24867         this.el.update(html.join(""));
24868         this.nodes = this.el.dom.childNodes;
24869         this.updateIndexes(0);
24870     },
24871
24872     /**
24873      * 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.
24874      * @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:
24875      <pre><code>
24876      view.load({
24877          url: "your-url.php",
24878          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24879          callback: yourFunction,
24880          scope: yourObject, //(optional scope)
24881          discardUrl: false,
24882          nocache: false,
24883          text: "Loading...",
24884          timeout: 30,
24885          scripts: false
24886      });
24887      </code></pre>
24888      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24889      * 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.
24890      * @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}
24891      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24892      * @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.
24893      */
24894     load : function(){
24895         var um = this.el.getUpdateManager();
24896         um.update.apply(um, arguments);
24897     },
24898
24899     render : function(el, response){
24900         this.clearSelections();
24901         this.el.update("");
24902         var o;
24903         try{
24904             o = Roo.util.JSON.decode(response.responseText);
24905             if(this.jsonRoot){
24906                 
24907                 o = o[this.jsonRoot];
24908             }
24909         } catch(e){
24910         }
24911         /**
24912          * The current JSON data or null
24913          */
24914         this.jsonData = o;
24915         this.beforeRender();
24916         this.refresh();
24917     },
24918
24919 /**
24920  * Get the number of records in the current JSON dataset
24921  * @return {Number}
24922  */
24923     getCount : function(){
24924         return this.jsonData ? this.jsonData.length : 0;
24925     },
24926
24927 /**
24928  * Returns the JSON object for the specified node(s)
24929  * @param {HTMLElement/Array} node The node or an array of nodes
24930  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24931  * you get the JSON object for the node
24932  */
24933     getNodeData : function(node){
24934         if(node instanceof Array){
24935             var data = [];
24936             for(var i = 0, len = node.length; i < len; i++){
24937                 data.push(this.getNodeData(node[i]));
24938             }
24939             return data;
24940         }
24941         return this.jsonData[this.indexOf(node)] || null;
24942     },
24943
24944     beforeRender : function(){
24945         this.snapshot = this.jsonData;
24946         if(this.sortInfo){
24947             this.sort.apply(this, this.sortInfo);
24948         }
24949         this.fireEvent("beforerender", this, this.jsonData);
24950     },
24951
24952     onLoad : function(el, o){
24953         this.fireEvent("load", this, this.jsonData, o);
24954     },
24955
24956     onLoadException : function(el, o){
24957         this.fireEvent("loadexception", this, o);
24958     },
24959
24960 /**
24961  * Filter the data by a specific property.
24962  * @param {String} property A property on your JSON objects
24963  * @param {String/RegExp} value Either string that the property values
24964  * should start with, or a RegExp to test against the property
24965  */
24966     filter : function(property, value){
24967         if(this.jsonData){
24968             var data = [];
24969             var ss = this.snapshot;
24970             if(typeof value == "string"){
24971                 var vlen = value.length;
24972                 if(vlen == 0){
24973                     this.clearFilter();
24974                     return;
24975                 }
24976                 value = value.toLowerCase();
24977                 for(var i = 0, len = ss.length; i < len; i++){
24978                     var o = ss[i];
24979                     if(o[property].substr(0, vlen).toLowerCase() == value){
24980                         data.push(o);
24981                     }
24982                 }
24983             } else if(value.exec){ // regex?
24984                 for(var i = 0, len = ss.length; i < len; i++){
24985                     var o = ss[i];
24986                     if(value.test(o[property])){
24987                         data.push(o);
24988                     }
24989                 }
24990             } else{
24991                 return;
24992             }
24993             this.jsonData = data;
24994             this.refresh();
24995         }
24996     },
24997
24998 /**
24999  * Filter by a function. The passed function will be called with each
25000  * object in the current dataset. If the function returns true the value is kept,
25001  * otherwise it is filtered.
25002  * @param {Function} fn
25003  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25004  */
25005     filterBy : function(fn, scope){
25006         if(this.jsonData){
25007             var data = [];
25008             var ss = this.snapshot;
25009             for(var i = 0, len = ss.length; i < len; i++){
25010                 var o = ss[i];
25011                 if(fn.call(scope || this, o)){
25012                     data.push(o);
25013                 }
25014             }
25015             this.jsonData = data;
25016             this.refresh();
25017         }
25018     },
25019
25020 /**
25021  * Clears the current filter.
25022  */
25023     clearFilter : function(){
25024         if(this.snapshot && this.jsonData != this.snapshot){
25025             this.jsonData = this.snapshot;
25026             this.refresh();
25027         }
25028     },
25029
25030
25031 /**
25032  * Sorts the data for this view and refreshes it.
25033  * @param {String} property A property on your JSON objects to sort on
25034  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25035  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25036  */
25037     sort : function(property, dir, sortType){
25038         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25039         if(this.jsonData){
25040             var p = property;
25041             var dsc = dir && dir.toLowerCase() == "desc";
25042             var f = function(o1, o2){
25043                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25044                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25045                 ;
25046                 if(v1 < v2){
25047                     return dsc ? +1 : -1;
25048                 } else if(v1 > v2){
25049                     return dsc ? -1 : +1;
25050                 } else{
25051                     return 0;
25052                 }
25053             };
25054             this.jsonData.sort(f);
25055             this.refresh();
25056             if(this.jsonData != this.snapshot){
25057                 this.snapshot.sort(f);
25058             }
25059         }
25060     }
25061 });/*
25062  * Based on:
25063  * Ext JS Library 1.1.1
25064  * Copyright(c) 2006-2007, Ext JS, LLC.
25065  *
25066  * Originally Released Under LGPL - original licence link has changed is not relivant.
25067  *
25068  * Fork - LGPL
25069  * <script type="text/javascript">
25070  */
25071  
25072
25073 /**
25074  * @class Roo.ColorPalette
25075  * @extends Roo.Component
25076  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25077  * Here's an example of typical usage:
25078  * <pre><code>
25079 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25080 cp.render('my-div');
25081
25082 cp.on('select', function(palette, selColor){
25083     // do something with selColor
25084 });
25085 </code></pre>
25086  * @constructor
25087  * Create a new ColorPalette
25088  * @param {Object} config The config object
25089  */
25090 Roo.ColorPalette = function(config){
25091     Roo.ColorPalette.superclass.constructor.call(this, config);
25092     this.addEvents({
25093         /**
25094              * @event select
25095              * Fires when a color is selected
25096              * @param {ColorPalette} this
25097              * @param {String} color The 6-digit color hex code (without the # symbol)
25098              */
25099         select: true
25100     });
25101
25102     if(this.handler){
25103         this.on("select", this.handler, this.scope, true);
25104     }
25105 };
25106 Roo.extend(Roo.ColorPalette, Roo.Component, {
25107     /**
25108      * @cfg {String} itemCls
25109      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25110      */
25111     itemCls : "x-color-palette",
25112     /**
25113      * @cfg {String} value
25114      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25115      * the hex codes are case-sensitive.
25116      */
25117     value : null,
25118     clickEvent:'click',
25119     // private
25120     ctype: "Roo.ColorPalette",
25121
25122     /**
25123      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25124      */
25125     allowReselect : false,
25126
25127     /**
25128      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25129      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25130      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25131      * of colors with the width setting until the box is symmetrical.</p>
25132      * <p>You can override individual colors if needed:</p>
25133      * <pre><code>
25134 var cp = new Roo.ColorPalette();
25135 cp.colors[0] = "FF0000";  // change the first box to red
25136 </code></pre>
25137
25138 Or you can provide a custom array of your own for complete control:
25139 <pre><code>
25140 var cp = new Roo.ColorPalette();
25141 cp.colors = ["000000", "993300", "333300"];
25142 </code></pre>
25143      * @type Array
25144      */
25145     colors : [
25146         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25147         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25148         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25149         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25150         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25151     ],
25152
25153     // private
25154     onRender : function(container, position){
25155         var t = new Roo.MasterTemplate(
25156             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25157         );
25158         var c = this.colors;
25159         for(var i = 0, len = c.length; i < len; i++){
25160             t.add([c[i]]);
25161         }
25162         var el = document.createElement("div");
25163         el.className = this.itemCls;
25164         t.overwrite(el);
25165         container.dom.insertBefore(el, position);
25166         this.el = Roo.get(el);
25167         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25168         if(this.clickEvent != 'click'){
25169             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25170         }
25171     },
25172
25173     // private
25174     afterRender : function(){
25175         Roo.ColorPalette.superclass.afterRender.call(this);
25176         if(this.value){
25177             var s = this.value;
25178             this.value = null;
25179             this.select(s);
25180         }
25181     },
25182
25183     // private
25184     handleClick : function(e, t){
25185         e.preventDefault();
25186         if(!this.disabled){
25187             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25188             this.select(c.toUpperCase());
25189         }
25190     },
25191
25192     /**
25193      * Selects the specified color in the palette (fires the select event)
25194      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25195      */
25196     select : function(color){
25197         color = color.replace("#", "");
25198         if(color != this.value || this.allowReselect){
25199             var el = this.el;
25200             if(this.value){
25201                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25202             }
25203             el.child("a.color-"+color).addClass("x-color-palette-sel");
25204             this.value = color;
25205             this.fireEvent("select", this, color);
25206         }
25207     }
25208 });/*
25209  * Based on:
25210  * Ext JS Library 1.1.1
25211  * Copyright(c) 2006-2007, Ext JS, LLC.
25212  *
25213  * Originally Released Under LGPL - original licence link has changed is not relivant.
25214  *
25215  * Fork - LGPL
25216  * <script type="text/javascript">
25217  */
25218  
25219 /**
25220  * @class Roo.DatePicker
25221  * @extends Roo.Component
25222  * Simple date picker class.
25223  * @constructor
25224  * Create a new DatePicker
25225  * @param {Object} config The config object
25226  */
25227 Roo.DatePicker = function(config){
25228     Roo.DatePicker.superclass.constructor.call(this, config);
25229
25230     this.value = config && config.value ?
25231                  config.value.clearTime() : new Date().clearTime();
25232
25233     this.addEvents({
25234         /**
25235              * @event select
25236              * Fires when a date is selected
25237              * @param {DatePicker} this
25238              * @param {Date} date The selected date
25239              */
25240         'select': true,
25241         /**
25242              * @event monthchange
25243              * Fires when the displayed month changes 
25244              * @param {DatePicker} this
25245              * @param {Date} date The selected month
25246              */
25247         'monthchange': true
25248     });
25249
25250     if(this.handler){
25251         this.on("select", this.handler,  this.scope || this);
25252     }
25253     // build the disabledDatesRE
25254     if(!this.disabledDatesRE && this.disabledDates){
25255         var dd = this.disabledDates;
25256         var re = "(?:";
25257         for(var i = 0; i < dd.length; i++){
25258             re += dd[i];
25259             if(i != dd.length-1) re += "|";
25260         }
25261         this.disabledDatesRE = new RegExp(re + ")");
25262     }
25263 };
25264
25265 Roo.extend(Roo.DatePicker, Roo.Component, {
25266     /**
25267      * @cfg {String} todayText
25268      * The text to display on the button that selects the current date (defaults to "Today")
25269      */
25270     todayText : "Today",
25271     /**
25272      * @cfg {String} okText
25273      * The text to display on the ok button
25274      */
25275     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25276     /**
25277      * @cfg {String} cancelText
25278      * The text to display on the cancel button
25279      */
25280     cancelText : "Cancel",
25281     /**
25282      * @cfg {String} todayTip
25283      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25284      */
25285     todayTip : "{0} (Spacebar)",
25286     /**
25287      * @cfg {Date} minDate
25288      * Minimum allowable date (JavaScript date object, defaults to null)
25289      */
25290     minDate : null,
25291     /**
25292      * @cfg {Date} maxDate
25293      * Maximum allowable date (JavaScript date object, defaults to null)
25294      */
25295     maxDate : null,
25296     /**
25297      * @cfg {String} minText
25298      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25299      */
25300     minText : "This date is before the minimum date",
25301     /**
25302      * @cfg {String} maxText
25303      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25304      */
25305     maxText : "This date is after the maximum date",
25306     /**
25307      * @cfg {String} format
25308      * The default date format string which can be overriden for localization support.  The format must be
25309      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25310      */
25311     format : "m/d/y",
25312     /**
25313      * @cfg {Array} disabledDays
25314      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25315      */
25316     disabledDays : null,
25317     /**
25318      * @cfg {String} disabledDaysText
25319      * The tooltip to display when the date falls on a disabled day (defaults to "")
25320      */
25321     disabledDaysText : "",
25322     /**
25323      * @cfg {RegExp} disabledDatesRE
25324      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25325      */
25326     disabledDatesRE : null,
25327     /**
25328      * @cfg {String} disabledDatesText
25329      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25330      */
25331     disabledDatesText : "",
25332     /**
25333      * @cfg {Boolean} constrainToViewport
25334      * True to constrain the date picker to the viewport (defaults to true)
25335      */
25336     constrainToViewport : true,
25337     /**
25338      * @cfg {Array} monthNames
25339      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25340      */
25341     monthNames : Date.monthNames,
25342     /**
25343      * @cfg {Array} dayNames
25344      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25345      */
25346     dayNames : Date.dayNames,
25347     /**
25348      * @cfg {String} nextText
25349      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25350      */
25351     nextText: 'Next Month (Control+Right)',
25352     /**
25353      * @cfg {String} prevText
25354      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25355      */
25356     prevText: 'Previous Month (Control+Left)',
25357     /**
25358      * @cfg {String} monthYearText
25359      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25360      */
25361     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25362     /**
25363      * @cfg {Number} startDay
25364      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25365      */
25366     startDay : 0,
25367     /**
25368      * @cfg {Bool} showClear
25369      * Show a clear button (usefull for date form elements that can be blank.)
25370      */
25371     
25372     showClear: false,
25373     
25374     /**
25375      * Sets the value of the date field
25376      * @param {Date} value The date to set
25377      */
25378     setValue : function(value){
25379         var old = this.value;
25380         
25381         if (typeof(value) == 'string') {
25382          
25383             value = Date.parseDate(value, this.format);
25384         }
25385         if (!value) {
25386             value = new Date();
25387         }
25388         
25389         this.value = value.clearTime(true);
25390         if(this.el){
25391             this.update(this.value);
25392         }
25393     },
25394
25395     /**
25396      * Gets the current selected value of the date field
25397      * @return {Date} The selected date
25398      */
25399     getValue : function(){
25400         return this.value;
25401     },
25402
25403     // private
25404     focus : function(){
25405         if(this.el){
25406             this.update(this.activeDate);
25407         }
25408     },
25409
25410     // privateval
25411     onRender : function(container, position){
25412         
25413         var m = [
25414              '<table cellspacing="0">',
25415                 '<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>',
25416                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25417         var dn = this.dayNames;
25418         for(var i = 0; i < 7; i++){
25419             var d = this.startDay+i;
25420             if(d > 6){
25421                 d = d-7;
25422             }
25423             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25424         }
25425         m[m.length] = "</tr></thead><tbody><tr>";
25426         for(var i = 0; i < 42; i++) {
25427             if(i % 7 == 0 && i != 0){
25428                 m[m.length] = "</tr><tr>";
25429             }
25430             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25431         }
25432         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
25433             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
25434
25435         var el = document.createElement("div");
25436         el.className = "x-date-picker";
25437         el.innerHTML = m.join("");
25438
25439         container.dom.insertBefore(el, position);
25440
25441         this.el = Roo.get(el);
25442         this.eventEl = Roo.get(el.firstChild);
25443
25444         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
25445             handler: this.showPrevMonth,
25446             scope: this,
25447             preventDefault:true,
25448             stopDefault:true
25449         });
25450
25451         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
25452             handler: this.showNextMonth,
25453             scope: this,
25454             preventDefault:true,
25455             stopDefault:true
25456         });
25457
25458         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
25459
25460         this.monthPicker = this.el.down('div.x-date-mp');
25461         this.monthPicker.enableDisplayMode('block');
25462         
25463         var kn = new Roo.KeyNav(this.eventEl, {
25464             "left" : function(e){
25465                 e.ctrlKey ?
25466                     this.showPrevMonth() :
25467                     this.update(this.activeDate.add("d", -1));
25468             },
25469
25470             "right" : function(e){
25471                 e.ctrlKey ?
25472                     this.showNextMonth() :
25473                     this.update(this.activeDate.add("d", 1));
25474             },
25475
25476             "up" : function(e){
25477                 e.ctrlKey ?
25478                     this.showNextYear() :
25479                     this.update(this.activeDate.add("d", -7));
25480             },
25481
25482             "down" : function(e){
25483                 e.ctrlKey ?
25484                     this.showPrevYear() :
25485                     this.update(this.activeDate.add("d", 7));
25486             },
25487
25488             "pageUp" : function(e){
25489                 this.showNextMonth();
25490             },
25491
25492             "pageDown" : function(e){
25493                 this.showPrevMonth();
25494             },
25495
25496             "enter" : function(e){
25497                 e.stopPropagation();
25498                 return true;
25499             },
25500
25501             scope : this
25502         });
25503
25504         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
25505
25506         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
25507
25508         this.el.unselectable();
25509         
25510         this.cells = this.el.select("table.x-date-inner tbody td");
25511         this.textNodes = this.el.query("table.x-date-inner tbody span");
25512
25513         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
25514             text: "&#160;",
25515             tooltip: this.monthYearText
25516         });
25517
25518         this.mbtn.on('click', this.showMonthPicker, this);
25519         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
25520
25521
25522         var today = (new Date()).dateFormat(this.format);
25523         
25524         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
25525         if (this.showClear) {
25526             baseTb.add( new Roo.Toolbar.Fill());
25527         }
25528         baseTb.add({
25529             text: String.format(this.todayText, today),
25530             tooltip: String.format(this.todayTip, today),
25531             handler: this.selectToday,
25532             scope: this
25533         });
25534         
25535         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
25536             
25537         //});
25538         if (this.showClear) {
25539             
25540             baseTb.add( new Roo.Toolbar.Fill());
25541             baseTb.add({
25542                 text: '&#160;',
25543                 cls: 'x-btn-icon x-btn-clear',
25544                 handler: function() {
25545                     //this.value = '';
25546                     this.fireEvent("select", this, '');
25547                 },
25548                 scope: this
25549             });
25550         }
25551         
25552         
25553         if(Roo.isIE){
25554             this.el.repaint();
25555         }
25556         this.update(this.value);
25557     },
25558
25559     createMonthPicker : function(){
25560         if(!this.monthPicker.dom.firstChild){
25561             var buf = ['<table border="0" cellspacing="0">'];
25562             for(var i = 0; i < 6; i++){
25563                 buf.push(
25564                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25565                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25566                     i == 0 ?
25567                     '<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>' :
25568                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25569                 );
25570             }
25571             buf.push(
25572                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25573                     this.okText,
25574                     '</button><button type="button" class="x-date-mp-cancel">',
25575                     this.cancelText,
25576                     '</button></td></tr>',
25577                 '</table>'
25578             );
25579             this.monthPicker.update(buf.join(''));
25580             this.monthPicker.on('click', this.onMonthClick, this);
25581             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25582
25583             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25584             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25585
25586             this.mpMonths.each(function(m, a, i){
25587                 i += 1;
25588                 if((i%2) == 0){
25589                     m.dom.xmonth = 5 + Math.round(i * .5);
25590                 }else{
25591                     m.dom.xmonth = Math.round((i-1) * .5);
25592                 }
25593             });
25594         }
25595     },
25596
25597     showMonthPicker : function(){
25598         this.createMonthPicker();
25599         var size = this.el.getSize();
25600         this.monthPicker.setSize(size);
25601         this.monthPicker.child('table').setSize(size);
25602
25603         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25604         this.updateMPMonth(this.mpSelMonth);
25605         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25606         this.updateMPYear(this.mpSelYear);
25607
25608         this.monthPicker.slideIn('t', {duration:.2});
25609     },
25610
25611     updateMPYear : function(y){
25612         this.mpyear = y;
25613         var ys = this.mpYears.elements;
25614         for(var i = 1; i <= 10; i++){
25615             var td = ys[i-1], y2;
25616             if((i%2) == 0){
25617                 y2 = y + Math.round(i * .5);
25618                 td.firstChild.innerHTML = y2;
25619                 td.xyear = y2;
25620             }else{
25621                 y2 = y - (5-Math.round(i * .5));
25622                 td.firstChild.innerHTML = y2;
25623                 td.xyear = y2;
25624             }
25625             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25626         }
25627     },
25628
25629     updateMPMonth : function(sm){
25630         this.mpMonths.each(function(m, a, i){
25631             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25632         });
25633     },
25634
25635     selectMPMonth: function(m){
25636         
25637     },
25638
25639     onMonthClick : function(e, t){
25640         e.stopEvent();
25641         var el = new Roo.Element(t), pn;
25642         if(el.is('button.x-date-mp-cancel')){
25643             this.hideMonthPicker();
25644         }
25645         else if(el.is('button.x-date-mp-ok')){
25646             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25647             this.hideMonthPicker();
25648         }
25649         else if(pn = el.up('td.x-date-mp-month', 2)){
25650             this.mpMonths.removeClass('x-date-mp-sel');
25651             pn.addClass('x-date-mp-sel');
25652             this.mpSelMonth = pn.dom.xmonth;
25653         }
25654         else if(pn = el.up('td.x-date-mp-year', 2)){
25655             this.mpYears.removeClass('x-date-mp-sel');
25656             pn.addClass('x-date-mp-sel');
25657             this.mpSelYear = pn.dom.xyear;
25658         }
25659         else if(el.is('a.x-date-mp-prev')){
25660             this.updateMPYear(this.mpyear-10);
25661         }
25662         else if(el.is('a.x-date-mp-next')){
25663             this.updateMPYear(this.mpyear+10);
25664         }
25665     },
25666
25667     onMonthDblClick : function(e, t){
25668         e.stopEvent();
25669         var el = new Roo.Element(t), pn;
25670         if(pn = el.up('td.x-date-mp-month', 2)){
25671             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25672             this.hideMonthPicker();
25673         }
25674         else if(pn = el.up('td.x-date-mp-year', 2)){
25675             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25676             this.hideMonthPicker();
25677         }
25678     },
25679
25680     hideMonthPicker : function(disableAnim){
25681         if(this.monthPicker){
25682             if(disableAnim === true){
25683                 this.monthPicker.hide();
25684             }else{
25685                 this.monthPicker.slideOut('t', {duration:.2});
25686             }
25687         }
25688     },
25689
25690     // private
25691     showPrevMonth : function(e){
25692         this.update(this.activeDate.add("mo", -1));
25693     },
25694
25695     // private
25696     showNextMonth : function(e){
25697         this.update(this.activeDate.add("mo", 1));
25698     },
25699
25700     // private
25701     showPrevYear : function(){
25702         this.update(this.activeDate.add("y", -1));
25703     },
25704
25705     // private
25706     showNextYear : function(){
25707         this.update(this.activeDate.add("y", 1));
25708     },
25709
25710     // private
25711     handleMouseWheel : function(e){
25712         var delta = e.getWheelDelta();
25713         if(delta > 0){
25714             this.showPrevMonth();
25715             e.stopEvent();
25716         } else if(delta < 0){
25717             this.showNextMonth();
25718             e.stopEvent();
25719         }
25720     },
25721
25722     // private
25723     handleDateClick : function(e, t){
25724         e.stopEvent();
25725         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25726             this.setValue(new Date(t.dateValue));
25727             this.fireEvent("select", this, this.value);
25728         }
25729     },
25730
25731     // private
25732     selectToday : function(){
25733         this.setValue(new Date().clearTime());
25734         this.fireEvent("select", this, this.value);
25735     },
25736
25737     // private
25738     update : function(date)
25739     {
25740         var vd = this.activeDate;
25741         this.activeDate = date;
25742         if(vd && this.el){
25743             var t = date.getTime();
25744             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25745                 this.cells.removeClass("x-date-selected");
25746                 this.cells.each(function(c){
25747                    if(c.dom.firstChild.dateValue == t){
25748                        c.addClass("x-date-selected");
25749                        setTimeout(function(){
25750                             try{c.dom.firstChild.focus();}catch(e){}
25751                        }, 50);
25752                        return false;
25753                    }
25754                 });
25755                 return;
25756             }
25757         }
25758         
25759         var days = date.getDaysInMonth();
25760         var firstOfMonth = date.getFirstDateOfMonth();
25761         var startingPos = firstOfMonth.getDay()-this.startDay;
25762
25763         if(startingPos <= this.startDay){
25764             startingPos += 7;
25765         }
25766
25767         var pm = date.add("mo", -1);
25768         var prevStart = pm.getDaysInMonth()-startingPos;
25769
25770         var cells = this.cells.elements;
25771         var textEls = this.textNodes;
25772         days += startingPos;
25773
25774         // convert everything to numbers so it's fast
25775         var day = 86400000;
25776         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25777         var today = new Date().clearTime().getTime();
25778         var sel = date.clearTime().getTime();
25779         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25780         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25781         var ddMatch = this.disabledDatesRE;
25782         var ddText = this.disabledDatesText;
25783         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25784         var ddaysText = this.disabledDaysText;
25785         var format = this.format;
25786
25787         var setCellClass = function(cal, cell){
25788             cell.title = "";
25789             var t = d.getTime();
25790             cell.firstChild.dateValue = t;
25791             if(t == today){
25792                 cell.className += " x-date-today";
25793                 cell.title = cal.todayText;
25794             }
25795             if(t == sel){
25796                 cell.className += " x-date-selected";
25797                 setTimeout(function(){
25798                     try{cell.firstChild.focus();}catch(e){}
25799                 }, 50);
25800             }
25801             // disabling
25802             if(t < min) {
25803                 cell.className = " x-date-disabled";
25804                 cell.title = cal.minText;
25805                 return;
25806             }
25807             if(t > max) {
25808                 cell.className = " x-date-disabled";
25809                 cell.title = cal.maxText;
25810                 return;
25811             }
25812             if(ddays){
25813                 if(ddays.indexOf(d.getDay()) != -1){
25814                     cell.title = ddaysText;
25815                     cell.className = " x-date-disabled";
25816                 }
25817             }
25818             if(ddMatch && format){
25819                 var fvalue = d.dateFormat(format);
25820                 if(ddMatch.test(fvalue)){
25821                     cell.title = ddText.replace("%0", fvalue);
25822                     cell.className = " x-date-disabled";
25823                 }
25824             }
25825         };
25826
25827         var i = 0;
25828         for(; i < startingPos; i++) {
25829             textEls[i].innerHTML = (++prevStart);
25830             d.setDate(d.getDate()+1);
25831             cells[i].className = "x-date-prevday";
25832             setCellClass(this, cells[i]);
25833         }
25834         for(; i < days; i++){
25835             intDay = i - startingPos + 1;
25836             textEls[i].innerHTML = (intDay);
25837             d.setDate(d.getDate()+1);
25838             cells[i].className = "x-date-active";
25839             setCellClass(this, cells[i]);
25840         }
25841         var extraDays = 0;
25842         for(; i < 42; i++) {
25843              textEls[i].innerHTML = (++extraDays);
25844              d.setDate(d.getDate()+1);
25845              cells[i].className = "x-date-nextday";
25846              setCellClass(this, cells[i]);
25847         }
25848
25849         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25850         this.fireEvent('monthchange', this, date);
25851         
25852         if(!this.internalRender){
25853             var main = this.el.dom.firstChild;
25854             var w = main.offsetWidth;
25855             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25856             Roo.fly(main).setWidth(w);
25857             this.internalRender = true;
25858             // opera does not respect the auto grow header center column
25859             // then, after it gets a width opera refuses to recalculate
25860             // without a second pass
25861             if(Roo.isOpera && !this.secondPass){
25862                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25863                 this.secondPass = true;
25864                 this.update.defer(10, this, [date]);
25865             }
25866         }
25867         
25868         
25869     }
25870 });        /*
25871  * Based on:
25872  * Ext JS Library 1.1.1
25873  * Copyright(c) 2006-2007, Ext JS, LLC.
25874  *
25875  * Originally Released Under LGPL - original licence link has changed is not relivant.
25876  *
25877  * Fork - LGPL
25878  * <script type="text/javascript">
25879  */
25880 /**
25881  * @class Roo.TabPanel
25882  * @extends Roo.util.Observable
25883  * A lightweight tab container.
25884  * <br><br>
25885  * Usage:
25886  * <pre><code>
25887 // basic tabs 1, built from existing content
25888 var tabs = new Roo.TabPanel("tabs1");
25889 tabs.addTab("script", "View Script");
25890 tabs.addTab("markup", "View Markup");
25891 tabs.activate("script");
25892
25893 // more advanced tabs, built from javascript
25894 var jtabs = new Roo.TabPanel("jtabs");
25895 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25896
25897 // set up the UpdateManager
25898 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25899 var updater = tab2.getUpdateManager();
25900 updater.setDefaultUrl("ajax1.htm");
25901 tab2.on('activate', updater.refresh, updater, true);
25902
25903 // Use setUrl for Ajax loading
25904 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25905 tab3.setUrl("ajax2.htm", null, true);
25906
25907 // Disabled tab
25908 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25909 tab4.disable();
25910
25911 jtabs.activate("jtabs-1");
25912  * </code></pre>
25913  * @constructor
25914  * Create a new TabPanel.
25915  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25916  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25917  */
25918 Roo.TabPanel = function(container, config){
25919     /**
25920     * The container element for this TabPanel.
25921     * @type Roo.Element
25922     */
25923     this.el = Roo.get(container, true);
25924     if(config){
25925         if(typeof config == "boolean"){
25926             this.tabPosition = config ? "bottom" : "top";
25927         }else{
25928             Roo.apply(this, config);
25929         }
25930     }
25931     if(this.tabPosition == "bottom"){
25932         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25933         this.el.addClass("x-tabs-bottom");
25934     }
25935     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25936     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25937     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25938     if(Roo.isIE){
25939         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25940     }
25941     if(this.tabPosition != "bottom"){
25942         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25943          * @type Roo.Element
25944          */
25945         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25946         this.el.addClass("x-tabs-top");
25947     }
25948     this.items = [];
25949
25950     this.bodyEl.setStyle("position", "relative");
25951
25952     this.active = null;
25953     this.activateDelegate = this.activate.createDelegate(this);
25954
25955     this.addEvents({
25956         /**
25957          * @event tabchange
25958          * Fires when the active tab changes
25959          * @param {Roo.TabPanel} this
25960          * @param {Roo.TabPanelItem} activePanel The new active tab
25961          */
25962         "tabchange": true,
25963         /**
25964          * @event beforetabchange
25965          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25966          * @param {Roo.TabPanel} this
25967          * @param {Object} e Set cancel to true on this object to cancel the tab change
25968          * @param {Roo.TabPanelItem} tab The tab being changed to
25969          */
25970         "beforetabchange" : true
25971     });
25972
25973     Roo.EventManager.onWindowResize(this.onResize, this);
25974     this.cpad = this.el.getPadding("lr");
25975     this.hiddenCount = 0;
25976
25977
25978     // toolbar on the tabbar support...
25979     if (this.toolbar) {
25980         var tcfg = this.toolbar;
25981         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25982         this.toolbar = new Roo.Toolbar(tcfg);
25983         if (Roo.isSafari) {
25984             var tbl = tcfg.container.child('table', true);
25985             tbl.setAttribute('width', '100%');
25986         }
25987         
25988     }
25989    
25990
25991
25992     Roo.TabPanel.superclass.constructor.call(this);
25993 };
25994
25995 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25996     /*
25997      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25998      */
25999     tabPosition : "top",
26000     /*
26001      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26002      */
26003     currentTabWidth : 0,
26004     /*
26005      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26006      */
26007     minTabWidth : 40,
26008     /*
26009      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26010      */
26011     maxTabWidth : 250,
26012     /*
26013      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26014      */
26015     preferredTabWidth : 175,
26016     /*
26017      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26018      */
26019     resizeTabs : false,
26020     /*
26021      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26022      */
26023     monitorResize : true,
26024     /*
26025      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26026      */
26027     toolbar : false,
26028
26029     /**
26030      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26031      * @param {String} id The id of the div to use <b>or create</b>
26032      * @param {String} text The text for the tab
26033      * @param {String} content (optional) Content to put in the TabPanelItem body
26034      * @param {Boolean} closable (optional) True to create a close icon on the tab
26035      * @return {Roo.TabPanelItem} The created TabPanelItem
26036      */
26037     addTab : function(id, text, content, closable){
26038         var item = new Roo.TabPanelItem(this, id, text, closable);
26039         this.addTabItem(item);
26040         if(content){
26041             item.setContent(content);
26042         }
26043         return item;
26044     },
26045
26046     /**
26047      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26048      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26049      * @return {Roo.TabPanelItem}
26050      */
26051     getTab : function(id){
26052         return this.items[id];
26053     },
26054
26055     /**
26056      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26057      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26058      */
26059     hideTab : function(id){
26060         var t = this.items[id];
26061         if(!t.isHidden()){
26062            t.setHidden(true);
26063            this.hiddenCount++;
26064            this.autoSizeTabs();
26065         }
26066     },
26067
26068     /**
26069      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26070      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26071      */
26072     unhideTab : function(id){
26073         var t = this.items[id];
26074         if(t.isHidden()){
26075            t.setHidden(false);
26076            this.hiddenCount--;
26077            this.autoSizeTabs();
26078         }
26079     },
26080
26081     /**
26082      * Adds an existing {@link Roo.TabPanelItem}.
26083      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26084      */
26085     addTabItem : function(item){
26086         this.items[item.id] = item;
26087         this.items.push(item);
26088         if(this.resizeTabs){
26089            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26090            this.autoSizeTabs();
26091         }else{
26092             item.autoSize();
26093         }
26094     },
26095
26096     /**
26097      * Removes a {@link Roo.TabPanelItem}.
26098      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26099      */
26100     removeTab : function(id){
26101         var items = this.items;
26102         var tab = items[id];
26103         if(!tab) { return; }
26104         var index = items.indexOf(tab);
26105         if(this.active == tab && items.length > 1){
26106             var newTab = this.getNextAvailable(index);
26107             if(newTab) {
26108                 newTab.activate();
26109             }
26110         }
26111         this.stripEl.dom.removeChild(tab.pnode.dom);
26112         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26113             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26114         }
26115         items.splice(index, 1);
26116         delete this.items[tab.id];
26117         tab.fireEvent("close", tab);
26118         tab.purgeListeners();
26119         this.autoSizeTabs();
26120     },
26121
26122     getNextAvailable : function(start){
26123         var items = this.items;
26124         var index = start;
26125         // look for a next tab that will slide over to
26126         // replace the one being removed
26127         while(index < items.length){
26128             var item = items[++index];
26129             if(item && !item.isHidden()){
26130                 return item;
26131             }
26132         }
26133         // if one isn't found select the previous tab (on the left)
26134         index = start;
26135         while(index >= 0){
26136             var item = items[--index];
26137             if(item && !item.isHidden()){
26138                 return item;
26139             }
26140         }
26141         return null;
26142     },
26143
26144     /**
26145      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26146      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26147      */
26148     disableTab : function(id){
26149         var tab = this.items[id];
26150         if(tab && this.active != tab){
26151             tab.disable();
26152         }
26153     },
26154
26155     /**
26156      * Enables a {@link Roo.TabPanelItem} that is disabled.
26157      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26158      */
26159     enableTab : function(id){
26160         var tab = this.items[id];
26161         tab.enable();
26162     },
26163
26164     /**
26165      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26166      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26167      * @return {Roo.TabPanelItem} The TabPanelItem.
26168      */
26169     activate : function(id){
26170         var tab = this.items[id];
26171         if(!tab){
26172             return null;
26173         }
26174         if(tab == this.active || tab.disabled){
26175             return tab;
26176         }
26177         var e = {};
26178         this.fireEvent("beforetabchange", this, e, tab);
26179         if(e.cancel !== true && !tab.disabled){
26180             if(this.active){
26181                 this.active.hide();
26182             }
26183             this.active = this.items[id];
26184             this.active.show();
26185             this.fireEvent("tabchange", this, this.active);
26186         }
26187         return tab;
26188     },
26189
26190     /**
26191      * Gets the active {@link Roo.TabPanelItem}.
26192      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26193      */
26194     getActiveTab : function(){
26195         return this.active;
26196     },
26197
26198     /**
26199      * Updates the tab body element to fit the height of the container element
26200      * for overflow scrolling
26201      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26202      */
26203     syncHeight : function(targetHeight){
26204         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26205         var bm = this.bodyEl.getMargins();
26206         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26207         this.bodyEl.setHeight(newHeight);
26208         return newHeight;
26209     },
26210
26211     onResize : function(){
26212         if(this.monitorResize){
26213             this.autoSizeTabs();
26214         }
26215     },
26216
26217     /**
26218      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26219      */
26220     beginUpdate : function(){
26221         this.updating = true;
26222     },
26223
26224     /**
26225      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26226      */
26227     endUpdate : function(){
26228         this.updating = false;
26229         this.autoSizeTabs();
26230     },
26231
26232     /**
26233      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26234      */
26235     autoSizeTabs : function(){
26236         var count = this.items.length;
26237         var vcount = count - this.hiddenCount;
26238         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26239         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26240         var availWidth = Math.floor(w / vcount);
26241         var b = this.stripBody;
26242         if(b.getWidth() > w){
26243             var tabs = this.items;
26244             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26245             if(availWidth < this.minTabWidth){
26246                 /*if(!this.sleft){    // incomplete scrolling code
26247                     this.createScrollButtons();
26248                 }
26249                 this.showScroll();
26250                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26251             }
26252         }else{
26253             if(this.currentTabWidth < this.preferredTabWidth){
26254                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26255             }
26256         }
26257     },
26258
26259     /**
26260      * Returns the number of tabs in this TabPanel.
26261      * @return {Number}
26262      */
26263      getCount : function(){
26264          return this.items.length;
26265      },
26266
26267     /**
26268      * Resizes all the tabs to the passed width
26269      * @param {Number} The new width
26270      */
26271     setTabWidth : function(width){
26272         this.currentTabWidth = width;
26273         for(var i = 0, len = this.items.length; i < len; i++) {
26274                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26275         }
26276     },
26277
26278     /**
26279      * Destroys this TabPanel
26280      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26281      */
26282     destroy : function(removeEl){
26283         Roo.EventManager.removeResizeListener(this.onResize, this);
26284         for(var i = 0, len = this.items.length; i < len; i++){
26285             this.items[i].purgeListeners();
26286         }
26287         if(removeEl === true){
26288             this.el.update("");
26289             this.el.remove();
26290         }
26291     }
26292 });
26293
26294 /**
26295  * @class Roo.TabPanelItem
26296  * @extends Roo.util.Observable
26297  * Represents an individual item (tab plus body) in a TabPanel.
26298  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26299  * @param {String} id The id of this TabPanelItem
26300  * @param {String} text The text for the tab of this TabPanelItem
26301  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26302  */
26303 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26304     /**
26305      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26306      * @type Roo.TabPanel
26307      */
26308     this.tabPanel = tabPanel;
26309     /**
26310      * The id for this TabPanelItem
26311      * @type String
26312      */
26313     this.id = id;
26314     /** @private */
26315     this.disabled = false;
26316     /** @private */
26317     this.text = text;
26318     /** @private */
26319     this.loaded = false;
26320     this.closable = closable;
26321
26322     /**
26323      * The body element for this TabPanelItem.
26324      * @type Roo.Element
26325      */
26326     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26327     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26328     this.bodyEl.setStyle("display", "block");
26329     this.bodyEl.setStyle("zoom", "1");
26330     this.hideAction();
26331
26332     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26333     /** @private */
26334     this.el = Roo.get(els.el, true);
26335     this.inner = Roo.get(els.inner, true);
26336     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26337     this.pnode = Roo.get(els.el.parentNode, true);
26338     this.el.on("mousedown", this.onTabMouseDown, this);
26339     this.el.on("click", this.onTabClick, this);
26340     /** @private */
26341     if(closable){
26342         var c = Roo.get(els.close, true);
26343         c.dom.title = this.closeText;
26344         c.addClassOnOver("close-over");
26345         c.on("click", this.closeClick, this);
26346      }
26347
26348     this.addEvents({
26349          /**
26350          * @event activate
26351          * Fires when this tab becomes the active tab.
26352          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26353          * @param {Roo.TabPanelItem} this
26354          */
26355         "activate": true,
26356         /**
26357          * @event beforeclose
26358          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26359          * @param {Roo.TabPanelItem} this
26360          * @param {Object} e Set cancel to true on this object to cancel the close.
26361          */
26362         "beforeclose": true,
26363         /**
26364          * @event close
26365          * Fires when this tab is closed.
26366          * @param {Roo.TabPanelItem} this
26367          */
26368          "close": true,
26369         /**
26370          * @event deactivate
26371          * Fires when this tab is no longer the active tab.
26372          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26373          * @param {Roo.TabPanelItem} this
26374          */
26375          "deactivate" : true
26376     });
26377     this.hidden = false;
26378
26379     Roo.TabPanelItem.superclass.constructor.call(this);
26380 };
26381
26382 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26383     purgeListeners : function(){
26384        Roo.util.Observable.prototype.purgeListeners.call(this);
26385        this.el.removeAllListeners();
26386     },
26387     /**
26388      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26389      */
26390     show : function(){
26391         this.pnode.addClass("on");
26392         this.showAction();
26393         if(Roo.isOpera){
26394             this.tabPanel.stripWrap.repaint();
26395         }
26396         this.fireEvent("activate", this.tabPanel, this);
26397     },
26398
26399     /**
26400      * Returns true if this tab is the active tab.
26401      * @return {Boolean}
26402      */
26403     isActive : function(){
26404         return this.tabPanel.getActiveTab() == this;
26405     },
26406
26407     /**
26408      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26409      */
26410     hide : function(){
26411         this.pnode.removeClass("on");
26412         this.hideAction();
26413         this.fireEvent("deactivate", this.tabPanel, this);
26414     },
26415
26416     hideAction : function(){
26417         this.bodyEl.hide();
26418         this.bodyEl.setStyle("position", "absolute");
26419         this.bodyEl.setLeft("-20000px");
26420         this.bodyEl.setTop("-20000px");
26421     },
26422
26423     showAction : function(){
26424         this.bodyEl.setStyle("position", "relative");
26425         this.bodyEl.setTop("");
26426         this.bodyEl.setLeft("");
26427         this.bodyEl.show();
26428     },
26429
26430     /**
26431      * Set the tooltip for the tab.
26432      * @param {String} tooltip The tab's tooltip
26433      */
26434     setTooltip : function(text){
26435         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
26436             this.textEl.dom.qtip = text;
26437             this.textEl.dom.removeAttribute('title');
26438         }else{
26439             this.textEl.dom.title = text;
26440         }
26441     },
26442
26443     onTabClick : function(e){
26444         e.preventDefault();
26445         this.tabPanel.activate(this.id);
26446     },
26447
26448     onTabMouseDown : function(e){
26449         e.preventDefault();
26450         this.tabPanel.activate(this.id);
26451     },
26452
26453     getWidth : function(){
26454         return this.inner.getWidth();
26455     },
26456
26457     setWidth : function(width){
26458         var iwidth = width - this.pnode.getPadding("lr");
26459         this.inner.setWidth(iwidth);
26460         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
26461         this.pnode.setWidth(width);
26462     },
26463
26464     /**
26465      * Show or hide the tab
26466      * @param {Boolean} hidden True to hide or false to show.
26467      */
26468     setHidden : function(hidden){
26469         this.hidden = hidden;
26470         this.pnode.setStyle("display", hidden ? "none" : "");
26471     },
26472
26473     /**
26474      * Returns true if this tab is "hidden"
26475      * @return {Boolean}
26476      */
26477     isHidden : function(){
26478         return this.hidden;
26479     },
26480
26481     /**
26482      * Returns the text for this tab
26483      * @return {String}
26484      */
26485     getText : function(){
26486         return this.text;
26487     },
26488
26489     autoSize : function(){
26490         //this.el.beginMeasure();
26491         this.textEl.setWidth(1);
26492         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
26493         //this.el.endMeasure();
26494     },
26495
26496     /**
26497      * Sets the text for the tab (Note: this also sets the tooltip text)
26498      * @param {String} text The tab's text and tooltip
26499      */
26500     setText : function(text){
26501         this.text = text;
26502         this.textEl.update(text);
26503         this.setTooltip(text);
26504         if(!this.tabPanel.resizeTabs){
26505             this.autoSize();
26506         }
26507     },
26508     /**
26509      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
26510      */
26511     activate : function(){
26512         this.tabPanel.activate(this.id);
26513     },
26514
26515     /**
26516      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
26517      */
26518     disable : function(){
26519         if(this.tabPanel.active != this){
26520             this.disabled = true;
26521             this.pnode.addClass("disabled");
26522         }
26523     },
26524
26525     /**
26526      * Enables this TabPanelItem if it was previously disabled.
26527      */
26528     enable : function(){
26529         this.disabled = false;
26530         this.pnode.removeClass("disabled");
26531     },
26532
26533     /**
26534      * Sets the content for this TabPanelItem.
26535      * @param {String} content The content
26536      * @param {Boolean} loadScripts true to look for and load scripts
26537      */
26538     setContent : function(content, loadScripts){
26539         this.bodyEl.update(content, loadScripts);
26540     },
26541
26542     /**
26543      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26544      * @return {Roo.UpdateManager} The UpdateManager
26545      */
26546     getUpdateManager : function(){
26547         return this.bodyEl.getUpdateManager();
26548     },
26549
26550     /**
26551      * Set a URL to be used to load the content for this TabPanelItem.
26552      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26553      * @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)
26554      * @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)
26555      * @return {Roo.UpdateManager} The UpdateManager
26556      */
26557     setUrl : function(url, params, loadOnce){
26558         if(this.refreshDelegate){
26559             this.un('activate', this.refreshDelegate);
26560         }
26561         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26562         this.on("activate", this.refreshDelegate);
26563         return this.bodyEl.getUpdateManager();
26564     },
26565
26566     /** @private */
26567     _handleRefresh : function(url, params, loadOnce){
26568         if(!loadOnce || !this.loaded){
26569             var updater = this.bodyEl.getUpdateManager();
26570             updater.update(url, params, this._setLoaded.createDelegate(this));
26571         }
26572     },
26573
26574     /**
26575      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26576      *   Will fail silently if the setUrl method has not been called.
26577      *   This does not activate the panel, just updates its content.
26578      */
26579     refresh : function(){
26580         if(this.refreshDelegate){
26581            this.loaded = false;
26582            this.refreshDelegate();
26583         }
26584     },
26585
26586     /** @private */
26587     _setLoaded : function(){
26588         this.loaded = true;
26589     },
26590
26591     /** @private */
26592     closeClick : function(e){
26593         var o = {};
26594         e.stopEvent();
26595         this.fireEvent("beforeclose", this, o);
26596         if(o.cancel !== true){
26597             this.tabPanel.removeTab(this.id);
26598         }
26599     },
26600     /**
26601      * The text displayed in the tooltip for the close icon.
26602      * @type String
26603      */
26604     closeText : "Close this tab"
26605 });
26606
26607 /** @private */
26608 Roo.TabPanel.prototype.createStrip = function(container){
26609     var strip = document.createElement("div");
26610     strip.className = "x-tabs-wrap";
26611     container.appendChild(strip);
26612     return strip;
26613 };
26614 /** @private */
26615 Roo.TabPanel.prototype.createStripList = function(strip){
26616     // div wrapper for retard IE
26617     // returns the "tr" element.
26618     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26619         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26620         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26621     return strip.firstChild.firstChild.firstChild.firstChild;
26622 };
26623 /** @private */
26624 Roo.TabPanel.prototype.createBody = function(container){
26625     var body = document.createElement("div");
26626     Roo.id(body, "tab-body");
26627     Roo.fly(body).addClass("x-tabs-body");
26628     container.appendChild(body);
26629     return body;
26630 };
26631 /** @private */
26632 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26633     var body = Roo.getDom(id);
26634     if(!body){
26635         body = document.createElement("div");
26636         body.id = id;
26637     }
26638     Roo.fly(body).addClass("x-tabs-item-body");
26639     bodyEl.insertBefore(body, bodyEl.firstChild);
26640     return body;
26641 };
26642 /** @private */
26643 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26644     var td = document.createElement("td");
26645     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26646     //stripEl.appendChild(td);
26647     if(closable){
26648         td.className = "x-tabs-closable";
26649         if(!this.closeTpl){
26650             this.closeTpl = new Roo.Template(
26651                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26652                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26653                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26654             );
26655         }
26656         var el = this.closeTpl.overwrite(td, {"text": text});
26657         var close = el.getElementsByTagName("div")[0];
26658         var inner = el.getElementsByTagName("em")[0];
26659         return {"el": el, "close": close, "inner": inner};
26660     } else {
26661         if(!this.tabTpl){
26662             this.tabTpl = new Roo.Template(
26663                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26664                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26665             );
26666         }
26667         var el = this.tabTpl.overwrite(td, {"text": text});
26668         var inner = el.getElementsByTagName("em")[0];
26669         return {"el": el, "inner": inner};
26670     }
26671 };/*
26672  * Based on:
26673  * Ext JS Library 1.1.1
26674  * Copyright(c) 2006-2007, Ext JS, LLC.
26675  *
26676  * Originally Released Under LGPL - original licence link has changed is not relivant.
26677  *
26678  * Fork - LGPL
26679  * <script type="text/javascript">
26680  */
26681
26682 /**
26683  * @class Roo.Button
26684  * @extends Roo.util.Observable
26685  * Simple Button class
26686  * @cfg {String} text The button text
26687  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26688  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26689  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26690  * @cfg {Object} scope The scope of the handler
26691  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26692  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26693  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26694  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26695  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26696  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26697    applies if enableToggle = true)
26698  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26699  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26700   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26701  * @constructor
26702  * Create a new button
26703  * @param {Object} config The config object
26704  */
26705 Roo.Button = function(renderTo, config)
26706 {
26707     if (!config) {
26708         config = renderTo;
26709         renderTo = config.renderTo || false;
26710     }
26711     
26712     Roo.apply(this, config);
26713     this.addEvents({
26714         /**
26715              * @event click
26716              * Fires when this button is clicked
26717              * @param {Button} this
26718              * @param {EventObject} e The click event
26719              */
26720             "click" : true,
26721         /**
26722              * @event toggle
26723              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26724              * @param {Button} this
26725              * @param {Boolean} pressed
26726              */
26727             "toggle" : true,
26728         /**
26729              * @event mouseover
26730              * Fires when the mouse hovers over the button
26731              * @param {Button} this
26732              * @param {Event} e The event object
26733              */
26734         'mouseover' : true,
26735         /**
26736              * @event mouseout
26737              * Fires when the mouse exits the button
26738              * @param {Button} this
26739              * @param {Event} e The event object
26740              */
26741         'mouseout': true,
26742          /**
26743              * @event render
26744              * Fires when the button is rendered
26745              * @param {Button} this
26746              */
26747         'render': true
26748     });
26749     if(this.menu){
26750         this.menu = Roo.menu.MenuMgr.get(this.menu);
26751     }
26752     // register listeners first!!  - so render can be captured..
26753     Roo.util.Observable.call(this);
26754     if(renderTo){
26755         this.render(renderTo);
26756     }
26757     
26758   
26759 };
26760
26761 Roo.extend(Roo.Button, Roo.util.Observable, {
26762     /**
26763      * 
26764      */
26765     
26766     /**
26767      * Read-only. True if this button is hidden
26768      * @type Boolean
26769      */
26770     hidden : false,
26771     /**
26772      * Read-only. True if this button is disabled
26773      * @type Boolean
26774      */
26775     disabled : false,
26776     /**
26777      * Read-only. True if this button is pressed (only if enableToggle = true)
26778      * @type Boolean
26779      */
26780     pressed : false,
26781
26782     /**
26783      * @cfg {Number} tabIndex 
26784      * The DOM tabIndex for this button (defaults to undefined)
26785      */
26786     tabIndex : undefined,
26787
26788     /**
26789      * @cfg {Boolean} enableToggle
26790      * True to enable pressed/not pressed toggling (defaults to false)
26791      */
26792     enableToggle: false,
26793     /**
26794      * @cfg {Mixed} menu
26795      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26796      */
26797     menu : undefined,
26798     /**
26799      * @cfg {String} menuAlign
26800      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26801      */
26802     menuAlign : "tl-bl?",
26803
26804     /**
26805      * @cfg {String} iconCls
26806      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26807      */
26808     iconCls : undefined,
26809     /**
26810      * @cfg {String} type
26811      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26812      */
26813     type : 'button',
26814
26815     // private
26816     menuClassTarget: 'tr',
26817
26818     /**
26819      * @cfg {String} clickEvent
26820      * The type of event to map to the button's event handler (defaults to 'click')
26821      */
26822     clickEvent : 'click',
26823
26824     /**
26825      * @cfg {Boolean} handleMouseEvents
26826      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26827      */
26828     handleMouseEvents : true,
26829
26830     /**
26831      * @cfg {String} tooltipType
26832      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26833      */
26834     tooltipType : 'qtip',
26835
26836     /**
26837      * @cfg {String} cls
26838      * A CSS class to apply to the button's main element.
26839      */
26840     
26841     /**
26842      * @cfg {Roo.Template} template (Optional)
26843      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26844      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26845      * require code modifications if required elements (e.g. a button) aren't present.
26846      */
26847
26848     // private
26849     render : function(renderTo){
26850         var btn;
26851         if(this.hideParent){
26852             this.parentEl = Roo.get(renderTo);
26853         }
26854         if(!this.dhconfig){
26855             if(!this.template){
26856                 if(!Roo.Button.buttonTemplate){
26857                     // hideous table template
26858                     Roo.Button.buttonTemplate = new Roo.Template(
26859                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26860                         '<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>',
26861                         "</tr></tbody></table>");
26862                 }
26863                 this.template = Roo.Button.buttonTemplate;
26864             }
26865             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26866             var btnEl = btn.child("button:first");
26867             btnEl.on('focus', this.onFocus, this);
26868             btnEl.on('blur', this.onBlur, this);
26869             if(this.cls){
26870                 btn.addClass(this.cls);
26871             }
26872             if(this.icon){
26873                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26874             }
26875             if(this.iconCls){
26876                 btnEl.addClass(this.iconCls);
26877                 if(!this.cls){
26878                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26879                 }
26880             }
26881             if(this.tabIndex !== undefined){
26882                 btnEl.dom.tabIndex = this.tabIndex;
26883             }
26884             if(this.tooltip){
26885                 if(typeof this.tooltip == 'object'){
26886                     Roo.QuickTips.tips(Roo.apply({
26887                           target: btnEl.id
26888                     }, this.tooltip));
26889                 } else {
26890                     btnEl.dom[this.tooltipType] = this.tooltip;
26891                 }
26892             }
26893         }else{
26894             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26895         }
26896         this.el = btn;
26897         if(this.id){
26898             this.el.dom.id = this.el.id = this.id;
26899         }
26900         if(this.menu){
26901             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26902             this.menu.on("show", this.onMenuShow, this);
26903             this.menu.on("hide", this.onMenuHide, this);
26904         }
26905         btn.addClass("x-btn");
26906         if(Roo.isIE && !Roo.isIE7){
26907             this.autoWidth.defer(1, this);
26908         }else{
26909             this.autoWidth();
26910         }
26911         if(this.handleMouseEvents){
26912             btn.on("mouseover", this.onMouseOver, this);
26913             btn.on("mouseout", this.onMouseOut, this);
26914             btn.on("mousedown", this.onMouseDown, this);
26915         }
26916         btn.on(this.clickEvent, this.onClick, this);
26917         //btn.on("mouseup", this.onMouseUp, this);
26918         if(this.hidden){
26919             this.hide();
26920         }
26921         if(this.disabled){
26922             this.disable();
26923         }
26924         Roo.ButtonToggleMgr.register(this);
26925         if(this.pressed){
26926             this.el.addClass("x-btn-pressed");
26927         }
26928         if(this.repeat){
26929             var repeater = new Roo.util.ClickRepeater(btn,
26930                 typeof this.repeat == "object" ? this.repeat : {}
26931             );
26932             repeater.on("click", this.onClick,  this);
26933         }
26934         
26935         this.fireEvent('render', this);
26936         
26937     },
26938     /**
26939      * Returns the button's underlying element
26940      * @return {Roo.Element} The element
26941      */
26942     getEl : function(){
26943         return this.el;  
26944     },
26945     
26946     /**
26947      * Destroys this Button and removes any listeners.
26948      */
26949     destroy : function(){
26950         Roo.ButtonToggleMgr.unregister(this);
26951         this.el.removeAllListeners();
26952         this.purgeListeners();
26953         this.el.remove();
26954     },
26955
26956     // private
26957     autoWidth : function(){
26958         if(this.el){
26959             this.el.setWidth("auto");
26960             if(Roo.isIE7 && Roo.isStrict){
26961                 var ib = this.el.child('button');
26962                 if(ib && ib.getWidth() > 20){
26963                     ib.clip();
26964                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26965                 }
26966             }
26967             if(this.minWidth){
26968                 if(this.hidden){
26969                     this.el.beginMeasure();
26970                 }
26971                 if(this.el.getWidth() < this.minWidth){
26972                     this.el.setWidth(this.minWidth);
26973                 }
26974                 if(this.hidden){
26975                     this.el.endMeasure();
26976                 }
26977             }
26978         }
26979     },
26980
26981     /**
26982      * Assigns this button's click handler
26983      * @param {Function} handler The function to call when the button is clicked
26984      * @param {Object} scope (optional) Scope for the function passed in
26985      */
26986     setHandler : function(handler, scope){
26987         this.handler = handler;
26988         this.scope = scope;  
26989     },
26990     
26991     /**
26992      * Sets this button's text
26993      * @param {String} text The button text
26994      */
26995     setText : function(text){
26996         this.text = text;
26997         if(this.el){
26998             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26999         }
27000         this.autoWidth();
27001     },
27002     
27003     /**
27004      * Gets the text for this button
27005      * @return {String} The button text
27006      */
27007     getText : function(){
27008         return this.text;  
27009     },
27010     
27011     /**
27012      * Show this button
27013      */
27014     show: function(){
27015         this.hidden = false;
27016         if(this.el){
27017             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27018         }
27019     },
27020     
27021     /**
27022      * Hide this button
27023      */
27024     hide: function(){
27025         this.hidden = true;
27026         if(this.el){
27027             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27028         }
27029     },
27030     
27031     /**
27032      * Convenience function for boolean show/hide
27033      * @param {Boolean} visible True to show, false to hide
27034      */
27035     setVisible: function(visible){
27036         if(visible) {
27037             this.show();
27038         }else{
27039             this.hide();
27040         }
27041     },
27042     
27043     /**
27044      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27045      * @param {Boolean} state (optional) Force a particular state
27046      */
27047     toggle : function(state){
27048         state = state === undefined ? !this.pressed : state;
27049         if(state != this.pressed){
27050             if(state){
27051                 this.el.addClass("x-btn-pressed");
27052                 this.pressed = true;
27053                 this.fireEvent("toggle", this, true);
27054             }else{
27055                 this.el.removeClass("x-btn-pressed");
27056                 this.pressed = false;
27057                 this.fireEvent("toggle", this, false);
27058             }
27059             if(this.toggleHandler){
27060                 this.toggleHandler.call(this.scope || this, this, state);
27061             }
27062         }
27063     },
27064     
27065     /**
27066      * Focus the button
27067      */
27068     focus : function(){
27069         this.el.child('button:first').focus();
27070     },
27071     
27072     /**
27073      * Disable this button
27074      */
27075     disable : function(){
27076         if(this.el){
27077             this.el.addClass("x-btn-disabled");
27078         }
27079         this.disabled = true;
27080     },
27081     
27082     /**
27083      * Enable this button
27084      */
27085     enable : function(){
27086         if(this.el){
27087             this.el.removeClass("x-btn-disabled");
27088         }
27089         this.disabled = false;
27090     },
27091
27092     /**
27093      * Convenience function for boolean enable/disable
27094      * @param {Boolean} enabled True to enable, false to disable
27095      */
27096     setDisabled : function(v){
27097         this[v !== true ? "enable" : "disable"]();
27098     },
27099
27100     // private
27101     onClick : function(e){
27102         if(e){
27103             e.preventDefault();
27104         }
27105         if(e.button != 0){
27106             return;
27107         }
27108         if(!this.disabled){
27109             if(this.enableToggle){
27110                 this.toggle();
27111             }
27112             if(this.menu && !this.menu.isVisible()){
27113                 this.menu.show(this.el, this.menuAlign);
27114             }
27115             this.fireEvent("click", this, e);
27116             if(this.handler){
27117                 this.el.removeClass("x-btn-over");
27118                 this.handler.call(this.scope || this, this, e);
27119             }
27120         }
27121     },
27122     // private
27123     onMouseOver : function(e){
27124         if(!this.disabled){
27125             this.el.addClass("x-btn-over");
27126             this.fireEvent('mouseover', this, e);
27127         }
27128     },
27129     // private
27130     onMouseOut : function(e){
27131         if(!e.within(this.el,  true)){
27132             this.el.removeClass("x-btn-over");
27133             this.fireEvent('mouseout', this, e);
27134         }
27135     },
27136     // private
27137     onFocus : function(e){
27138         if(!this.disabled){
27139             this.el.addClass("x-btn-focus");
27140         }
27141     },
27142     // private
27143     onBlur : function(e){
27144         this.el.removeClass("x-btn-focus");
27145     },
27146     // private
27147     onMouseDown : function(e){
27148         if(!this.disabled && e.button == 0){
27149             this.el.addClass("x-btn-click");
27150             Roo.get(document).on('mouseup', this.onMouseUp, this);
27151         }
27152     },
27153     // private
27154     onMouseUp : function(e){
27155         if(e.button == 0){
27156             this.el.removeClass("x-btn-click");
27157             Roo.get(document).un('mouseup', this.onMouseUp, this);
27158         }
27159     },
27160     // private
27161     onMenuShow : function(e){
27162         this.el.addClass("x-btn-menu-active");
27163     },
27164     // private
27165     onMenuHide : function(e){
27166         this.el.removeClass("x-btn-menu-active");
27167     }   
27168 });
27169
27170 // Private utility class used by Button
27171 Roo.ButtonToggleMgr = function(){
27172    var groups = {};
27173    
27174    function toggleGroup(btn, state){
27175        if(state){
27176            var g = groups[btn.toggleGroup];
27177            for(var i = 0, l = g.length; i < l; i++){
27178                if(g[i] != btn){
27179                    g[i].toggle(false);
27180                }
27181            }
27182        }
27183    }
27184    
27185    return {
27186        register : function(btn){
27187            if(!btn.toggleGroup){
27188                return;
27189            }
27190            var g = groups[btn.toggleGroup];
27191            if(!g){
27192                g = groups[btn.toggleGroup] = [];
27193            }
27194            g.push(btn);
27195            btn.on("toggle", toggleGroup);
27196        },
27197        
27198        unregister : function(btn){
27199            if(!btn.toggleGroup){
27200                return;
27201            }
27202            var g = groups[btn.toggleGroup];
27203            if(g){
27204                g.remove(btn);
27205                btn.un("toggle", toggleGroup);
27206            }
27207        }
27208    };
27209 }();/*
27210  * Based on:
27211  * Ext JS Library 1.1.1
27212  * Copyright(c) 2006-2007, Ext JS, LLC.
27213  *
27214  * Originally Released Under LGPL - original licence link has changed is not relivant.
27215  *
27216  * Fork - LGPL
27217  * <script type="text/javascript">
27218  */
27219  
27220 /**
27221  * @class Roo.SplitButton
27222  * @extends Roo.Button
27223  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27224  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27225  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27226  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27227  * @cfg {String} arrowTooltip The title attribute of the arrow
27228  * @constructor
27229  * Create a new menu button
27230  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27231  * @param {Object} config The config object
27232  */
27233 Roo.SplitButton = function(renderTo, config){
27234     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27235     /**
27236      * @event arrowclick
27237      * Fires when this button's arrow is clicked
27238      * @param {SplitButton} this
27239      * @param {EventObject} e The click event
27240      */
27241     this.addEvents({"arrowclick":true});
27242 };
27243
27244 Roo.extend(Roo.SplitButton, Roo.Button, {
27245     render : function(renderTo){
27246         // this is one sweet looking template!
27247         var tpl = new Roo.Template(
27248             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27249             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27250             '<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>',
27251             "</tbody></table></td><td>",
27252             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27253             '<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>',
27254             "</tbody></table></td></tr></table>"
27255         );
27256         var btn = tpl.append(renderTo, [this.text, this.type], true);
27257         var btnEl = btn.child("button");
27258         if(this.cls){
27259             btn.addClass(this.cls);
27260         }
27261         if(this.icon){
27262             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27263         }
27264         if(this.iconCls){
27265             btnEl.addClass(this.iconCls);
27266             if(!this.cls){
27267                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27268             }
27269         }
27270         this.el = btn;
27271         if(this.handleMouseEvents){
27272             btn.on("mouseover", this.onMouseOver, this);
27273             btn.on("mouseout", this.onMouseOut, this);
27274             btn.on("mousedown", this.onMouseDown, this);
27275             btn.on("mouseup", this.onMouseUp, this);
27276         }
27277         btn.on(this.clickEvent, this.onClick, this);
27278         if(this.tooltip){
27279             if(typeof this.tooltip == 'object'){
27280                 Roo.QuickTips.tips(Roo.apply({
27281                       target: btnEl.id
27282                 }, this.tooltip));
27283             } else {
27284                 btnEl.dom[this.tooltipType] = this.tooltip;
27285             }
27286         }
27287         if(this.arrowTooltip){
27288             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27289         }
27290         if(this.hidden){
27291             this.hide();
27292         }
27293         if(this.disabled){
27294             this.disable();
27295         }
27296         if(this.pressed){
27297             this.el.addClass("x-btn-pressed");
27298         }
27299         if(Roo.isIE && !Roo.isIE7){
27300             this.autoWidth.defer(1, this);
27301         }else{
27302             this.autoWidth();
27303         }
27304         if(this.menu){
27305             this.menu.on("show", this.onMenuShow, this);
27306             this.menu.on("hide", this.onMenuHide, this);
27307         }
27308         this.fireEvent('render', this);
27309     },
27310
27311     // private
27312     autoWidth : function(){
27313         if(this.el){
27314             var tbl = this.el.child("table:first");
27315             var tbl2 = this.el.child("table:last");
27316             this.el.setWidth("auto");
27317             tbl.setWidth("auto");
27318             if(Roo.isIE7 && Roo.isStrict){
27319                 var ib = this.el.child('button:first');
27320                 if(ib && ib.getWidth() > 20){
27321                     ib.clip();
27322                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27323                 }
27324             }
27325             if(this.minWidth){
27326                 if(this.hidden){
27327                     this.el.beginMeasure();
27328                 }
27329                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27330                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27331                 }
27332                 if(this.hidden){
27333                     this.el.endMeasure();
27334                 }
27335             }
27336             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27337         } 
27338     },
27339     /**
27340      * Sets this button's click handler
27341      * @param {Function} handler The function to call when the button is clicked
27342      * @param {Object} scope (optional) Scope for the function passed above
27343      */
27344     setHandler : function(handler, scope){
27345         this.handler = handler;
27346         this.scope = scope;  
27347     },
27348     
27349     /**
27350      * Sets this button's arrow click handler
27351      * @param {Function} handler The function to call when the arrow is clicked
27352      * @param {Object} scope (optional) Scope for the function passed above
27353      */
27354     setArrowHandler : function(handler, scope){
27355         this.arrowHandler = handler;
27356         this.scope = scope;  
27357     },
27358     
27359     /**
27360      * Focus the button
27361      */
27362     focus : function(){
27363         if(this.el){
27364             this.el.child("button:first").focus();
27365         }
27366     },
27367
27368     // private
27369     onClick : function(e){
27370         e.preventDefault();
27371         if(!this.disabled){
27372             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27373                 if(this.menu && !this.menu.isVisible()){
27374                     this.menu.show(this.el, this.menuAlign);
27375                 }
27376                 this.fireEvent("arrowclick", this, e);
27377                 if(this.arrowHandler){
27378                     this.arrowHandler.call(this.scope || this, this, e);
27379                 }
27380             }else{
27381                 this.fireEvent("click", this, e);
27382                 if(this.handler){
27383                     this.handler.call(this.scope || this, this, e);
27384                 }
27385             }
27386         }
27387     },
27388     // private
27389     onMouseDown : function(e){
27390         if(!this.disabled){
27391             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27392         }
27393     },
27394     // private
27395     onMouseUp : function(e){
27396         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27397     }   
27398 });
27399
27400
27401 // backwards compat
27402 Roo.MenuButton = Roo.SplitButton;/*
27403  * Based on:
27404  * Ext JS Library 1.1.1
27405  * Copyright(c) 2006-2007, Ext JS, LLC.
27406  *
27407  * Originally Released Under LGPL - original licence link has changed is not relivant.
27408  *
27409  * Fork - LGPL
27410  * <script type="text/javascript">
27411  */
27412
27413 /**
27414  * @class Roo.Toolbar
27415  * Basic Toolbar class.
27416  * @constructor
27417  * Creates a new Toolbar
27418  * @param {Object} container The config object
27419  */ 
27420 Roo.Toolbar = function(container, buttons, config)
27421 {
27422     /// old consturctor format still supported..
27423     if(container instanceof Array){ // omit the container for later rendering
27424         buttons = container;
27425         config = buttons;
27426         container = null;
27427     }
27428     if (typeof(container) == 'object' && container.xtype) {
27429         config = container;
27430         container = config.container;
27431         buttons = config.buttons || []; // not really - use items!!
27432     }
27433     var xitems = [];
27434     if (config && config.items) {
27435         xitems = config.items;
27436         delete config.items;
27437     }
27438     Roo.apply(this, config);
27439     this.buttons = buttons;
27440     
27441     if(container){
27442         this.render(container);
27443     }
27444     this.xitems = xitems;
27445     Roo.each(xitems, function(b) {
27446         this.add(b);
27447     }, this);
27448     
27449 };
27450
27451 Roo.Toolbar.prototype = {
27452     /**
27453      * @cfg {Array} items
27454      * array of button configs or elements to add (will be converted to a MixedCollection)
27455      */
27456     
27457     /**
27458      * @cfg {String/HTMLElement/Element} container
27459      * The id or element that will contain the toolbar
27460      */
27461     // private
27462     render : function(ct){
27463         this.el = Roo.get(ct);
27464         if(this.cls){
27465             this.el.addClass(this.cls);
27466         }
27467         // using a table allows for vertical alignment
27468         // 100% width is needed by Safari...
27469         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
27470         this.tr = this.el.child("tr", true);
27471         var autoId = 0;
27472         this.items = new Roo.util.MixedCollection(false, function(o){
27473             return o.id || ("item" + (++autoId));
27474         });
27475         if(this.buttons){
27476             this.add.apply(this, this.buttons);
27477             delete this.buttons;
27478         }
27479     },
27480
27481     /**
27482      * Adds element(s) to the toolbar -- this function takes a variable number of 
27483      * arguments of mixed type and adds them to the toolbar.
27484      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
27485      * <ul>
27486      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
27487      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
27488      * <li>Field: Any form field (equivalent to {@link #addField})</li>
27489      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
27490      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
27491      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
27492      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
27493      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
27494      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
27495      * </ul>
27496      * @param {Mixed} arg2
27497      * @param {Mixed} etc.
27498      */
27499     add : function(){
27500         var a = arguments, l = a.length;
27501         for(var i = 0; i < l; i++){
27502             this._add(a[i]);
27503         }
27504     },
27505     // private..
27506     _add : function(el) {
27507         
27508         if (el.xtype) {
27509             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
27510         }
27511         
27512         if (el.applyTo){ // some kind of form field
27513             return this.addField(el);
27514         } 
27515         if (el.render){ // some kind of Toolbar.Item
27516             return this.addItem(el);
27517         }
27518         if (typeof el == "string"){ // string
27519             if(el == "separator" || el == "-"){
27520                 return this.addSeparator();
27521             }
27522             if (el == " "){
27523                 return this.addSpacer();
27524             }
27525             if(el == "->"){
27526                 return this.addFill();
27527             }
27528             return this.addText(el);
27529             
27530         }
27531         if(el.tagName){ // element
27532             return this.addElement(el);
27533         }
27534         if(typeof el == "object"){ // must be button config?
27535             return this.addButton(el);
27536         }
27537         // and now what?!?!
27538         return false;
27539         
27540     },
27541     
27542     /**
27543      * Add an Xtype element
27544      * @param {Object} xtype Xtype Object
27545      * @return {Object} created Object
27546      */
27547     addxtype : function(e){
27548         return this.add(e);  
27549     },
27550     
27551     /**
27552      * Returns the Element for this toolbar.
27553      * @return {Roo.Element}
27554      */
27555     getEl : function(){
27556         return this.el;  
27557     },
27558     
27559     /**
27560      * Adds a separator
27561      * @return {Roo.Toolbar.Item} The separator item
27562      */
27563     addSeparator : function(){
27564         return this.addItem(new Roo.Toolbar.Separator());
27565     },
27566
27567     /**
27568      * Adds a spacer element
27569      * @return {Roo.Toolbar.Spacer} The spacer item
27570      */
27571     addSpacer : function(){
27572         return this.addItem(new Roo.Toolbar.Spacer());
27573     },
27574
27575     /**
27576      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27577      * @return {Roo.Toolbar.Fill} The fill item
27578      */
27579     addFill : function(){
27580         return this.addItem(new Roo.Toolbar.Fill());
27581     },
27582
27583     /**
27584      * Adds any standard HTML element to the toolbar
27585      * @param {String/HTMLElement/Element} el The element or id of the element to add
27586      * @return {Roo.Toolbar.Item} The element's item
27587      */
27588     addElement : function(el){
27589         return this.addItem(new Roo.Toolbar.Item(el));
27590     },
27591     /**
27592      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27593      * @type Roo.util.MixedCollection  
27594      */
27595     items : false,
27596      
27597     /**
27598      * Adds any Toolbar.Item or subclass
27599      * @param {Roo.Toolbar.Item} item
27600      * @return {Roo.Toolbar.Item} The item
27601      */
27602     addItem : function(item){
27603         var td = this.nextBlock();
27604         item.render(td);
27605         this.items.add(item);
27606         return item;
27607     },
27608     
27609     /**
27610      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27611      * @param {Object/Array} config A button config or array of configs
27612      * @return {Roo.Toolbar.Button/Array}
27613      */
27614     addButton : function(config){
27615         if(config instanceof Array){
27616             var buttons = [];
27617             for(var i = 0, len = config.length; i < len; i++) {
27618                 buttons.push(this.addButton(config[i]));
27619             }
27620             return buttons;
27621         }
27622         var b = config;
27623         if(!(config instanceof Roo.Toolbar.Button)){
27624             b = config.split ?
27625                 new Roo.Toolbar.SplitButton(config) :
27626                 new Roo.Toolbar.Button(config);
27627         }
27628         var td = this.nextBlock();
27629         b.render(td);
27630         this.items.add(b);
27631         return b;
27632     },
27633     
27634     /**
27635      * Adds text to the toolbar
27636      * @param {String} text The text to add
27637      * @return {Roo.Toolbar.Item} The element's item
27638      */
27639     addText : function(text){
27640         return this.addItem(new Roo.Toolbar.TextItem(text));
27641     },
27642     
27643     /**
27644      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27645      * @param {Number} index The index where the item is to be inserted
27646      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27647      * @return {Roo.Toolbar.Button/Item}
27648      */
27649     insertButton : function(index, item){
27650         if(item instanceof Array){
27651             var buttons = [];
27652             for(var i = 0, len = item.length; i < len; i++) {
27653                buttons.push(this.insertButton(index + i, item[i]));
27654             }
27655             return buttons;
27656         }
27657         if (!(item instanceof Roo.Toolbar.Button)){
27658            item = new Roo.Toolbar.Button(item);
27659         }
27660         var td = document.createElement("td");
27661         this.tr.insertBefore(td, this.tr.childNodes[index]);
27662         item.render(td);
27663         this.items.insert(index, item);
27664         return item;
27665     },
27666     
27667     /**
27668      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27669      * @param {Object} config
27670      * @return {Roo.Toolbar.Item} The element's item
27671      */
27672     addDom : function(config, returnEl){
27673         var td = this.nextBlock();
27674         Roo.DomHelper.overwrite(td, config);
27675         var ti = new Roo.Toolbar.Item(td.firstChild);
27676         ti.render(td);
27677         this.items.add(ti);
27678         return ti;
27679     },
27680
27681     /**
27682      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27683      * @type Roo.util.MixedCollection  
27684      */
27685     fields : false,
27686     
27687     /**
27688      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27689      * Note: the field should not have been rendered yet. For a field that has already been
27690      * rendered, use {@link #addElement}.
27691      * @param {Roo.form.Field} field
27692      * @return {Roo.ToolbarItem}
27693      */
27694      
27695       
27696     addField : function(field) {
27697         if (!this.fields) {
27698             var autoId = 0;
27699             this.fields = new Roo.util.MixedCollection(false, function(o){
27700                 return o.id || ("item" + (++autoId));
27701             });
27702
27703         }
27704         
27705         var td = this.nextBlock();
27706         field.render(td);
27707         var ti = new Roo.Toolbar.Item(td.firstChild);
27708         ti.render(td);
27709         this.items.add(ti);
27710         this.fields.add(field);
27711         return ti;
27712     },
27713     /**
27714      * Hide the toolbar
27715      * @method hide
27716      */
27717      
27718       
27719     hide : function()
27720     {
27721         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27722         this.el.child('div').hide();
27723     },
27724     /**
27725      * Show the toolbar
27726      * @method show
27727      */
27728     show : function()
27729     {
27730         this.el.child('div').show();
27731     },
27732       
27733     // private
27734     nextBlock : function(){
27735         var td = document.createElement("td");
27736         this.tr.appendChild(td);
27737         return td;
27738     },
27739
27740     // private
27741     destroy : function(){
27742         if(this.items){ // rendered?
27743             Roo.destroy.apply(Roo, this.items.items);
27744         }
27745         if(this.fields){ // rendered?
27746             Roo.destroy.apply(Roo, this.fields.items);
27747         }
27748         Roo.Element.uncache(this.el, this.tr);
27749     }
27750 };
27751
27752 /**
27753  * @class Roo.Toolbar.Item
27754  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27755  * @constructor
27756  * Creates a new Item
27757  * @param {HTMLElement} el 
27758  */
27759 Roo.Toolbar.Item = function(el){
27760     this.el = Roo.getDom(el);
27761     this.id = Roo.id(this.el);
27762     this.hidden = false;
27763 };
27764
27765 Roo.Toolbar.Item.prototype = {
27766     
27767     /**
27768      * Get this item's HTML Element
27769      * @return {HTMLElement}
27770      */
27771     getEl : function(){
27772        return this.el;  
27773     },
27774
27775     // private
27776     render : function(td){
27777         this.td = td;
27778         td.appendChild(this.el);
27779     },
27780     
27781     /**
27782      * Removes and destroys this item.
27783      */
27784     destroy : function(){
27785         this.td.parentNode.removeChild(this.td);
27786     },
27787     
27788     /**
27789      * Shows this item.
27790      */
27791     show: function(){
27792         this.hidden = false;
27793         this.td.style.display = "";
27794     },
27795     
27796     /**
27797      * Hides this item.
27798      */
27799     hide: function(){
27800         this.hidden = true;
27801         this.td.style.display = "none";
27802     },
27803     
27804     /**
27805      * Convenience function for boolean show/hide.
27806      * @param {Boolean} visible true to show/false to hide
27807      */
27808     setVisible: function(visible){
27809         if(visible) {
27810             this.show();
27811         }else{
27812             this.hide();
27813         }
27814     },
27815     
27816     /**
27817      * Try to focus this item.
27818      */
27819     focus : function(){
27820         Roo.fly(this.el).focus();
27821     },
27822     
27823     /**
27824      * Disables this item.
27825      */
27826     disable : function(){
27827         Roo.fly(this.td).addClass("x-item-disabled");
27828         this.disabled = true;
27829         this.el.disabled = true;
27830     },
27831     
27832     /**
27833      * Enables this item.
27834      */
27835     enable : function(){
27836         Roo.fly(this.td).removeClass("x-item-disabled");
27837         this.disabled = false;
27838         this.el.disabled = false;
27839     }
27840 };
27841
27842
27843 /**
27844  * @class Roo.Toolbar.Separator
27845  * @extends Roo.Toolbar.Item
27846  * A simple toolbar separator class
27847  * @constructor
27848  * Creates a new Separator
27849  */
27850 Roo.Toolbar.Separator = function(){
27851     var s = document.createElement("span");
27852     s.className = "ytb-sep";
27853     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27854 };
27855 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27856     enable:Roo.emptyFn,
27857     disable:Roo.emptyFn,
27858     focus:Roo.emptyFn
27859 });
27860
27861 /**
27862  * @class Roo.Toolbar.Spacer
27863  * @extends Roo.Toolbar.Item
27864  * A simple element that adds extra horizontal space to a toolbar.
27865  * @constructor
27866  * Creates a new Spacer
27867  */
27868 Roo.Toolbar.Spacer = function(){
27869     var s = document.createElement("div");
27870     s.className = "ytb-spacer";
27871     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27872 };
27873 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27874     enable:Roo.emptyFn,
27875     disable:Roo.emptyFn,
27876     focus:Roo.emptyFn
27877 });
27878
27879 /**
27880  * @class Roo.Toolbar.Fill
27881  * @extends Roo.Toolbar.Spacer
27882  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27883  * @constructor
27884  * Creates a new Spacer
27885  */
27886 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27887     // private
27888     render : function(td){
27889         td.style.width = '100%';
27890         Roo.Toolbar.Fill.superclass.render.call(this, td);
27891     }
27892 });
27893
27894 /**
27895  * @class Roo.Toolbar.TextItem
27896  * @extends Roo.Toolbar.Item
27897  * A simple class that renders text directly into a toolbar.
27898  * @constructor
27899  * Creates a new TextItem
27900  * @param {String} text
27901  */
27902 Roo.Toolbar.TextItem = function(text){
27903     if (typeof(text) == 'object') {
27904         text = text.text;
27905     }
27906     var s = document.createElement("span");
27907     s.className = "ytb-text";
27908     s.innerHTML = text;
27909     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27910 };
27911 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27912     enable:Roo.emptyFn,
27913     disable:Roo.emptyFn,
27914     focus:Roo.emptyFn
27915 });
27916
27917 /**
27918  * @class Roo.Toolbar.Button
27919  * @extends Roo.Button
27920  * A button that renders into a toolbar.
27921  * @constructor
27922  * Creates a new Button
27923  * @param {Object} config A standard {@link Roo.Button} config object
27924  */
27925 Roo.Toolbar.Button = function(config){
27926     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27927 };
27928 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27929     render : function(td){
27930         this.td = td;
27931         Roo.Toolbar.Button.superclass.render.call(this, td);
27932     },
27933     
27934     /**
27935      * Removes and destroys this button
27936      */
27937     destroy : function(){
27938         Roo.Toolbar.Button.superclass.destroy.call(this);
27939         this.td.parentNode.removeChild(this.td);
27940     },
27941     
27942     /**
27943      * Shows this button
27944      */
27945     show: function(){
27946         this.hidden = false;
27947         this.td.style.display = "";
27948     },
27949     
27950     /**
27951      * Hides this button
27952      */
27953     hide: function(){
27954         this.hidden = true;
27955         this.td.style.display = "none";
27956     },
27957
27958     /**
27959      * Disables this item
27960      */
27961     disable : function(){
27962         Roo.fly(this.td).addClass("x-item-disabled");
27963         this.disabled = true;
27964     },
27965
27966     /**
27967      * Enables this item
27968      */
27969     enable : function(){
27970         Roo.fly(this.td).removeClass("x-item-disabled");
27971         this.disabled = false;
27972     }
27973 });
27974 // backwards compat
27975 Roo.ToolbarButton = Roo.Toolbar.Button;
27976
27977 /**
27978  * @class Roo.Toolbar.SplitButton
27979  * @extends Roo.SplitButton
27980  * A menu button that renders into a toolbar.
27981  * @constructor
27982  * Creates a new SplitButton
27983  * @param {Object} config A standard {@link Roo.SplitButton} config object
27984  */
27985 Roo.Toolbar.SplitButton = function(config){
27986     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27987 };
27988 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27989     render : function(td){
27990         this.td = td;
27991         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27992     },
27993     
27994     /**
27995      * Removes and destroys this button
27996      */
27997     destroy : function(){
27998         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27999         this.td.parentNode.removeChild(this.td);
28000     },
28001     
28002     /**
28003      * Shows this button
28004      */
28005     show: function(){
28006         this.hidden = false;
28007         this.td.style.display = "";
28008     },
28009     
28010     /**
28011      * Hides this button
28012      */
28013     hide: function(){
28014         this.hidden = true;
28015         this.td.style.display = "none";
28016     }
28017 });
28018
28019 // backwards compat
28020 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28021  * Based on:
28022  * Ext JS Library 1.1.1
28023  * Copyright(c) 2006-2007, Ext JS, LLC.
28024  *
28025  * Originally Released Under LGPL - original licence link has changed is not relivant.
28026  *
28027  * Fork - LGPL
28028  * <script type="text/javascript">
28029  */
28030  
28031 /**
28032  * @class Roo.PagingToolbar
28033  * @extends Roo.Toolbar
28034  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28035  * @constructor
28036  * Create a new PagingToolbar
28037  * @param {Object} config The config object
28038  */
28039 Roo.PagingToolbar = function(el, ds, config)
28040 {
28041     // old args format still supported... - xtype is prefered..
28042     if (typeof(el) == 'object' && el.xtype) {
28043         // created from xtype...
28044         config = el;
28045         ds = el.dataSource;
28046         el = config.container;
28047     }
28048     var items = [];
28049     if (config.items) {
28050         items = config.items;
28051         config.items = [];
28052     }
28053     
28054     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28055     this.ds = ds;
28056     this.cursor = 0;
28057     this.renderButtons(this.el);
28058     this.bind(ds);
28059     
28060     // supprot items array.
28061    
28062     Roo.each(items, function(e) {
28063         this.add(Roo.factory(e));
28064     },this);
28065     
28066 };
28067
28068 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28069     /**
28070      * @cfg {Roo.data.Store} dataSource
28071      * The underlying data store providing the paged data
28072      */
28073     /**
28074      * @cfg {String/HTMLElement/Element} container
28075      * container The id or element that will contain the toolbar
28076      */
28077     /**
28078      * @cfg {Boolean} displayInfo
28079      * True to display the displayMsg (defaults to false)
28080      */
28081     /**
28082      * @cfg {Number} pageSize
28083      * The number of records to display per page (defaults to 20)
28084      */
28085     pageSize: 20,
28086     /**
28087      * @cfg {String} displayMsg
28088      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28089      */
28090     displayMsg : 'Displaying {0} - {1} of {2}',
28091     /**
28092      * @cfg {String} emptyMsg
28093      * The message to display when no records are found (defaults to "No data to display")
28094      */
28095     emptyMsg : 'No data to display',
28096     /**
28097      * Customizable piece of the default paging text (defaults to "Page")
28098      * @type String
28099      */
28100     beforePageText : "Page",
28101     /**
28102      * Customizable piece of the default paging text (defaults to "of %0")
28103      * @type String
28104      */
28105     afterPageText : "of {0}",
28106     /**
28107      * Customizable piece of the default paging text (defaults to "First Page")
28108      * @type String
28109      */
28110     firstText : "First Page",
28111     /**
28112      * Customizable piece of the default paging text (defaults to "Previous Page")
28113      * @type String
28114      */
28115     prevText : "Previous Page",
28116     /**
28117      * Customizable piece of the default paging text (defaults to "Next Page")
28118      * @type String
28119      */
28120     nextText : "Next Page",
28121     /**
28122      * Customizable piece of the default paging text (defaults to "Last Page")
28123      * @type String
28124      */
28125     lastText : "Last Page",
28126     /**
28127      * Customizable piece of the default paging text (defaults to "Refresh")
28128      * @type String
28129      */
28130     refreshText : "Refresh",
28131
28132     // private
28133     renderButtons : function(el){
28134         Roo.PagingToolbar.superclass.render.call(this, el);
28135         this.first = this.addButton({
28136             tooltip: this.firstText,
28137             cls: "x-btn-icon x-grid-page-first",
28138             disabled: true,
28139             handler: this.onClick.createDelegate(this, ["first"])
28140         });
28141         this.prev = this.addButton({
28142             tooltip: this.prevText,
28143             cls: "x-btn-icon x-grid-page-prev",
28144             disabled: true,
28145             handler: this.onClick.createDelegate(this, ["prev"])
28146         });
28147         //this.addSeparator();
28148         this.add(this.beforePageText);
28149         this.field = Roo.get(this.addDom({
28150            tag: "input",
28151            type: "text",
28152            size: "3",
28153            value: "1",
28154            cls: "x-grid-page-number"
28155         }).el);
28156         this.field.on("keydown", this.onPagingKeydown, this);
28157         this.field.on("focus", function(){this.dom.select();});
28158         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28159         this.field.setHeight(18);
28160         //this.addSeparator();
28161         this.next = this.addButton({
28162             tooltip: this.nextText,
28163             cls: "x-btn-icon x-grid-page-next",
28164             disabled: true,
28165             handler: this.onClick.createDelegate(this, ["next"])
28166         });
28167         this.last = this.addButton({
28168             tooltip: this.lastText,
28169             cls: "x-btn-icon x-grid-page-last",
28170             disabled: true,
28171             handler: this.onClick.createDelegate(this, ["last"])
28172         });
28173         //this.addSeparator();
28174         this.loading = this.addButton({
28175             tooltip: this.refreshText,
28176             cls: "x-btn-icon x-grid-loading",
28177             handler: this.onClick.createDelegate(this, ["refresh"])
28178         });
28179
28180         if(this.displayInfo){
28181             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28182         }
28183     },
28184
28185     // private
28186     updateInfo : function(){
28187         if(this.displayEl){
28188             var count = this.ds.getCount();
28189             var msg = count == 0 ?
28190                 this.emptyMsg :
28191                 String.format(
28192                     this.displayMsg,
28193                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28194                 );
28195             this.displayEl.update(msg);
28196         }
28197     },
28198
28199     // private
28200     onLoad : function(ds, r, o){
28201        this.cursor = o.params ? o.params.start : 0;
28202        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28203
28204        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28205        this.field.dom.value = ap;
28206        this.first.setDisabled(ap == 1);
28207        this.prev.setDisabled(ap == 1);
28208        this.next.setDisabled(ap == ps);
28209        this.last.setDisabled(ap == ps);
28210        this.loading.enable();
28211        this.updateInfo();
28212     },
28213
28214     // private
28215     getPageData : function(){
28216         var total = this.ds.getTotalCount();
28217         return {
28218             total : total,
28219             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28220             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28221         };
28222     },
28223
28224     // private
28225     onLoadError : function(){
28226         this.loading.enable();
28227     },
28228
28229     // private
28230     onPagingKeydown : function(e){
28231         var k = e.getKey();
28232         var d = this.getPageData();
28233         if(k == e.RETURN){
28234             var v = this.field.dom.value, pageNum;
28235             if(!v || isNaN(pageNum = parseInt(v, 10))){
28236                 this.field.dom.value = d.activePage;
28237                 return;
28238             }
28239             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28240             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28241             e.stopEvent();
28242         }
28243         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))
28244         {
28245           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28246           this.field.dom.value = pageNum;
28247           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28248           e.stopEvent();
28249         }
28250         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28251         {
28252           var v = this.field.dom.value, pageNum; 
28253           var increment = (e.shiftKey) ? 10 : 1;
28254           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28255             increment *= -1;
28256           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28257             this.field.dom.value = d.activePage;
28258             return;
28259           }
28260           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28261           {
28262             this.field.dom.value = parseInt(v, 10) + increment;
28263             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28264             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28265           }
28266           e.stopEvent();
28267         }
28268     },
28269
28270     // private
28271     beforeLoad : function(){
28272         if(this.loading){
28273             this.loading.disable();
28274         }
28275     },
28276
28277     // private
28278     onClick : function(which){
28279         var ds = this.ds;
28280         switch(which){
28281             case "first":
28282                 ds.load({params:{start: 0, limit: this.pageSize}});
28283             break;
28284             case "prev":
28285                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28286             break;
28287             case "next":
28288                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28289             break;
28290             case "last":
28291                 var total = ds.getTotalCount();
28292                 var extra = total % this.pageSize;
28293                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28294                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28295             break;
28296             case "refresh":
28297                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28298             break;
28299         }
28300     },
28301
28302     /**
28303      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28304      * @param {Roo.data.Store} store The data store to unbind
28305      */
28306     unbind : function(ds){
28307         ds.un("beforeload", this.beforeLoad, this);
28308         ds.un("load", this.onLoad, this);
28309         ds.un("loadexception", this.onLoadError, this);
28310         ds.un("remove", this.updateInfo, this);
28311         ds.un("add", this.updateInfo, this);
28312         this.ds = undefined;
28313     },
28314
28315     /**
28316      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28317      * @param {Roo.data.Store} store The data store to bind
28318      */
28319     bind : function(ds){
28320         ds.on("beforeload", this.beforeLoad, this);
28321         ds.on("load", this.onLoad, this);
28322         ds.on("loadexception", this.onLoadError, this);
28323         ds.on("remove", this.updateInfo, this);
28324         ds.on("add", this.updateInfo, this);
28325         this.ds = ds;
28326     }
28327 });/*
28328  * Based on:
28329  * Ext JS Library 1.1.1
28330  * Copyright(c) 2006-2007, Ext JS, LLC.
28331  *
28332  * Originally Released Under LGPL - original licence link has changed is not relivant.
28333  *
28334  * Fork - LGPL
28335  * <script type="text/javascript">
28336  */
28337
28338 /**
28339  * @class Roo.Resizable
28340  * @extends Roo.util.Observable
28341  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28342  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28343  * 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
28344  * the element will be wrapped for you automatically.</p>
28345  * <p>Here is the list of valid resize handles:</p>
28346  * <pre>
28347 Value   Description
28348 ------  -------------------
28349  'n'     north
28350  's'     south
28351  'e'     east
28352  'w'     west
28353  'nw'    northwest
28354  'sw'    southwest
28355  'se'    southeast
28356  'ne'    northeast
28357  'hd'    horizontal drag
28358  'all'   all
28359 </pre>
28360  * <p>Here's an example showing the creation of a typical Resizable:</p>
28361  * <pre><code>
28362 var resizer = new Roo.Resizable("element-id", {
28363     handles: 'all',
28364     minWidth: 200,
28365     minHeight: 100,
28366     maxWidth: 500,
28367     maxHeight: 400,
28368     pinned: true
28369 });
28370 resizer.on("resize", myHandler);
28371 </code></pre>
28372  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28373  * resizer.east.setDisplayed(false);</p>
28374  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28375  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28376  * resize operation's new size (defaults to [0, 0])
28377  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28378  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28379  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28380  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28381  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28382  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28383  * @cfg {Number} width The width of the element in pixels (defaults to null)
28384  * @cfg {Number} height The height of the element in pixels (defaults to null)
28385  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28386  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28387  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28388  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28389  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28390  * in favor of the handles config option (defaults to false)
28391  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28392  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28393  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28394  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28395  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28396  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28397  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28398  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28399  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28400  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28401  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28402  * @constructor
28403  * Create a new resizable component
28404  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28405  * @param {Object} config configuration options
28406   */
28407 Roo.Resizable = function(el, config)
28408 {
28409     this.el = Roo.get(el);
28410
28411     if(config && config.wrap){
28412         config.resizeChild = this.el;
28413         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28414         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28415         this.el.setStyle("overflow", "hidden");
28416         this.el.setPositioning(config.resizeChild.getPositioning());
28417         config.resizeChild.clearPositioning();
28418         if(!config.width || !config.height){
28419             var csize = config.resizeChild.getSize();
28420             this.el.setSize(csize.width, csize.height);
28421         }
28422         if(config.pinned && !config.adjustments){
28423             config.adjustments = "auto";
28424         }
28425     }
28426
28427     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28428     this.proxy.unselectable();
28429     this.proxy.enableDisplayMode('block');
28430
28431     Roo.apply(this, config);
28432
28433     if(this.pinned){
28434         this.disableTrackOver = true;
28435         this.el.addClass("x-resizable-pinned");
28436     }
28437     // if the element isn't positioned, make it relative
28438     var position = this.el.getStyle("position");
28439     if(position != "absolute" && position != "fixed"){
28440         this.el.setStyle("position", "relative");
28441     }
28442     if(!this.handles){ // no handles passed, must be legacy style
28443         this.handles = 's,e,se';
28444         if(this.multiDirectional){
28445             this.handles += ',n,w';
28446         }
28447     }
28448     if(this.handles == "all"){
28449         this.handles = "n s e w ne nw se sw";
28450     }
28451     var hs = this.handles.split(/\s*?[,;]\s*?| /);
28452     var ps = Roo.Resizable.positions;
28453     for(var i = 0, len = hs.length; i < len; i++){
28454         if(hs[i] && ps[hs[i]]){
28455             var pos = ps[hs[i]];
28456             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
28457         }
28458     }
28459     // legacy
28460     this.corner = this.southeast;
28461     
28462     // updateBox = the box can move..
28463     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
28464         this.updateBox = true;
28465     }
28466
28467     this.activeHandle = null;
28468
28469     if(this.resizeChild){
28470         if(typeof this.resizeChild == "boolean"){
28471             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
28472         }else{
28473             this.resizeChild = Roo.get(this.resizeChild, true);
28474         }
28475     }
28476     
28477     if(this.adjustments == "auto"){
28478         var rc = this.resizeChild;
28479         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
28480         if(rc && (hw || hn)){
28481             rc.position("relative");
28482             rc.setLeft(hw ? hw.el.getWidth() : 0);
28483             rc.setTop(hn ? hn.el.getHeight() : 0);
28484         }
28485         this.adjustments = [
28486             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
28487             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
28488         ];
28489     }
28490
28491     if(this.draggable){
28492         this.dd = this.dynamic ?
28493             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
28494         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
28495     }
28496
28497     // public events
28498     this.addEvents({
28499         /**
28500          * @event beforeresize
28501          * Fired before resize is allowed. Set enabled to false to cancel resize.
28502          * @param {Roo.Resizable} this
28503          * @param {Roo.EventObject} e The mousedown event
28504          */
28505         "beforeresize" : true,
28506         /**
28507          * @event resizing
28508          * Fired a resizing.
28509          * @param {Roo.Resizable} this
28510          * @param {Number} x The new x position
28511          * @param {Number} y The new y position
28512          * @param {Number} w The new w width
28513          * @param {Number} h The new h hight
28514          * @param {Roo.EventObject} e The mouseup event
28515          */
28516         "resizing" : true,
28517         /**
28518          * @event resize
28519          * Fired after a resize.
28520          * @param {Roo.Resizable} this
28521          * @param {Number} width The new width
28522          * @param {Number} height The new height
28523          * @param {Roo.EventObject} e The mouseup event
28524          */
28525         "resize" : true
28526     });
28527
28528     if(this.width !== null && this.height !== null){
28529         this.resizeTo(this.width, this.height);
28530     }else{
28531         this.updateChildSize();
28532     }
28533     if(Roo.isIE){
28534         this.el.dom.style.zoom = 1;
28535     }
28536     Roo.Resizable.superclass.constructor.call(this);
28537 };
28538
28539 Roo.extend(Roo.Resizable, Roo.util.Observable, {
28540         resizeChild : false,
28541         adjustments : [0, 0],
28542         minWidth : 5,
28543         minHeight : 5,
28544         maxWidth : 10000,
28545         maxHeight : 10000,
28546         enabled : true,
28547         animate : false,
28548         duration : .35,
28549         dynamic : false,
28550         handles : false,
28551         multiDirectional : false,
28552         disableTrackOver : false,
28553         easing : 'easeOutStrong',
28554         widthIncrement : 0,
28555         heightIncrement : 0,
28556         pinned : false,
28557         width : null,
28558         height : null,
28559         preserveRatio : false,
28560         transparent: false,
28561         minX: 0,
28562         minY: 0,
28563         draggable: false,
28564
28565         /**
28566          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28567          */
28568         constrainTo: undefined,
28569         /**
28570          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28571          */
28572         resizeRegion: undefined,
28573
28574
28575     /**
28576      * Perform a manual resize
28577      * @param {Number} width
28578      * @param {Number} height
28579      */
28580     resizeTo : function(width, height){
28581         this.el.setSize(width, height);
28582         this.updateChildSize();
28583         this.fireEvent("resize", this, width, height, null);
28584     },
28585
28586     // private
28587     startSizing : function(e, handle){
28588         this.fireEvent("beforeresize", this, e);
28589         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28590
28591             if(!this.overlay){
28592                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28593                 this.overlay.unselectable();
28594                 this.overlay.enableDisplayMode("block");
28595                 this.overlay.on("mousemove", this.onMouseMove, this);
28596                 this.overlay.on("mouseup", this.onMouseUp, this);
28597             }
28598             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28599
28600             this.resizing = true;
28601             this.startBox = this.el.getBox();
28602             this.startPoint = e.getXY();
28603             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28604                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28605
28606             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28607             this.overlay.show();
28608
28609             if(this.constrainTo) {
28610                 var ct = Roo.get(this.constrainTo);
28611                 this.resizeRegion = ct.getRegion().adjust(
28612                     ct.getFrameWidth('t'),
28613                     ct.getFrameWidth('l'),
28614                     -ct.getFrameWidth('b'),
28615                     -ct.getFrameWidth('r')
28616                 );
28617             }
28618
28619             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28620             this.proxy.show();
28621             this.proxy.setBox(this.startBox);
28622             if(!this.dynamic){
28623                 this.proxy.setStyle('visibility', 'visible');
28624             }
28625         }
28626     },
28627
28628     // private
28629     onMouseDown : function(handle, e){
28630         if(this.enabled){
28631             e.stopEvent();
28632             this.activeHandle = handle;
28633             this.startSizing(e, handle);
28634         }
28635     },
28636
28637     // private
28638     onMouseUp : function(e){
28639         var size = this.resizeElement();
28640         this.resizing = false;
28641         this.handleOut();
28642         this.overlay.hide();
28643         this.proxy.hide();
28644         this.fireEvent("resize", this, size.width, size.height, e);
28645     },
28646
28647     // private
28648     updateChildSize : function(){
28649         
28650         if(this.resizeChild){
28651             var el = this.el;
28652             var child = this.resizeChild;
28653             var adj = this.adjustments;
28654             if(el.dom.offsetWidth){
28655                 var b = el.getSize(true);
28656                 child.setSize(b.width+adj[0], b.height+adj[1]);
28657             }
28658             // Second call here for IE
28659             // The first call enables instant resizing and
28660             // the second call corrects scroll bars if they
28661             // exist
28662             if(Roo.isIE){
28663                 setTimeout(function(){
28664                     if(el.dom.offsetWidth){
28665                         var b = el.getSize(true);
28666                         child.setSize(b.width+adj[0], b.height+adj[1]);
28667                     }
28668                 }, 10);
28669             }
28670         }
28671     },
28672
28673     // private
28674     snap : function(value, inc, min){
28675         if(!inc || !value) return value;
28676         var newValue = value;
28677         var m = value % inc;
28678         if(m > 0){
28679             if(m > (inc/2)){
28680                 newValue = value + (inc-m);
28681             }else{
28682                 newValue = value - m;
28683             }
28684         }
28685         return Math.max(min, newValue);
28686     },
28687
28688     // private
28689     resizeElement : function(){
28690         var box = this.proxy.getBox();
28691         if(this.updateBox){
28692             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28693         }else{
28694             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28695         }
28696         this.updateChildSize();
28697         if(!this.dynamic){
28698             this.proxy.hide();
28699         }
28700         return box;
28701     },
28702
28703     // private
28704     constrain : function(v, diff, m, mx){
28705         if(v - diff < m){
28706             diff = v - m;
28707         }else if(v - diff > mx){
28708             diff = mx - v;
28709         }
28710         return diff;
28711     },
28712
28713     // private
28714     onMouseMove : function(e){
28715         
28716         if(this.enabled){
28717             try{// try catch so if something goes wrong the user doesn't get hung
28718
28719             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28720                 return;
28721             }
28722
28723             //var curXY = this.startPoint;
28724             var curSize = this.curSize || this.startBox;
28725             var x = this.startBox.x, y = this.startBox.y;
28726             var ox = x, oy = y;
28727             var w = curSize.width, h = curSize.height;
28728             var ow = w, oh = h;
28729             var mw = this.minWidth, mh = this.minHeight;
28730             var mxw = this.maxWidth, mxh = this.maxHeight;
28731             var wi = this.widthIncrement;
28732             var hi = this.heightIncrement;
28733
28734             var eventXY = e.getXY();
28735             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28736             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28737
28738             var pos = this.activeHandle.position;
28739
28740             switch(pos){
28741                 case "east":
28742                     w += diffX;
28743                     w = Math.min(Math.max(mw, w), mxw);
28744                     break;
28745              
28746                 case "south":
28747                     h += diffY;
28748                     h = Math.min(Math.max(mh, h), mxh);
28749                     break;
28750                 case "southeast":
28751                     w += diffX;
28752                     h += diffY;
28753                     w = Math.min(Math.max(mw, w), mxw);
28754                     h = Math.min(Math.max(mh, h), mxh);
28755                     break;
28756                 case "north":
28757                     diffY = this.constrain(h, diffY, mh, mxh);
28758                     y += diffY;
28759                     h -= diffY;
28760                     break;
28761                 case "hdrag":
28762                     
28763                     if (wi) {
28764                         var adiffX = Math.abs(diffX);
28765                         var sub = (adiffX % wi); // how much 
28766                         if (sub > (wi/2)) { // far enough to snap
28767                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28768                         } else {
28769                             // remove difference.. 
28770                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28771                         }
28772                     }
28773                     x += diffX;
28774                     x = Math.max(this.minX, x);
28775                     break;
28776                 case "west":
28777                     diffX = this.constrain(w, diffX, mw, mxw);
28778                     x += diffX;
28779                     w -= diffX;
28780                     break;
28781                 case "northeast":
28782                     w += diffX;
28783                     w = Math.min(Math.max(mw, w), mxw);
28784                     diffY = this.constrain(h, diffY, mh, mxh);
28785                     y += diffY;
28786                     h -= diffY;
28787                     break;
28788                 case "northwest":
28789                     diffX = this.constrain(w, diffX, mw, mxw);
28790                     diffY = this.constrain(h, diffY, mh, mxh);
28791                     y += diffY;
28792                     h -= diffY;
28793                     x += diffX;
28794                     w -= diffX;
28795                     break;
28796                case "southwest":
28797                     diffX = this.constrain(w, diffX, mw, mxw);
28798                     h += diffY;
28799                     h = Math.min(Math.max(mh, h), mxh);
28800                     x += diffX;
28801                     w -= diffX;
28802                     break;
28803             }
28804
28805             var sw = this.snap(w, wi, mw);
28806             var sh = this.snap(h, hi, mh);
28807             if(sw != w || sh != h){
28808                 switch(pos){
28809                     case "northeast":
28810                         y -= sh - h;
28811                     break;
28812                     case "north":
28813                         y -= sh - h;
28814                         break;
28815                     case "southwest":
28816                         x -= sw - w;
28817                     break;
28818                     case "west":
28819                         x -= sw - w;
28820                         break;
28821                     case "northwest":
28822                         x -= sw - w;
28823                         y -= sh - h;
28824                     break;
28825                 }
28826                 w = sw;
28827                 h = sh;
28828             }
28829
28830             if(this.preserveRatio){
28831                 switch(pos){
28832                     case "southeast":
28833                     case "east":
28834                         h = oh * (w/ow);
28835                         h = Math.min(Math.max(mh, h), mxh);
28836                         w = ow * (h/oh);
28837                        break;
28838                     case "south":
28839                         w = ow * (h/oh);
28840                         w = Math.min(Math.max(mw, w), mxw);
28841                         h = oh * (w/ow);
28842                         break;
28843                     case "northeast":
28844                         w = ow * (h/oh);
28845                         w = Math.min(Math.max(mw, w), mxw);
28846                         h = oh * (w/ow);
28847                     break;
28848                     case "north":
28849                         var tw = w;
28850                         w = ow * (h/oh);
28851                         w = Math.min(Math.max(mw, w), mxw);
28852                         h = oh * (w/ow);
28853                         x += (tw - w) / 2;
28854                         break;
28855                     case "southwest":
28856                         h = oh * (w/ow);
28857                         h = Math.min(Math.max(mh, h), mxh);
28858                         var tw = w;
28859                         w = ow * (h/oh);
28860                         x += tw - w;
28861                         break;
28862                     case "west":
28863                         var th = h;
28864                         h = oh * (w/ow);
28865                         h = Math.min(Math.max(mh, h), mxh);
28866                         y += (th - h) / 2;
28867                         var tw = w;
28868                         w = ow * (h/oh);
28869                         x += tw - w;
28870                        break;
28871                     case "northwest":
28872                         var tw = w;
28873                         var th = h;
28874                         h = oh * (w/ow);
28875                         h = Math.min(Math.max(mh, h), mxh);
28876                         w = ow * (h/oh);
28877                         y += th - h;
28878                         x += tw - w;
28879                        break;
28880
28881                 }
28882             }
28883             if (pos == 'hdrag') {
28884                 w = ow;
28885             }
28886             this.proxy.setBounds(x, y, w, h);
28887             if(this.dynamic){
28888                 this.resizeElement();
28889             }
28890             }catch(e){}
28891         }
28892         this.fireEvent("resizing", this, x, y, w, h, e);
28893     },
28894
28895     // private
28896     handleOver : function(){
28897         if(this.enabled){
28898             this.el.addClass("x-resizable-over");
28899         }
28900     },
28901
28902     // private
28903     handleOut : function(){
28904         if(!this.resizing){
28905             this.el.removeClass("x-resizable-over");
28906         }
28907     },
28908
28909     /**
28910      * Returns the element this component is bound to.
28911      * @return {Roo.Element}
28912      */
28913     getEl : function(){
28914         return this.el;
28915     },
28916
28917     /**
28918      * Returns the resizeChild element (or null).
28919      * @return {Roo.Element}
28920      */
28921     getResizeChild : function(){
28922         return this.resizeChild;
28923     },
28924     groupHandler : function()
28925     {
28926         
28927     },
28928     /**
28929      * Destroys this resizable. If the element was wrapped and
28930      * removeEl is not true then the element remains.
28931      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28932      */
28933     destroy : function(removeEl){
28934         this.proxy.remove();
28935         if(this.overlay){
28936             this.overlay.removeAllListeners();
28937             this.overlay.remove();
28938         }
28939         var ps = Roo.Resizable.positions;
28940         for(var k in ps){
28941             if(typeof ps[k] != "function" && this[ps[k]]){
28942                 var h = this[ps[k]];
28943                 h.el.removeAllListeners();
28944                 h.el.remove();
28945             }
28946         }
28947         if(removeEl){
28948             this.el.update("");
28949             this.el.remove();
28950         }
28951     }
28952 });
28953
28954 // private
28955 // hash to map config positions to true positions
28956 Roo.Resizable.positions = {
28957     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28958     hd: "hdrag"
28959 };
28960
28961 // private
28962 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28963     if(!this.tpl){
28964         // only initialize the template if resizable is used
28965         var tpl = Roo.DomHelper.createTemplate(
28966             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28967         );
28968         tpl.compile();
28969         Roo.Resizable.Handle.prototype.tpl = tpl;
28970     }
28971     this.position = pos;
28972     this.rz = rz;
28973     // show north drag fro topdra
28974     var handlepos = pos == 'hdrag' ? 'north' : pos;
28975     
28976     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28977     if (pos == 'hdrag') {
28978         this.el.setStyle('cursor', 'pointer');
28979     }
28980     this.el.unselectable();
28981     if(transparent){
28982         this.el.setOpacity(0);
28983     }
28984     this.el.on("mousedown", this.onMouseDown, this);
28985     if(!disableTrackOver){
28986         this.el.on("mouseover", this.onMouseOver, this);
28987         this.el.on("mouseout", this.onMouseOut, this);
28988     }
28989 };
28990
28991 // private
28992 Roo.Resizable.Handle.prototype = {
28993     afterResize : function(rz){
28994         // do nothing
28995     },
28996     // private
28997     onMouseDown : function(e){
28998         this.rz.onMouseDown(this, e);
28999     },
29000     // private
29001     onMouseOver : function(e){
29002         this.rz.handleOver(this, e);
29003     },
29004     // private
29005     onMouseOut : function(e){
29006         this.rz.handleOut(this, e);
29007     }
29008 };/*
29009  * Based on:
29010  * Ext JS Library 1.1.1
29011  * Copyright(c) 2006-2007, Ext JS, LLC.
29012  *
29013  * Originally Released Under LGPL - original licence link has changed is not relivant.
29014  *
29015  * Fork - LGPL
29016  * <script type="text/javascript">
29017  */
29018
29019 /**
29020  * @class Roo.Editor
29021  * @extends Roo.Component
29022  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29023  * @constructor
29024  * Create a new Editor
29025  * @param {Roo.form.Field} field The Field object (or descendant)
29026  * @param {Object} config The config object
29027  */
29028 Roo.Editor = function(field, config){
29029     Roo.Editor.superclass.constructor.call(this, config);
29030     this.field = field;
29031     this.addEvents({
29032         /**
29033              * @event beforestartedit
29034              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29035              * false from the handler of this event.
29036              * @param {Editor} this
29037              * @param {Roo.Element} boundEl The underlying element bound to this editor
29038              * @param {Mixed} value The field value being set
29039              */
29040         "beforestartedit" : true,
29041         /**
29042              * @event startedit
29043              * Fires when this editor is displayed
29044              * @param {Roo.Element} boundEl The underlying element bound to this editor
29045              * @param {Mixed} value The starting field value
29046              */
29047         "startedit" : true,
29048         /**
29049              * @event beforecomplete
29050              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29051              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29052              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29053              * event will not fire since no edit actually occurred.
29054              * @param {Editor} this
29055              * @param {Mixed} value The current field value
29056              * @param {Mixed} startValue The original field value
29057              */
29058         "beforecomplete" : true,
29059         /**
29060              * @event complete
29061              * Fires after editing is complete and any changed value has been written to the underlying field.
29062              * @param {Editor} this
29063              * @param {Mixed} value The current field value
29064              * @param {Mixed} startValue The original field value
29065              */
29066         "complete" : true,
29067         /**
29068          * @event specialkey
29069          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29070          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29071          * @param {Roo.form.Field} this
29072          * @param {Roo.EventObject} e The event object
29073          */
29074         "specialkey" : true
29075     });
29076 };
29077
29078 Roo.extend(Roo.Editor, Roo.Component, {
29079     /**
29080      * @cfg {Boolean/String} autosize
29081      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29082      * or "height" to adopt the height only (defaults to false)
29083      */
29084     /**
29085      * @cfg {Boolean} revertInvalid
29086      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29087      * validation fails (defaults to true)
29088      */
29089     /**
29090      * @cfg {Boolean} ignoreNoChange
29091      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29092      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29093      * will never be ignored.
29094      */
29095     /**
29096      * @cfg {Boolean} hideEl
29097      * False to keep the bound element visible while the editor is displayed (defaults to true)
29098      */
29099     /**
29100      * @cfg {Mixed} value
29101      * The data value of the underlying field (defaults to "")
29102      */
29103     value : "",
29104     /**
29105      * @cfg {String} alignment
29106      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29107      */
29108     alignment: "c-c?",
29109     /**
29110      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29111      * for bottom-right shadow (defaults to "frame")
29112      */
29113     shadow : "frame",
29114     /**
29115      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29116      */
29117     constrain : false,
29118     /**
29119      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29120      */
29121     completeOnEnter : false,
29122     /**
29123      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29124      */
29125     cancelOnEsc : false,
29126     /**
29127      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29128      */
29129     updateEl : false,
29130
29131     // private
29132     onRender : function(ct, position){
29133         this.el = new Roo.Layer({
29134             shadow: this.shadow,
29135             cls: "x-editor",
29136             parentEl : ct,
29137             shim : this.shim,
29138             shadowOffset:4,
29139             id: this.id,
29140             constrain: this.constrain
29141         });
29142         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29143         if(this.field.msgTarget != 'title'){
29144             this.field.msgTarget = 'qtip';
29145         }
29146         this.field.render(this.el);
29147         if(Roo.isGecko){
29148             this.field.el.dom.setAttribute('autocomplete', 'off');
29149         }
29150         this.field.on("specialkey", this.onSpecialKey, this);
29151         if(this.swallowKeys){
29152             this.field.el.swallowEvent(['keydown','keypress']);
29153         }
29154         this.field.show();
29155         this.field.on("blur", this.onBlur, this);
29156         if(this.field.grow){
29157             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29158         }
29159     },
29160
29161     onSpecialKey : function(field, e)
29162     {
29163         //Roo.log('editor onSpecialKey');
29164         if(this.completeOnEnter && e.getKey() == e.ENTER){
29165             e.stopEvent();
29166             this.completeEdit();
29167             return;
29168         }
29169         // do not fire special key otherwise it might hide close the editor...
29170         if(e.getKey() == e.ENTER){    
29171             return;
29172         }
29173         if(this.cancelOnEsc && e.getKey() == e.ESC){
29174             this.cancelEdit();
29175             return;
29176         } 
29177         this.fireEvent('specialkey', field, e);
29178     
29179     },
29180
29181     /**
29182      * Starts the editing process and shows the editor.
29183      * @param {String/HTMLElement/Element} el The element to edit
29184      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29185       * to the innerHTML of el.
29186      */
29187     startEdit : function(el, value){
29188         if(this.editing){
29189             this.completeEdit();
29190         }
29191         this.boundEl = Roo.get(el);
29192         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29193         if(!this.rendered){
29194             this.render(this.parentEl || document.body);
29195         }
29196         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29197             return;
29198         }
29199         this.startValue = v;
29200         this.field.setValue(v);
29201         if(this.autoSize){
29202             var sz = this.boundEl.getSize();
29203             switch(this.autoSize){
29204                 case "width":
29205                 this.setSize(sz.width,  "");
29206                 break;
29207                 case "height":
29208                 this.setSize("",  sz.height);
29209                 break;
29210                 default:
29211                 this.setSize(sz.width,  sz.height);
29212             }
29213         }
29214         this.el.alignTo(this.boundEl, this.alignment);
29215         this.editing = true;
29216         if(Roo.QuickTips){
29217             Roo.QuickTips.disable();
29218         }
29219         this.show();
29220     },
29221
29222     /**
29223      * Sets the height and width of this editor.
29224      * @param {Number} width The new width
29225      * @param {Number} height The new height
29226      */
29227     setSize : function(w, h){
29228         this.field.setSize(w, h);
29229         if(this.el){
29230             this.el.sync();
29231         }
29232     },
29233
29234     /**
29235      * Realigns the editor to the bound field based on the current alignment config value.
29236      */
29237     realign : function(){
29238         this.el.alignTo(this.boundEl, this.alignment);
29239     },
29240
29241     /**
29242      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29243      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29244      */
29245     completeEdit : function(remainVisible){
29246         if(!this.editing){
29247             return;
29248         }
29249         var v = this.getValue();
29250         if(this.revertInvalid !== false && !this.field.isValid()){
29251             v = this.startValue;
29252             this.cancelEdit(true);
29253         }
29254         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29255             this.editing = false;
29256             this.hide();
29257             return;
29258         }
29259         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29260             this.editing = false;
29261             if(this.updateEl && this.boundEl){
29262                 this.boundEl.update(v);
29263             }
29264             if(remainVisible !== true){
29265                 this.hide();
29266             }
29267             this.fireEvent("complete", this, v, this.startValue);
29268         }
29269     },
29270
29271     // private
29272     onShow : function(){
29273         this.el.show();
29274         if(this.hideEl !== false){
29275             this.boundEl.hide();
29276         }
29277         this.field.show();
29278         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29279             this.fixIEFocus = true;
29280             this.deferredFocus.defer(50, this);
29281         }else{
29282             this.field.focus();
29283         }
29284         this.fireEvent("startedit", this.boundEl, this.startValue);
29285     },
29286
29287     deferredFocus : function(){
29288         if(this.editing){
29289             this.field.focus();
29290         }
29291     },
29292
29293     /**
29294      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29295      * reverted to the original starting value.
29296      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29297      * cancel (defaults to false)
29298      */
29299     cancelEdit : function(remainVisible){
29300         if(this.editing){
29301             this.setValue(this.startValue);
29302             if(remainVisible !== true){
29303                 this.hide();
29304             }
29305         }
29306     },
29307
29308     // private
29309     onBlur : function(){
29310         if(this.allowBlur !== true && this.editing){
29311             this.completeEdit();
29312         }
29313     },
29314
29315     // private
29316     onHide : function(){
29317         if(this.editing){
29318             this.completeEdit();
29319             return;
29320         }
29321         this.field.blur();
29322         if(this.field.collapse){
29323             this.field.collapse();
29324         }
29325         this.el.hide();
29326         if(this.hideEl !== false){
29327             this.boundEl.show();
29328         }
29329         if(Roo.QuickTips){
29330             Roo.QuickTips.enable();
29331         }
29332     },
29333
29334     /**
29335      * Sets the data value of the editor
29336      * @param {Mixed} value Any valid value supported by the underlying field
29337      */
29338     setValue : function(v){
29339         this.field.setValue(v);
29340     },
29341
29342     /**
29343      * Gets the data value of the editor
29344      * @return {Mixed} The data value
29345      */
29346     getValue : function(){
29347         return this.field.getValue();
29348     }
29349 });/*
29350  * Based on:
29351  * Ext JS Library 1.1.1
29352  * Copyright(c) 2006-2007, Ext JS, LLC.
29353  *
29354  * Originally Released Under LGPL - original licence link has changed is not relivant.
29355  *
29356  * Fork - LGPL
29357  * <script type="text/javascript">
29358  */
29359  
29360 /**
29361  * @class Roo.BasicDialog
29362  * @extends Roo.util.Observable
29363  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29364  * <pre><code>
29365 var dlg = new Roo.BasicDialog("my-dlg", {
29366     height: 200,
29367     width: 300,
29368     minHeight: 100,
29369     minWidth: 150,
29370     modal: true,
29371     proxyDrag: true,
29372     shadow: true
29373 });
29374 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29375 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29376 dlg.addButton('Cancel', dlg.hide, dlg);
29377 dlg.show();
29378 </code></pre>
29379   <b>A Dialog should always be a direct child of the body element.</b>
29380  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29381  * @cfg {String} title Default text to display in the title bar (defaults to null)
29382  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29383  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29384  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29385  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29386  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29387  * (defaults to null with no animation)
29388  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29389  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29390  * property for valid values (defaults to 'all')
29391  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29392  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29393  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29394  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29395  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29396  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29397  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29398  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29399  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29400  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29401  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29402  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29403  * draggable = true (defaults to false)
29404  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29405  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29406  * shadow (defaults to false)
29407  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29408  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29409  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29410  * @cfg {Array} buttons Array of buttons
29411  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29412  * @constructor
29413  * Create a new BasicDialog.
29414  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29415  * @param {Object} config Configuration options
29416  */
29417 Roo.BasicDialog = function(el, config){
29418     this.el = Roo.get(el);
29419     var dh = Roo.DomHelper;
29420     if(!this.el && config && config.autoCreate){
29421         if(typeof config.autoCreate == "object"){
29422             if(!config.autoCreate.id){
29423                 config.autoCreate.id = el;
29424             }
29425             this.el = dh.append(document.body,
29426                         config.autoCreate, true);
29427         }else{
29428             this.el = dh.append(document.body,
29429                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29430         }
29431     }
29432     el = this.el;
29433     el.setDisplayed(true);
29434     el.hide = this.hideAction;
29435     this.id = el.id;
29436     el.addClass("x-dlg");
29437
29438     Roo.apply(this, config);
29439
29440     this.proxy = el.createProxy("x-dlg-proxy");
29441     this.proxy.hide = this.hideAction;
29442     this.proxy.setOpacity(.5);
29443     this.proxy.hide();
29444
29445     if(config.width){
29446         el.setWidth(config.width);
29447     }
29448     if(config.height){
29449         el.setHeight(config.height);
29450     }
29451     this.size = el.getSize();
29452     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
29453         this.xy = [config.x,config.y];
29454     }else{
29455         this.xy = el.getCenterXY(true);
29456     }
29457     /** The header element @type Roo.Element */
29458     this.header = el.child("> .x-dlg-hd");
29459     /** The body element @type Roo.Element */
29460     this.body = el.child("> .x-dlg-bd");
29461     /** The footer element @type Roo.Element */
29462     this.footer = el.child("> .x-dlg-ft");
29463
29464     if(!this.header){
29465         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
29466     }
29467     if(!this.body){
29468         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
29469     }
29470
29471     this.header.unselectable();
29472     if(this.title){
29473         this.header.update(this.title);
29474     }
29475     // this element allows the dialog to be focused for keyboard event
29476     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
29477     this.focusEl.swallowEvent("click", true);
29478
29479     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
29480
29481     // wrap the body and footer for special rendering
29482     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
29483     if(this.footer){
29484         this.bwrap.dom.appendChild(this.footer.dom);
29485     }
29486
29487     this.bg = this.el.createChild({
29488         tag: "div", cls:"x-dlg-bg",
29489         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
29490     });
29491     this.centerBg = this.bg.child("div.x-dlg-bg-center");
29492
29493
29494     if(this.autoScroll !== false && !this.autoTabs){
29495         this.body.setStyle("overflow", "auto");
29496     }
29497
29498     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
29499
29500     if(this.closable !== false){
29501         this.el.addClass("x-dlg-closable");
29502         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
29503         this.close.on("click", this.closeClick, this);
29504         this.close.addClassOnOver("x-dlg-close-over");
29505     }
29506     if(this.collapsible !== false){
29507         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
29508         this.collapseBtn.on("click", this.collapseClick, this);
29509         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
29510         this.header.on("dblclick", this.collapseClick, this);
29511     }
29512     if(this.resizable !== false){
29513         this.el.addClass("x-dlg-resizable");
29514         this.resizer = new Roo.Resizable(el, {
29515             minWidth: this.minWidth || 80,
29516             minHeight:this.minHeight || 80,
29517             handles: this.resizeHandles || "all",
29518             pinned: true
29519         });
29520         this.resizer.on("beforeresize", this.beforeResize, this);
29521         this.resizer.on("resize", this.onResize, this);
29522     }
29523     if(this.draggable !== false){
29524         el.addClass("x-dlg-draggable");
29525         if (!this.proxyDrag) {
29526             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
29527         }
29528         else {
29529             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
29530         }
29531         dd.setHandleElId(this.header.id);
29532         dd.endDrag = this.endMove.createDelegate(this);
29533         dd.startDrag = this.startMove.createDelegate(this);
29534         dd.onDrag = this.onDrag.createDelegate(this);
29535         dd.scroll = false;
29536         this.dd = dd;
29537     }
29538     if(this.modal){
29539         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
29540         this.mask.enableDisplayMode("block");
29541         this.mask.hide();
29542         this.el.addClass("x-dlg-modal");
29543     }
29544     if(this.shadow){
29545         this.shadow = new Roo.Shadow({
29546             mode : typeof this.shadow == "string" ? this.shadow : "sides",
29547             offset : this.shadowOffset
29548         });
29549     }else{
29550         this.shadowOffset = 0;
29551     }
29552     if(Roo.useShims && this.shim !== false){
29553         this.shim = this.el.createShim();
29554         this.shim.hide = this.hideAction;
29555         this.shim.hide();
29556     }else{
29557         this.shim = false;
29558     }
29559     if(this.autoTabs){
29560         this.initTabs();
29561     }
29562     if (this.buttons) { 
29563         var bts= this.buttons;
29564         this.buttons = [];
29565         Roo.each(bts, function(b) {
29566             this.addButton(b);
29567         }, this);
29568     }
29569     
29570     
29571     this.addEvents({
29572         /**
29573          * @event keydown
29574          * Fires when a key is pressed
29575          * @param {Roo.BasicDialog} this
29576          * @param {Roo.EventObject} e
29577          */
29578         "keydown" : true,
29579         /**
29580          * @event move
29581          * Fires when this dialog is moved by the user.
29582          * @param {Roo.BasicDialog} this
29583          * @param {Number} x The new page X
29584          * @param {Number} y The new page Y
29585          */
29586         "move" : true,
29587         /**
29588          * @event resize
29589          * Fires when this dialog is resized by the user.
29590          * @param {Roo.BasicDialog} this
29591          * @param {Number} width The new width
29592          * @param {Number} height The new height
29593          */
29594         "resize" : true,
29595         /**
29596          * @event beforehide
29597          * Fires before this dialog is hidden.
29598          * @param {Roo.BasicDialog} this
29599          */
29600         "beforehide" : true,
29601         /**
29602          * @event hide
29603          * Fires when this dialog is hidden.
29604          * @param {Roo.BasicDialog} this
29605          */
29606         "hide" : true,
29607         /**
29608          * @event beforeshow
29609          * Fires before this dialog is shown.
29610          * @param {Roo.BasicDialog} this
29611          */
29612         "beforeshow" : true,
29613         /**
29614          * @event show
29615          * Fires when this dialog is shown.
29616          * @param {Roo.BasicDialog} this
29617          */
29618         "show" : true
29619     });
29620     el.on("keydown", this.onKeyDown, this);
29621     el.on("mousedown", this.toFront, this);
29622     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29623     this.el.hide();
29624     Roo.DialogManager.register(this);
29625     Roo.BasicDialog.superclass.constructor.call(this);
29626 };
29627
29628 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29629     shadowOffset: Roo.isIE ? 6 : 5,
29630     minHeight: 80,
29631     minWidth: 200,
29632     minButtonWidth: 75,
29633     defaultButton: null,
29634     buttonAlign: "right",
29635     tabTag: 'div',
29636     firstShow: true,
29637
29638     /**
29639      * Sets the dialog title text
29640      * @param {String} text The title text to display
29641      * @return {Roo.BasicDialog} this
29642      */
29643     setTitle : function(text){
29644         this.header.update(text);
29645         return this;
29646     },
29647
29648     // private
29649     closeClick : function(){
29650         this.hide();
29651     },
29652
29653     // private
29654     collapseClick : function(){
29655         this[this.collapsed ? "expand" : "collapse"]();
29656     },
29657
29658     /**
29659      * Collapses the dialog to its minimized state (only the title bar is visible).
29660      * Equivalent to the user clicking the collapse dialog button.
29661      */
29662     collapse : function(){
29663         if(!this.collapsed){
29664             this.collapsed = true;
29665             this.el.addClass("x-dlg-collapsed");
29666             this.restoreHeight = this.el.getHeight();
29667             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29668         }
29669     },
29670
29671     /**
29672      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29673      * clicking the expand dialog button.
29674      */
29675     expand : function(){
29676         if(this.collapsed){
29677             this.collapsed = false;
29678             this.el.removeClass("x-dlg-collapsed");
29679             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29680         }
29681     },
29682
29683     /**
29684      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29685      * @return {Roo.TabPanel} The tabs component
29686      */
29687     initTabs : function(){
29688         var tabs = this.getTabs();
29689         while(tabs.getTab(0)){
29690             tabs.removeTab(0);
29691         }
29692         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29693             var dom = el.dom;
29694             tabs.addTab(Roo.id(dom), dom.title);
29695             dom.title = "";
29696         });
29697         tabs.activate(0);
29698         return tabs;
29699     },
29700
29701     // private
29702     beforeResize : function(){
29703         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29704     },
29705
29706     // private
29707     onResize : function(){
29708         this.refreshSize();
29709         this.syncBodyHeight();
29710         this.adjustAssets();
29711         this.focus();
29712         this.fireEvent("resize", this, this.size.width, this.size.height);
29713     },
29714
29715     // private
29716     onKeyDown : function(e){
29717         if(this.isVisible()){
29718             this.fireEvent("keydown", this, e);
29719         }
29720     },
29721
29722     /**
29723      * Resizes the dialog.
29724      * @param {Number} width
29725      * @param {Number} height
29726      * @return {Roo.BasicDialog} this
29727      */
29728     resizeTo : function(width, height){
29729         this.el.setSize(width, height);
29730         this.size = {width: width, height: height};
29731         this.syncBodyHeight();
29732         if(this.fixedcenter){
29733             this.center();
29734         }
29735         if(this.isVisible()){
29736             this.constrainXY();
29737             this.adjustAssets();
29738         }
29739         this.fireEvent("resize", this, width, height);
29740         return this;
29741     },
29742
29743
29744     /**
29745      * Resizes the dialog to fit the specified content size.
29746      * @param {Number} width
29747      * @param {Number} height
29748      * @return {Roo.BasicDialog} this
29749      */
29750     setContentSize : function(w, h){
29751         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29752         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29753         //if(!this.el.isBorderBox()){
29754             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29755             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29756         //}
29757         if(this.tabs){
29758             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29759             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29760         }
29761         this.resizeTo(w, h);
29762         return this;
29763     },
29764
29765     /**
29766      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29767      * executed in response to a particular key being pressed while the dialog is active.
29768      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29769      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29770      * @param {Function} fn The function to call
29771      * @param {Object} scope (optional) The scope of the function
29772      * @return {Roo.BasicDialog} this
29773      */
29774     addKeyListener : function(key, fn, scope){
29775         var keyCode, shift, ctrl, alt;
29776         if(typeof key == "object" && !(key instanceof Array)){
29777             keyCode = key["key"];
29778             shift = key["shift"];
29779             ctrl = key["ctrl"];
29780             alt = key["alt"];
29781         }else{
29782             keyCode = key;
29783         }
29784         var handler = function(dlg, e){
29785             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29786                 var k = e.getKey();
29787                 if(keyCode instanceof Array){
29788                     for(var i = 0, len = keyCode.length; i < len; i++){
29789                         if(keyCode[i] == k){
29790                           fn.call(scope || window, dlg, k, e);
29791                           return;
29792                         }
29793                     }
29794                 }else{
29795                     if(k == keyCode){
29796                         fn.call(scope || window, dlg, k, e);
29797                     }
29798                 }
29799             }
29800         };
29801         this.on("keydown", handler);
29802         return this;
29803     },
29804
29805     /**
29806      * Returns the TabPanel component (creates it if it doesn't exist).
29807      * Note: If you wish to simply check for the existence of tabs without creating them,
29808      * check for a null 'tabs' property.
29809      * @return {Roo.TabPanel} The tabs component
29810      */
29811     getTabs : function(){
29812         if(!this.tabs){
29813             this.el.addClass("x-dlg-auto-tabs");
29814             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29815             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29816         }
29817         return this.tabs;
29818     },
29819
29820     /**
29821      * Adds a button to the footer section of the dialog.
29822      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29823      * object or a valid Roo.DomHelper element config
29824      * @param {Function} handler The function called when the button is clicked
29825      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29826      * @return {Roo.Button} The new button
29827      */
29828     addButton : function(config, handler, scope){
29829         var dh = Roo.DomHelper;
29830         if(!this.footer){
29831             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29832         }
29833         if(!this.btnContainer){
29834             var tb = this.footer.createChild({
29835
29836                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29837                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29838             }, null, true);
29839             this.btnContainer = tb.firstChild.firstChild.firstChild;
29840         }
29841         var bconfig = {
29842             handler: handler,
29843             scope: scope,
29844             minWidth: this.minButtonWidth,
29845             hideParent:true
29846         };
29847         if(typeof config == "string"){
29848             bconfig.text = config;
29849         }else{
29850             if(config.tag){
29851                 bconfig.dhconfig = config;
29852             }else{
29853                 Roo.apply(bconfig, config);
29854             }
29855         }
29856         var fc = false;
29857         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29858             bconfig.position = Math.max(0, bconfig.position);
29859             fc = this.btnContainer.childNodes[bconfig.position];
29860         }
29861          
29862         var btn = new Roo.Button(
29863             fc ? 
29864                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29865                 : this.btnContainer.appendChild(document.createElement("td")),
29866             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29867             bconfig
29868         );
29869         this.syncBodyHeight();
29870         if(!this.buttons){
29871             /**
29872              * Array of all the buttons that have been added to this dialog via addButton
29873              * @type Array
29874              */
29875             this.buttons = [];
29876         }
29877         this.buttons.push(btn);
29878         return btn;
29879     },
29880
29881     /**
29882      * Sets the default button to be focused when the dialog is displayed.
29883      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29884      * @return {Roo.BasicDialog} this
29885      */
29886     setDefaultButton : function(btn){
29887         this.defaultButton = btn;
29888         return this;
29889     },
29890
29891     // private
29892     getHeaderFooterHeight : function(safe){
29893         var height = 0;
29894         if(this.header){
29895            height += this.header.getHeight();
29896         }
29897         if(this.footer){
29898            var fm = this.footer.getMargins();
29899             height += (this.footer.getHeight()+fm.top+fm.bottom);
29900         }
29901         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29902         height += this.centerBg.getPadding("tb");
29903         return height;
29904     },
29905
29906     // private
29907     syncBodyHeight : function()
29908     {
29909         var bd = this.body, // the text
29910             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
29911             bw = this.bwrap;
29912         var height = this.size.height - this.getHeaderFooterHeight(false);
29913         bd.setHeight(height-bd.getMargins("tb"));
29914         var hh = this.header.getHeight();
29915         var h = this.size.height-hh;
29916         cb.setHeight(h);
29917         
29918         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29919         bw.setHeight(h-cb.getPadding("tb"));
29920         
29921         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29922         bd.setWidth(bw.getWidth(true));
29923         if(this.tabs){
29924             this.tabs.syncHeight();
29925             if(Roo.isIE){
29926                 this.tabs.el.repaint();
29927             }
29928         }
29929     },
29930
29931     /**
29932      * Restores the previous state of the dialog if Roo.state is configured.
29933      * @return {Roo.BasicDialog} this
29934      */
29935     restoreState : function(){
29936         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29937         if(box && box.width){
29938             this.xy = [box.x, box.y];
29939             this.resizeTo(box.width, box.height);
29940         }
29941         return this;
29942     },
29943
29944     // private
29945     beforeShow : function(){
29946         this.expand();
29947         if(this.fixedcenter){
29948             this.xy = this.el.getCenterXY(true);
29949         }
29950         if(this.modal){
29951             Roo.get(document.body).addClass("x-body-masked");
29952             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29953             this.mask.show();
29954         }
29955         this.constrainXY();
29956     },
29957
29958     // private
29959     animShow : function(){
29960         var b = Roo.get(this.animateTarget).getBox();
29961         this.proxy.setSize(b.width, b.height);
29962         this.proxy.setLocation(b.x, b.y);
29963         this.proxy.show();
29964         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29965                     true, .35, this.showEl.createDelegate(this));
29966     },
29967
29968     /**
29969      * Shows the dialog.
29970      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29971      * @return {Roo.BasicDialog} this
29972      */
29973     show : function(animateTarget){
29974         if (this.fireEvent("beforeshow", this) === false){
29975             return;
29976         }
29977         if(this.syncHeightBeforeShow){
29978             this.syncBodyHeight();
29979         }else if(this.firstShow){
29980             this.firstShow = false;
29981             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29982         }
29983         this.animateTarget = animateTarget || this.animateTarget;
29984         if(!this.el.isVisible()){
29985             this.beforeShow();
29986             if(this.animateTarget && Roo.get(this.animateTarget)){
29987                 this.animShow();
29988             }else{
29989                 this.showEl();
29990             }
29991         }
29992         return this;
29993     },
29994
29995     // private
29996     showEl : function(){
29997         this.proxy.hide();
29998         this.el.setXY(this.xy);
29999         this.el.show();
30000         this.adjustAssets(true);
30001         this.toFront();
30002         this.focus();
30003         // IE peekaboo bug - fix found by Dave Fenwick
30004         if(Roo.isIE){
30005             this.el.repaint();
30006         }
30007         this.fireEvent("show", this);
30008     },
30009
30010     /**
30011      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30012      * dialog itself will receive focus.
30013      */
30014     focus : function(){
30015         if(this.defaultButton){
30016             this.defaultButton.focus();
30017         }else{
30018             this.focusEl.focus();
30019         }
30020     },
30021
30022     // private
30023     constrainXY : function(){
30024         if(this.constraintoviewport !== false){
30025             if(!this.viewSize){
30026                 if(this.container){
30027                     var s = this.container.getSize();
30028                     this.viewSize = [s.width, s.height];
30029                 }else{
30030                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30031                 }
30032             }
30033             var s = Roo.get(this.container||document).getScroll();
30034
30035             var x = this.xy[0], y = this.xy[1];
30036             var w = this.size.width, h = this.size.height;
30037             var vw = this.viewSize[0], vh = this.viewSize[1];
30038             // only move it if it needs it
30039             var moved = false;
30040             // first validate right/bottom
30041             if(x + w > vw+s.left){
30042                 x = vw - w;
30043                 moved = true;
30044             }
30045             if(y + h > vh+s.top){
30046                 y = vh - h;
30047                 moved = true;
30048             }
30049             // then make sure top/left isn't negative
30050             if(x < s.left){
30051                 x = s.left;
30052                 moved = true;
30053             }
30054             if(y < s.top){
30055                 y = s.top;
30056                 moved = true;
30057             }
30058             if(moved){
30059                 // cache xy
30060                 this.xy = [x, y];
30061                 if(this.isVisible()){
30062                     this.el.setLocation(x, y);
30063                     this.adjustAssets();
30064                 }
30065             }
30066         }
30067     },
30068
30069     // private
30070     onDrag : function(){
30071         if(!this.proxyDrag){
30072             this.xy = this.el.getXY();
30073             this.adjustAssets();
30074         }
30075     },
30076
30077     // private
30078     adjustAssets : function(doShow){
30079         var x = this.xy[0], y = this.xy[1];
30080         var w = this.size.width, h = this.size.height;
30081         if(doShow === true){
30082             if(this.shadow){
30083                 this.shadow.show(this.el);
30084             }
30085             if(this.shim){
30086                 this.shim.show();
30087             }
30088         }
30089         if(this.shadow && this.shadow.isVisible()){
30090             this.shadow.show(this.el);
30091         }
30092         if(this.shim && this.shim.isVisible()){
30093             this.shim.setBounds(x, y, w, h);
30094         }
30095     },
30096
30097     // private
30098     adjustViewport : function(w, h){
30099         if(!w || !h){
30100             w = Roo.lib.Dom.getViewWidth();
30101             h = Roo.lib.Dom.getViewHeight();
30102         }
30103         // cache the size
30104         this.viewSize = [w, h];
30105         if(this.modal && this.mask.isVisible()){
30106             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30107             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30108         }
30109         if(this.isVisible()){
30110             this.constrainXY();
30111         }
30112     },
30113
30114     /**
30115      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30116      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30117      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30118      */
30119     destroy : function(removeEl){
30120         if(this.isVisible()){
30121             this.animateTarget = null;
30122             this.hide();
30123         }
30124         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30125         if(this.tabs){
30126             this.tabs.destroy(removeEl);
30127         }
30128         Roo.destroy(
30129              this.shim,
30130              this.proxy,
30131              this.resizer,
30132              this.close,
30133              this.mask
30134         );
30135         if(this.dd){
30136             this.dd.unreg();
30137         }
30138         if(this.buttons){
30139            for(var i = 0, len = this.buttons.length; i < len; i++){
30140                this.buttons[i].destroy();
30141            }
30142         }
30143         this.el.removeAllListeners();
30144         if(removeEl === true){
30145             this.el.update("");
30146             this.el.remove();
30147         }
30148         Roo.DialogManager.unregister(this);
30149     },
30150
30151     // private
30152     startMove : function(){
30153         if(this.proxyDrag){
30154             this.proxy.show();
30155         }
30156         if(this.constraintoviewport !== false){
30157             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30158         }
30159     },
30160
30161     // private
30162     endMove : function(){
30163         if(!this.proxyDrag){
30164             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30165         }else{
30166             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30167             this.proxy.hide();
30168         }
30169         this.refreshSize();
30170         this.adjustAssets();
30171         this.focus();
30172         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30173     },
30174
30175     /**
30176      * Brings this dialog to the front of any other visible dialogs
30177      * @return {Roo.BasicDialog} this
30178      */
30179     toFront : function(){
30180         Roo.DialogManager.bringToFront(this);
30181         return this;
30182     },
30183
30184     /**
30185      * Sends this dialog to the back (under) of any other visible dialogs
30186      * @return {Roo.BasicDialog} this
30187      */
30188     toBack : function(){
30189         Roo.DialogManager.sendToBack(this);
30190         return this;
30191     },
30192
30193     /**
30194      * Centers this dialog in the viewport
30195      * @return {Roo.BasicDialog} this
30196      */
30197     center : function(){
30198         var xy = this.el.getCenterXY(true);
30199         this.moveTo(xy[0], xy[1]);
30200         return this;
30201     },
30202
30203     /**
30204      * Moves the dialog's top-left corner to the specified point
30205      * @param {Number} x
30206      * @param {Number} y
30207      * @return {Roo.BasicDialog} this
30208      */
30209     moveTo : function(x, y){
30210         this.xy = [x,y];
30211         if(this.isVisible()){
30212             this.el.setXY(this.xy);
30213             this.adjustAssets();
30214         }
30215         return this;
30216     },
30217
30218     /**
30219      * Aligns the dialog to the specified element
30220      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30221      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30222      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30223      * @return {Roo.BasicDialog} this
30224      */
30225     alignTo : function(element, position, offsets){
30226         this.xy = this.el.getAlignToXY(element, position, offsets);
30227         if(this.isVisible()){
30228             this.el.setXY(this.xy);
30229             this.adjustAssets();
30230         }
30231         return this;
30232     },
30233
30234     /**
30235      * Anchors an element to another element and realigns it when the window is resized.
30236      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30237      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30238      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30239      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30240      * is a number, it is used as the buffer delay (defaults to 50ms).
30241      * @return {Roo.BasicDialog} this
30242      */
30243     anchorTo : function(el, alignment, offsets, monitorScroll){
30244         var action = function(){
30245             this.alignTo(el, alignment, offsets);
30246         };
30247         Roo.EventManager.onWindowResize(action, this);
30248         var tm = typeof monitorScroll;
30249         if(tm != 'undefined'){
30250             Roo.EventManager.on(window, 'scroll', action, this,
30251                 {buffer: tm == 'number' ? monitorScroll : 50});
30252         }
30253         action.call(this);
30254         return this;
30255     },
30256
30257     /**
30258      * Returns true if the dialog is visible
30259      * @return {Boolean}
30260      */
30261     isVisible : function(){
30262         return this.el.isVisible();
30263     },
30264
30265     // private
30266     animHide : function(callback){
30267         var b = Roo.get(this.animateTarget).getBox();
30268         this.proxy.show();
30269         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30270         this.el.hide();
30271         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30272                     this.hideEl.createDelegate(this, [callback]));
30273     },
30274
30275     /**
30276      * Hides the dialog.
30277      * @param {Function} callback (optional) Function to call when the dialog is hidden
30278      * @return {Roo.BasicDialog} this
30279      */
30280     hide : function(callback){
30281         if (this.fireEvent("beforehide", this) === false){
30282             return;
30283         }
30284         if(this.shadow){
30285             this.shadow.hide();
30286         }
30287         if(this.shim) {
30288           this.shim.hide();
30289         }
30290         // sometimes animateTarget seems to get set.. causing problems...
30291         // this just double checks..
30292         if(this.animateTarget && Roo.get(this.animateTarget)) {
30293            this.animHide(callback);
30294         }else{
30295             this.el.hide();
30296             this.hideEl(callback);
30297         }
30298         return this;
30299     },
30300
30301     // private
30302     hideEl : function(callback){
30303         this.proxy.hide();
30304         if(this.modal){
30305             this.mask.hide();
30306             Roo.get(document.body).removeClass("x-body-masked");
30307         }
30308         this.fireEvent("hide", this);
30309         if(typeof callback == "function"){
30310             callback();
30311         }
30312     },
30313
30314     // private
30315     hideAction : function(){
30316         this.setLeft("-10000px");
30317         this.setTop("-10000px");
30318         this.setStyle("visibility", "hidden");
30319     },
30320
30321     // private
30322     refreshSize : function(){
30323         this.size = this.el.getSize();
30324         this.xy = this.el.getXY();
30325         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30326     },
30327
30328     // private
30329     // z-index is managed by the DialogManager and may be overwritten at any time
30330     setZIndex : function(index){
30331         if(this.modal){
30332             this.mask.setStyle("z-index", index);
30333         }
30334         if(this.shim){
30335             this.shim.setStyle("z-index", ++index);
30336         }
30337         if(this.shadow){
30338             this.shadow.setZIndex(++index);
30339         }
30340         this.el.setStyle("z-index", ++index);
30341         if(this.proxy){
30342             this.proxy.setStyle("z-index", ++index);
30343         }
30344         if(this.resizer){
30345             this.resizer.proxy.setStyle("z-index", ++index);
30346         }
30347
30348         this.lastZIndex = index;
30349     },
30350
30351     /**
30352      * Returns the element for this dialog
30353      * @return {Roo.Element} The underlying dialog Element
30354      */
30355     getEl : function(){
30356         return this.el;
30357     }
30358 });
30359
30360 /**
30361  * @class Roo.DialogManager
30362  * Provides global access to BasicDialogs that have been created and
30363  * support for z-indexing (layering) multiple open dialogs.
30364  */
30365 Roo.DialogManager = function(){
30366     var list = {};
30367     var accessList = [];
30368     var front = null;
30369
30370     // private
30371     var sortDialogs = function(d1, d2){
30372         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30373     };
30374
30375     // private
30376     var orderDialogs = function(){
30377         accessList.sort(sortDialogs);
30378         var seed = Roo.DialogManager.zseed;
30379         for(var i = 0, len = accessList.length; i < len; i++){
30380             var dlg = accessList[i];
30381             if(dlg){
30382                 dlg.setZIndex(seed + (i*10));
30383             }
30384         }
30385     };
30386
30387     return {
30388         /**
30389          * The starting z-index for BasicDialogs (defaults to 9000)
30390          * @type Number The z-index value
30391          */
30392         zseed : 9000,
30393
30394         // private
30395         register : function(dlg){
30396             list[dlg.id] = dlg;
30397             accessList.push(dlg);
30398         },
30399
30400         // private
30401         unregister : function(dlg){
30402             delete list[dlg.id];
30403             var i=0;
30404             var len=0;
30405             if(!accessList.indexOf){
30406                 for(  i = 0, len = accessList.length; i < len; i++){
30407                     if(accessList[i] == dlg){
30408                         accessList.splice(i, 1);
30409                         return;
30410                     }
30411                 }
30412             }else{
30413                  i = accessList.indexOf(dlg);
30414                 if(i != -1){
30415                     accessList.splice(i, 1);
30416                 }
30417             }
30418         },
30419
30420         /**
30421          * Gets a registered dialog by id
30422          * @param {String/Object} id The id of the dialog or a dialog
30423          * @return {Roo.BasicDialog} this
30424          */
30425         get : function(id){
30426             return typeof id == "object" ? id : list[id];
30427         },
30428
30429         /**
30430          * Brings the specified dialog to the front
30431          * @param {String/Object} dlg The id of the dialog or a dialog
30432          * @return {Roo.BasicDialog} this
30433          */
30434         bringToFront : function(dlg){
30435             dlg = this.get(dlg);
30436             if(dlg != front){
30437                 front = dlg;
30438                 dlg._lastAccess = new Date().getTime();
30439                 orderDialogs();
30440             }
30441             return dlg;
30442         },
30443
30444         /**
30445          * Sends the specified dialog to the back
30446          * @param {String/Object} dlg The id of the dialog or a dialog
30447          * @return {Roo.BasicDialog} this
30448          */
30449         sendToBack : function(dlg){
30450             dlg = this.get(dlg);
30451             dlg._lastAccess = -(new Date().getTime());
30452             orderDialogs();
30453             return dlg;
30454         },
30455
30456         /**
30457          * Hides all dialogs
30458          */
30459         hideAll : function(){
30460             for(var id in list){
30461                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
30462                     list[id].hide();
30463                 }
30464             }
30465         }
30466     };
30467 }();
30468
30469 /**
30470  * @class Roo.LayoutDialog
30471  * @extends Roo.BasicDialog
30472  * Dialog which provides adjustments for working with a layout in a Dialog.
30473  * Add your necessary layout config options to the dialog's config.<br>
30474  * Example usage (including a nested layout):
30475  * <pre><code>
30476 if(!dialog){
30477     dialog = new Roo.LayoutDialog("download-dlg", {
30478         modal: true,
30479         width:600,
30480         height:450,
30481         shadow:true,
30482         minWidth:500,
30483         minHeight:350,
30484         autoTabs:true,
30485         proxyDrag:true,
30486         // layout config merges with the dialog config
30487         center:{
30488             tabPosition: "top",
30489             alwaysShowTabs: true
30490         }
30491     });
30492     dialog.addKeyListener(27, dialog.hide, dialog);
30493     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
30494     dialog.addButton("Build It!", this.getDownload, this);
30495
30496     // we can even add nested layouts
30497     var innerLayout = new Roo.BorderLayout("dl-inner", {
30498         east: {
30499             initialSize: 200,
30500             autoScroll:true,
30501             split:true
30502         },
30503         center: {
30504             autoScroll:true
30505         }
30506     });
30507     innerLayout.beginUpdate();
30508     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
30509     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
30510     innerLayout.endUpdate(true);
30511
30512     var layout = dialog.getLayout();
30513     layout.beginUpdate();
30514     layout.add("center", new Roo.ContentPanel("standard-panel",
30515                         {title: "Download the Source", fitToFrame:true}));
30516     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
30517                {title: "Build your own roo.js"}));
30518     layout.getRegion("center").showPanel(sp);
30519     layout.endUpdate();
30520 }
30521 </code></pre>
30522     * @constructor
30523     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
30524     * @param {Object} config configuration options
30525   */
30526 Roo.LayoutDialog = function(el, cfg){
30527     
30528     var config=  cfg;
30529     if (typeof(cfg) == 'undefined') {
30530         config = Roo.apply({}, el);
30531         // not sure why we use documentElement here.. - it should always be body.
30532         // IE7 borks horribly if we use documentElement.
30533         // webkit also does not like documentElement - it creates a body element...
30534         el = Roo.get( document.body || document.documentElement ).createChild();
30535         //config.autoCreate = true;
30536     }
30537     
30538     
30539     config.autoTabs = false;
30540     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
30541     this.body.setStyle({overflow:"hidden", position:"relative"});
30542     this.layout = new Roo.BorderLayout(this.body.dom, config);
30543     this.layout.monitorWindowResize = false;
30544     this.el.addClass("x-dlg-auto-layout");
30545     // fix case when center region overwrites center function
30546     this.center = Roo.BasicDialog.prototype.center;
30547     this.on("show", this.layout.layout, this.layout, true);
30548     if (config.items) {
30549         var xitems = config.items;
30550         delete config.items;
30551         Roo.each(xitems, this.addxtype, this);
30552     }
30553     
30554     
30555 };
30556 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
30557     /**
30558      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
30559      * @deprecated
30560      */
30561     endUpdate : function(){
30562         this.layout.endUpdate();
30563     },
30564
30565     /**
30566      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30567      *  @deprecated
30568      */
30569     beginUpdate : function(){
30570         this.layout.beginUpdate();
30571     },
30572
30573     /**
30574      * Get the BorderLayout for this dialog
30575      * @return {Roo.BorderLayout}
30576      */
30577     getLayout : function(){
30578         return this.layout;
30579     },
30580
30581     showEl : function(){
30582         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30583         if(Roo.isIE7){
30584             this.layout.layout();
30585         }
30586     },
30587
30588     // private
30589     // Use the syncHeightBeforeShow config option to control this automatically
30590     syncBodyHeight : function(){
30591         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30592         if(this.layout){this.layout.layout();}
30593     },
30594     
30595       /**
30596      * Add an xtype element (actually adds to the layout.)
30597      * @return {Object} xdata xtype object data.
30598      */
30599     
30600     addxtype : function(c) {
30601         return this.layout.addxtype(c);
30602     }
30603 });/*
30604  * Based on:
30605  * Ext JS Library 1.1.1
30606  * Copyright(c) 2006-2007, Ext JS, LLC.
30607  *
30608  * Originally Released Under LGPL - original licence link has changed is not relivant.
30609  *
30610  * Fork - LGPL
30611  * <script type="text/javascript">
30612  */
30613  
30614 /**
30615  * @class Roo.MessageBox
30616  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30617  * Example usage:
30618  *<pre><code>
30619 // Basic alert:
30620 Roo.Msg.alert('Status', 'Changes saved successfully.');
30621
30622 // Prompt for user data:
30623 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30624     if (btn == 'ok'){
30625         // process text value...
30626     }
30627 });
30628
30629 // Show a dialog using config options:
30630 Roo.Msg.show({
30631    title:'Save Changes?',
30632    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30633    buttons: Roo.Msg.YESNOCANCEL,
30634    fn: processResult,
30635    animEl: 'elId'
30636 });
30637 </code></pre>
30638  * @singleton
30639  */
30640 Roo.MessageBox = function(){
30641     var dlg, opt, mask, waitTimer;
30642     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30643     var buttons, activeTextEl, bwidth;
30644
30645     // private
30646     var handleButton = function(button){
30647         dlg.hide();
30648         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30649     };
30650
30651     // private
30652     var handleHide = function(){
30653         if(opt && opt.cls){
30654             dlg.el.removeClass(opt.cls);
30655         }
30656         if(waitTimer){
30657             Roo.TaskMgr.stop(waitTimer);
30658             waitTimer = null;
30659         }
30660     };
30661
30662     // private
30663     var updateButtons = function(b){
30664         var width = 0;
30665         if(!b){
30666             buttons["ok"].hide();
30667             buttons["cancel"].hide();
30668             buttons["yes"].hide();
30669             buttons["no"].hide();
30670             dlg.footer.dom.style.display = 'none';
30671             return width;
30672         }
30673         dlg.footer.dom.style.display = '';
30674         for(var k in buttons){
30675             if(typeof buttons[k] != "function"){
30676                 if(b[k]){
30677                     buttons[k].show();
30678                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30679                     width += buttons[k].el.getWidth()+15;
30680                 }else{
30681                     buttons[k].hide();
30682                 }
30683             }
30684         }
30685         return width;
30686     };
30687
30688     // private
30689     var handleEsc = function(d, k, e){
30690         if(opt && opt.closable !== false){
30691             dlg.hide();
30692         }
30693         if(e){
30694             e.stopEvent();
30695         }
30696     };
30697
30698     return {
30699         /**
30700          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30701          * @return {Roo.BasicDialog} The BasicDialog element
30702          */
30703         getDialog : function(){
30704            if(!dlg){
30705                 dlg = new Roo.BasicDialog("x-msg-box", {
30706                     autoCreate : true,
30707                     shadow: true,
30708                     draggable: true,
30709                     resizable:false,
30710                     constraintoviewport:false,
30711                     fixedcenter:true,
30712                     collapsible : false,
30713                     shim:true,
30714                     modal: true,
30715                     width:400, height:100,
30716                     buttonAlign:"center",
30717                     closeClick : function(){
30718                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30719                             handleButton("no");
30720                         }else{
30721                             handleButton("cancel");
30722                         }
30723                     }
30724                 });
30725                 dlg.on("hide", handleHide);
30726                 mask = dlg.mask;
30727                 dlg.addKeyListener(27, handleEsc);
30728                 buttons = {};
30729                 var bt = this.buttonText;
30730                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30731                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30732                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30733                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30734                 bodyEl = dlg.body.createChild({
30735
30736                     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>'
30737                 });
30738                 msgEl = bodyEl.dom.firstChild;
30739                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30740                 textboxEl.enableDisplayMode();
30741                 textboxEl.addKeyListener([10,13], function(){
30742                     if(dlg.isVisible() && opt && opt.buttons){
30743                         if(opt.buttons.ok){
30744                             handleButton("ok");
30745                         }else if(opt.buttons.yes){
30746                             handleButton("yes");
30747                         }
30748                     }
30749                 });
30750                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30751                 textareaEl.enableDisplayMode();
30752                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30753                 progressEl.enableDisplayMode();
30754                 var pf = progressEl.dom.firstChild;
30755                 if (pf) {
30756                     pp = Roo.get(pf.firstChild);
30757                     pp.setHeight(pf.offsetHeight);
30758                 }
30759                 
30760             }
30761             return dlg;
30762         },
30763
30764         /**
30765          * Updates the message box body text
30766          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30767          * the XHTML-compliant non-breaking space character '&amp;#160;')
30768          * @return {Roo.MessageBox} This message box
30769          */
30770         updateText : function(text){
30771             if(!dlg.isVisible() && !opt.width){
30772                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30773             }
30774             msgEl.innerHTML = text || '&#160;';
30775       
30776             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30777             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30778             var w = Math.max(
30779                     Math.min(opt.width || cw , this.maxWidth), 
30780                     Math.max(opt.minWidth || this.minWidth, bwidth)
30781             );
30782             if(opt.prompt){
30783                 activeTextEl.setWidth(w);
30784             }
30785             if(dlg.isVisible()){
30786                 dlg.fixedcenter = false;
30787             }
30788             // to big, make it scroll. = But as usual stupid IE does not support
30789             // !important..
30790             
30791             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30792                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30793                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30794             } else {
30795                 bodyEl.dom.style.height = '';
30796                 bodyEl.dom.style.overflowY = '';
30797             }
30798             if (cw > w) {
30799                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30800             } else {
30801                 bodyEl.dom.style.overflowX = '';
30802             }
30803             
30804             dlg.setContentSize(w, bodyEl.getHeight());
30805             if(dlg.isVisible()){
30806                 dlg.fixedcenter = true;
30807             }
30808             return this;
30809         },
30810
30811         /**
30812          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30813          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30814          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30815          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30816          * @return {Roo.MessageBox} This message box
30817          */
30818         updateProgress : function(value, text){
30819             if(text){
30820                 this.updateText(text);
30821             }
30822             if (pp) { // weird bug on my firefox - for some reason this is not defined
30823                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30824             }
30825             return this;
30826         },        
30827
30828         /**
30829          * Returns true if the message box is currently displayed
30830          * @return {Boolean} True if the message box is visible, else false
30831          */
30832         isVisible : function(){
30833             return dlg && dlg.isVisible();  
30834         },
30835
30836         /**
30837          * Hides the message box if it is displayed
30838          */
30839         hide : function(){
30840             if(this.isVisible()){
30841                 dlg.hide();
30842             }  
30843         },
30844
30845         /**
30846          * Displays a new message box, or reinitializes an existing message box, based on the config options
30847          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30848          * The following config object properties are supported:
30849          * <pre>
30850 Property    Type             Description
30851 ----------  ---------------  ------------------------------------------------------------------------------------
30852 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30853                                    closes (defaults to undefined)
30854 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30855                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30856 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30857                                    progress and wait dialogs will ignore this property and always hide the
30858                                    close button as they can only be closed programmatically.
30859 cls               String           A custom CSS class to apply to the message box element
30860 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30861                                    displayed (defaults to 75)
30862 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30863                                    function will be btn (the name of the button that was clicked, if applicable,
30864                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30865                                    Progress and wait dialogs will ignore this option since they do not respond to
30866                                    user actions and can only be closed programmatically, so any required function
30867                                    should be called by the same code after it closes the dialog.
30868 icon              String           A CSS class that provides a background image to be used as an icon for
30869                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30870 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30871 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30872 modal             Boolean          False to allow user interaction with the page while the message box is
30873                                    displayed (defaults to true)
30874 msg               String           A string that will replace the existing message box body text (defaults
30875                                    to the XHTML-compliant non-breaking space character '&#160;')
30876 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30877 progress          Boolean          True to display a progress bar (defaults to false)
30878 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30879 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30880 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30881 title             String           The title text
30882 value             String           The string value to set into the active textbox element if displayed
30883 wait              Boolean          True to display a progress bar (defaults to false)
30884 width             Number           The width of the dialog in pixels
30885 </pre>
30886          *
30887          * Example usage:
30888          * <pre><code>
30889 Roo.Msg.show({
30890    title: 'Address',
30891    msg: 'Please enter your address:',
30892    width: 300,
30893    buttons: Roo.MessageBox.OKCANCEL,
30894    multiline: true,
30895    fn: saveAddress,
30896    animEl: 'addAddressBtn'
30897 });
30898 </code></pre>
30899          * @param {Object} config Configuration options
30900          * @return {Roo.MessageBox} This message box
30901          */
30902         show : function(options)
30903         {
30904             
30905             // this causes nightmares if you show one dialog after another
30906             // especially on callbacks..
30907              
30908             if(this.isVisible()){
30909                 
30910                 this.hide();
30911                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30912                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30913                 Roo.log("New Dialog Message:" +  options.msg )
30914                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30915                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30916                 
30917             }
30918             var d = this.getDialog();
30919             opt = options;
30920             d.setTitle(opt.title || "&#160;");
30921             d.close.setDisplayed(opt.closable !== false);
30922             activeTextEl = textboxEl;
30923             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30924             if(opt.prompt){
30925                 if(opt.multiline){
30926                     textboxEl.hide();
30927                     textareaEl.show();
30928                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30929                         opt.multiline : this.defaultTextHeight);
30930                     activeTextEl = textareaEl;
30931                 }else{
30932                     textboxEl.show();
30933                     textareaEl.hide();
30934                 }
30935             }else{
30936                 textboxEl.hide();
30937                 textareaEl.hide();
30938             }
30939             progressEl.setDisplayed(opt.progress === true);
30940             this.updateProgress(0);
30941             activeTextEl.dom.value = opt.value || "";
30942             if(opt.prompt){
30943                 dlg.setDefaultButton(activeTextEl);
30944             }else{
30945                 var bs = opt.buttons;
30946                 var db = null;
30947                 if(bs && bs.ok){
30948                     db = buttons["ok"];
30949                 }else if(bs && bs.yes){
30950                     db = buttons["yes"];
30951                 }
30952                 dlg.setDefaultButton(db);
30953             }
30954             bwidth = updateButtons(opt.buttons);
30955             this.updateText(opt.msg);
30956             if(opt.cls){
30957                 d.el.addClass(opt.cls);
30958             }
30959             d.proxyDrag = opt.proxyDrag === true;
30960             d.modal = opt.modal !== false;
30961             d.mask = opt.modal !== false ? mask : false;
30962             if(!d.isVisible()){
30963                 // force it to the end of the z-index stack so it gets a cursor in FF
30964                 document.body.appendChild(dlg.el.dom);
30965                 d.animateTarget = null;
30966                 d.show(options.animEl);
30967             }
30968             return this;
30969         },
30970
30971         /**
30972          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30973          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30974          * and closing the message box when the process is complete.
30975          * @param {String} title The title bar text
30976          * @param {String} msg The message box body text
30977          * @return {Roo.MessageBox} This message box
30978          */
30979         progress : function(title, msg){
30980             this.show({
30981                 title : title,
30982                 msg : msg,
30983                 buttons: false,
30984                 progress:true,
30985                 closable:false,
30986                 minWidth: this.minProgressWidth,
30987                 modal : true
30988             });
30989             return this;
30990         },
30991
30992         /**
30993          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30994          * If a callback function is passed it will be called after the user clicks the button, and the
30995          * id of the button that was clicked will be passed as the only parameter to the callback
30996          * (could also be the top-right close button).
30997          * @param {String} title The title bar text
30998          * @param {String} msg The message box body text
30999          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31000          * @param {Object} scope (optional) The scope of the callback function
31001          * @return {Roo.MessageBox} This message box
31002          */
31003         alert : function(title, msg, fn, scope){
31004             this.show({
31005                 title : title,
31006                 msg : msg,
31007                 buttons: this.OK,
31008                 fn: fn,
31009                 scope : scope,
31010                 modal : true
31011             });
31012             return this;
31013         },
31014
31015         /**
31016          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31017          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31018          * You are responsible for closing the message box when the process is complete.
31019          * @param {String} msg The message box body text
31020          * @param {String} title (optional) The title bar text
31021          * @return {Roo.MessageBox} This message box
31022          */
31023         wait : function(msg, title){
31024             this.show({
31025                 title : title,
31026                 msg : msg,
31027                 buttons: false,
31028                 closable:false,
31029                 progress:true,
31030                 modal:true,
31031                 width:300,
31032                 wait:true
31033             });
31034             waitTimer = Roo.TaskMgr.start({
31035                 run: function(i){
31036                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31037                 },
31038                 interval: 1000
31039             });
31040             return this;
31041         },
31042
31043         /**
31044          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31045          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31046          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31047          * @param {String} title The title bar text
31048          * @param {String} msg The message box body text
31049          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31050          * @param {Object} scope (optional) The scope of the callback function
31051          * @return {Roo.MessageBox} This message box
31052          */
31053         confirm : function(title, msg, fn, scope){
31054             this.show({
31055                 title : title,
31056                 msg : msg,
31057                 buttons: this.YESNO,
31058                 fn: fn,
31059                 scope : scope,
31060                 modal : true
31061             });
31062             return this;
31063         },
31064
31065         /**
31066          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31067          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31068          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31069          * (could also be the top-right close button) and the text that was entered will be passed as the two
31070          * parameters to the callback.
31071          * @param {String} title The title bar text
31072          * @param {String} msg The message box body text
31073          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31074          * @param {Object} scope (optional) The scope of the callback function
31075          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31076          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31077          * @return {Roo.MessageBox} This message box
31078          */
31079         prompt : function(title, msg, fn, scope, multiline){
31080             this.show({
31081                 title : title,
31082                 msg : msg,
31083                 buttons: this.OKCANCEL,
31084                 fn: fn,
31085                 minWidth:250,
31086                 scope : scope,
31087                 prompt:true,
31088                 multiline: multiline,
31089                 modal : true
31090             });
31091             return this;
31092         },
31093
31094         /**
31095          * Button config that displays a single OK button
31096          * @type Object
31097          */
31098         OK : {ok:true},
31099         /**
31100          * Button config that displays Yes and No buttons
31101          * @type Object
31102          */
31103         YESNO : {yes:true, no:true},
31104         /**
31105          * Button config that displays OK and Cancel buttons
31106          * @type Object
31107          */
31108         OKCANCEL : {ok:true, cancel:true},
31109         /**
31110          * Button config that displays Yes, No and Cancel buttons
31111          * @type Object
31112          */
31113         YESNOCANCEL : {yes:true, no:true, cancel:true},
31114
31115         /**
31116          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31117          * @type Number
31118          */
31119         defaultTextHeight : 75,
31120         /**
31121          * The maximum width in pixels of the message box (defaults to 600)
31122          * @type Number
31123          */
31124         maxWidth : 600,
31125         /**
31126          * The minimum width in pixels of the message box (defaults to 100)
31127          * @type Number
31128          */
31129         minWidth : 100,
31130         /**
31131          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31132          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31133          * @type Number
31134          */
31135         minProgressWidth : 250,
31136         /**
31137          * An object containing the default button text strings that can be overriden for localized language support.
31138          * Supported properties are: ok, cancel, yes and no.
31139          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31140          * @type Object
31141          */
31142         buttonText : {
31143             ok : "OK",
31144             cancel : "Cancel",
31145             yes : "Yes",
31146             no : "No"
31147         }
31148     };
31149 }();
31150
31151 /**
31152  * Shorthand for {@link Roo.MessageBox}
31153  */
31154 Roo.Msg = Roo.MessageBox;/*
31155  * Based on:
31156  * Ext JS Library 1.1.1
31157  * Copyright(c) 2006-2007, Ext JS, LLC.
31158  *
31159  * Originally Released Under LGPL - original licence link has changed is not relivant.
31160  *
31161  * Fork - LGPL
31162  * <script type="text/javascript">
31163  */
31164 /**
31165  * @class Roo.QuickTips
31166  * Provides attractive and customizable tooltips for any element.
31167  * @singleton
31168  */
31169 Roo.QuickTips = function(){
31170     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31171     var ce, bd, xy, dd;
31172     var visible = false, disabled = true, inited = false;
31173     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31174     
31175     var onOver = function(e){
31176         if(disabled){
31177             return;
31178         }
31179         var t = e.getTarget();
31180         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31181             return;
31182         }
31183         if(ce && t == ce.el){
31184             clearTimeout(hideProc);
31185             return;
31186         }
31187         if(t && tagEls[t.id]){
31188             tagEls[t.id].el = t;
31189             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31190             return;
31191         }
31192         var ttp, et = Roo.fly(t);
31193         var ns = cfg.namespace;
31194         if(tm.interceptTitles && t.title){
31195             ttp = t.title;
31196             t.qtip = ttp;
31197             t.removeAttribute("title");
31198             e.preventDefault();
31199         }else{
31200             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31201         }
31202         if(ttp){
31203             showProc = show.defer(tm.showDelay, tm, [{
31204                 el: t, 
31205                 text: ttp, 
31206                 width: et.getAttributeNS(ns, cfg.width),
31207                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31208                 title: et.getAttributeNS(ns, cfg.title),
31209                     cls: et.getAttributeNS(ns, cfg.cls)
31210             }]);
31211         }
31212     };
31213     
31214     var onOut = function(e){
31215         clearTimeout(showProc);
31216         var t = e.getTarget();
31217         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31218             hideProc = setTimeout(hide, tm.hideDelay);
31219         }
31220     };
31221     
31222     var onMove = function(e){
31223         if(disabled){
31224             return;
31225         }
31226         xy = e.getXY();
31227         xy[1] += 18;
31228         if(tm.trackMouse && ce){
31229             el.setXY(xy);
31230         }
31231     };
31232     
31233     var onDown = function(e){
31234         clearTimeout(showProc);
31235         clearTimeout(hideProc);
31236         if(!e.within(el)){
31237             if(tm.hideOnClick){
31238                 hide();
31239                 tm.disable();
31240                 tm.enable.defer(100, tm);
31241             }
31242         }
31243     };
31244     
31245     var getPad = function(){
31246         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31247     };
31248
31249     var show = function(o){
31250         if(disabled){
31251             return;
31252         }
31253         clearTimeout(dismissProc);
31254         ce = o;
31255         if(removeCls){ // in case manually hidden
31256             el.removeClass(removeCls);
31257             removeCls = null;
31258         }
31259         if(ce.cls){
31260             el.addClass(ce.cls);
31261             removeCls = ce.cls;
31262         }
31263         if(ce.title){
31264             tipTitle.update(ce.title);
31265             tipTitle.show();
31266         }else{
31267             tipTitle.update('');
31268             tipTitle.hide();
31269         }
31270         el.dom.style.width  = tm.maxWidth+'px';
31271         //tipBody.dom.style.width = '';
31272         tipBodyText.update(o.text);
31273         var p = getPad(), w = ce.width;
31274         if(!w){
31275             var td = tipBodyText.dom;
31276             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31277             if(aw > tm.maxWidth){
31278                 w = tm.maxWidth;
31279             }else if(aw < tm.minWidth){
31280                 w = tm.minWidth;
31281             }else{
31282                 w = aw;
31283             }
31284         }
31285         //tipBody.setWidth(w);
31286         el.setWidth(parseInt(w, 10) + p);
31287         if(ce.autoHide === false){
31288             close.setDisplayed(true);
31289             if(dd){
31290                 dd.unlock();
31291             }
31292         }else{
31293             close.setDisplayed(false);
31294             if(dd){
31295                 dd.lock();
31296             }
31297         }
31298         if(xy){
31299             el.avoidY = xy[1]-18;
31300             el.setXY(xy);
31301         }
31302         if(tm.animate){
31303             el.setOpacity(.1);
31304             el.setStyle("visibility", "visible");
31305             el.fadeIn({callback: afterShow});
31306         }else{
31307             afterShow();
31308         }
31309     };
31310     
31311     var afterShow = function(){
31312         if(ce){
31313             el.show();
31314             esc.enable();
31315             if(tm.autoDismiss && ce.autoHide !== false){
31316                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31317             }
31318         }
31319     };
31320     
31321     var hide = function(noanim){
31322         clearTimeout(dismissProc);
31323         clearTimeout(hideProc);
31324         ce = null;
31325         if(el.isVisible()){
31326             esc.disable();
31327             if(noanim !== true && tm.animate){
31328                 el.fadeOut({callback: afterHide});
31329             }else{
31330                 afterHide();
31331             } 
31332         }
31333     };
31334     
31335     var afterHide = function(){
31336         el.hide();
31337         if(removeCls){
31338             el.removeClass(removeCls);
31339             removeCls = null;
31340         }
31341     };
31342     
31343     return {
31344         /**
31345         * @cfg {Number} minWidth
31346         * The minimum width of the quick tip (defaults to 40)
31347         */
31348        minWidth : 40,
31349         /**
31350         * @cfg {Number} maxWidth
31351         * The maximum width of the quick tip (defaults to 300)
31352         */
31353        maxWidth : 300,
31354         /**
31355         * @cfg {Boolean} interceptTitles
31356         * True to automatically use the element's DOM title value if available (defaults to false)
31357         */
31358        interceptTitles : false,
31359         /**
31360         * @cfg {Boolean} trackMouse
31361         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31362         */
31363        trackMouse : false,
31364         /**
31365         * @cfg {Boolean} hideOnClick
31366         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31367         */
31368        hideOnClick : true,
31369         /**
31370         * @cfg {Number} showDelay
31371         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31372         */
31373        showDelay : 500,
31374         /**
31375         * @cfg {Number} hideDelay
31376         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31377         */
31378        hideDelay : 200,
31379         /**
31380         * @cfg {Boolean} autoHide
31381         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31382         * Used in conjunction with hideDelay.
31383         */
31384        autoHide : true,
31385         /**
31386         * @cfg {Boolean}
31387         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31388         * (defaults to true).  Used in conjunction with autoDismissDelay.
31389         */
31390        autoDismiss : true,
31391         /**
31392         * @cfg {Number}
31393         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31394         */
31395        autoDismissDelay : 5000,
31396        /**
31397         * @cfg {Boolean} animate
31398         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31399         */
31400        animate : false,
31401
31402        /**
31403         * @cfg {String} title
31404         * Title text to display (defaults to '').  This can be any valid HTML markup.
31405         */
31406         title: '',
31407        /**
31408         * @cfg {String} text
31409         * Body text to display (defaults to '').  This can be any valid HTML markup.
31410         */
31411         text : '',
31412        /**
31413         * @cfg {String} cls
31414         * A CSS class to apply to the base quick tip element (defaults to '').
31415         */
31416         cls : '',
31417        /**
31418         * @cfg {Number} width
31419         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31420         * minWidth or maxWidth.
31421         */
31422         width : null,
31423
31424     /**
31425      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31426      * or display QuickTips in a page.
31427      */
31428        init : function(){
31429           tm = Roo.QuickTips;
31430           cfg = tm.tagConfig;
31431           if(!inited){
31432               if(!Roo.isReady){ // allow calling of init() before onReady
31433                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
31434                   return;
31435               }
31436               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
31437               el.fxDefaults = {stopFx: true};
31438               // maximum custom styling
31439               //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>');
31440               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>');              
31441               tipTitle = el.child('h3');
31442               tipTitle.enableDisplayMode("block");
31443               tipBody = el.child('div.x-tip-bd');
31444               tipBodyText = el.child('div.x-tip-bd-inner');
31445               //bdLeft = el.child('div.x-tip-bd-left');
31446               //bdRight = el.child('div.x-tip-bd-right');
31447               close = el.child('div.x-tip-close');
31448               close.enableDisplayMode("block");
31449               close.on("click", hide);
31450               var d = Roo.get(document);
31451               d.on("mousedown", onDown);
31452               d.on("mouseover", onOver);
31453               d.on("mouseout", onOut);
31454               d.on("mousemove", onMove);
31455               esc = d.addKeyListener(27, hide);
31456               esc.disable();
31457               if(Roo.dd.DD){
31458                   dd = el.initDD("default", null, {
31459                       onDrag : function(){
31460                           el.sync();  
31461                       }
31462                   });
31463                   dd.setHandleElId(tipTitle.id);
31464                   dd.lock();
31465               }
31466               inited = true;
31467           }
31468           this.enable(); 
31469        },
31470
31471     /**
31472      * Configures a new quick tip instance and assigns it to a target element.  The following config options
31473      * are supported:
31474      * <pre>
31475 Property    Type                   Description
31476 ----------  ---------------------  ------------------------------------------------------------------------
31477 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
31478      * </ul>
31479      * @param {Object} config The config object
31480      */
31481        register : function(config){
31482            var cs = config instanceof Array ? config : arguments;
31483            for(var i = 0, len = cs.length; i < len; i++) {
31484                var c = cs[i];
31485                var target = c.target;
31486                if(target){
31487                    if(target instanceof Array){
31488                        for(var j = 0, jlen = target.length; j < jlen; j++){
31489                            tagEls[target[j]] = c;
31490                        }
31491                    }else{
31492                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
31493                    }
31494                }
31495            }
31496        },
31497
31498     /**
31499      * Removes this quick tip from its element and destroys it.
31500      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
31501      */
31502        unregister : function(el){
31503            delete tagEls[Roo.id(el)];
31504        },
31505
31506     /**
31507      * Enable this quick tip.
31508      */
31509        enable : function(){
31510            if(inited && disabled){
31511                locks.pop();
31512                if(locks.length < 1){
31513                    disabled = false;
31514                }
31515            }
31516        },
31517
31518     /**
31519      * Disable this quick tip.
31520      */
31521        disable : function(){
31522           disabled = true;
31523           clearTimeout(showProc);
31524           clearTimeout(hideProc);
31525           clearTimeout(dismissProc);
31526           if(ce){
31527               hide(true);
31528           }
31529           locks.push(1);
31530        },
31531
31532     /**
31533      * Returns true if the quick tip is enabled, else false.
31534      */
31535        isEnabled : function(){
31536             return !disabled;
31537        },
31538
31539         // private
31540        tagConfig : {
31541            namespace : "ext",
31542            attribute : "qtip",
31543            width : "width",
31544            target : "target",
31545            title : "qtitle",
31546            hide : "hide",
31547            cls : "qclass"
31548        }
31549    };
31550 }();
31551
31552 // backwards compat
31553 Roo.QuickTips.tips = Roo.QuickTips.register;/*
31554  * Based on:
31555  * Ext JS Library 1.1.1
31556  * Copyright(c) 2006-2007, Ext JS, LLC.
31557  *
31558  * Originally Released Under LGPL - original licence link has changed is not relivant.
31559  *
31560  * Fork - LGPL
31561  * <script type="text/javascript">
31562  */
31563  
31564
31565 /**
31566  * @class Roo.tree.TreePanel
31567  * @extends Roo.data.Tree
31568
31569  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31570  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31571  * @cfg {Boolean} enableDD true to enable drag and drop
31572  * @cfg {Boolean} enableDrag true to enable just drag
31573  * @cfg {Boolean} enableDrop true to enable just drop
31574  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31575  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31576  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31577  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31578  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31579  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31580  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31581  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31582  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31583  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31584  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31585  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31586  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31587  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31588  * @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>
31589  * @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>
31590  * 
31591  * @constructor
31592  * @param {String/HTMLElement/Element} el The container element
31593  * @param {Object} config
31594  */
31595 Roo.tree.TreePanel = function(el, config){
31596     var root = false;
31597     var loader = false;
31598     if (config.root) {
31599         root = config.root;
31600         delete config.root;
31601     }
31602     if (config.loader) {
31603         loader = config.loader;
31604         delete config.loader;
31605     }
31606     
31607     Roo.apply(this, config);
31608     Roo.tree.TreePanel.superclass.constructor.call(this);
31609     this.el = Roo.get(el);
31610     this.el.addClass('x-tree');
31611     //console.log(root);
31612     if (root) {
31613         this.setRootNode( Roo.factory(root, Roo.tree));
31614     }
31615     if (loader) {
31616         this.loader = Roo.factory(loader, Roo.tree);
31617     }
31618    /**
31619     * Read-only. The id of the container element becomes this TreePanel's id.
31620     */
31621     this.id = this.el.id;
31622     this.addEvents({
31623         /**
31624         * @event beforeload
31625         * Fires before a node is loaded, return false to cancel
31626         * @param {Node} node The node being loaded
31627         */
31628         "beforeload" : true,
31629         /**
31630         * @event load
31631         * Fires when a node is loaded
31632         * @param {Node} node The node that was loaded
31633         */
31634         "load" : true,
31635         /**
31636         * @event textchange
31637         * Fires when the text for a node is changed
31638         * @param {Node} node The node
31639         * @param {String} text The new text
31640         * @param {String} oldText The old text
31641         */
31642         "textchange" : true,
31643         /**
31644         * @event beforeexpand
31645         * Fires before a node is expanded, return false to cancel.
31646         * @param {Node} node The node
31647         * @param {Boolean} deep
31648         * @param {Boolean} anim
31649         */
31650         "beforeexpand" : true,
31651         /**
31652         * @event beforecollapse
31653         * Fires before a node is collapsed, return false to cancel.
31654         * @param {Node} node The node
31655         * @param {Boolean} deep
31656         * @param {Boolean} anim
31657         */
31658         "beforecollapse" : true,
31659         /**
31660         * @event expand
31661         * Fires when a node is expanded
31662         * @param {Node} node The node
31663         */
31664         "expand" : true,
31665         /**
31666         * @event disabledchange
31667         * Fires when the disabled status of a node changes
31668         * @param {Node} node The node
31669         * @param {Boolean} disabled
31670         */
31671         "disabledchange" : true,
31672         /**
31673         * @event collapse
31674         * Fires when a node is collapsed
31675         * @param {Node} node The node
31676         */
31677         "collapse" : true,
31678         /**
31679         * @event beforeclick
31680         * Fires before click processing on a node. Return false to cancel the default action.
31681         * @param {Node} node The node
31682         * @param {Roo.EventObject} e The event object
31683         */
31684         "beforeclick":true,
31685         /**
31686         * @event checkchange
31687         * Fires when a node with a checkbox's checked property changes
31688         * @param {Node} this This node
31689         * @param {Boolean} checked
31690         */
31691         "checkchange":true,
31692         /**
31693         * @event click
31694         * Fires when a node is clicked
31695         * @param {Node} node The node
31696         * @param {Roo.EventObject} e The event object
31697         */
31698         "click":true,
31699         /**
31700         * @event dblclick
31701         * Fires when a node is double clicked
31702         * @param {Node} node The node
31703         * @param {Roo.EventObject} e The event object
31704         */
31705         "dblclick":true,
31706         /**
31707         * @event contextmenu
31708         * Fires when a node is right clicked
31709         * @param {Node} node The node
31710         * @param {Roo.EventObject} e The event object
31711         */
31712         "contextmenu":true,
31713         /**
31714         * @event beforechildrenrendered
31715         * Fires right before the child nodes for a node are rendered
31716         * @param {Node} node The node
31717         */
31718         "beforechildrenrendered":true,
31719         /**
31720         * @event startdrag
31721         * Fires when a node starts being dragged
31722         * @param {Roo.tree.TreePanel} this
31723         * @param {Roo.tree.TreeNode} node
31724         * @param {event} e The raw browser event
31725         */ 
31726        "startdrag" : true,
31727        /**
31728         * @event enddrag
31729         * Fires when a drag operation is complete
31730         * @param {Roo.tree.TreePanel} this
31731         * @param {Roo.tree.TreeNode} node
31732         * @param {event} e The raw browser event
31733         */
31734        "enddrag" : true,
31735        /**
31736         * @event dragdrop
31737         * Fires when a dragged node is dropped on a valid DD target
31738         * @param {Roo.tree.TreePanel} this
31739         * @param {Roo.tree.TreeNode} node
31740         * @param {DD} dd The dd it was dropped on
31741         * @param {event} e The raw browser event
31742         */
31743        "dragdrop" : true,
31744        /**
31745         * @event beforenodedrop
31746         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31747         * passed to handlers has the following properties:<br />
31748         * <ul style="padding:5px;padding-left:16px;">
31749         * <li>tree - The TreePanel</li>
31750         * <li>target - The node being targeted for the drop</li>
31751         * <li>data - The drag data from the drag source</li>
31752         * <li>point - The point of the drop - append, above or below</li>
31753         * <li>source - The drag source</li>
31754         * <li>rawEvent - Raw mouse event</li>
31755         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31756         * to be inserted by setting them on this object.</li>
31757         * <li>cancel - Set this to true to cancel the drop.</li>
31758         * </ul>
31759         * @param {Object} dropEvent
31760         */
31761        "beforenodedrop" : true,
31762        /**
31763         * @event nodedrop
31764         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31765         * passed to handlers has the following properties:<br />
31766         * <ul style="padding:5px;padding-left:16px;">
31767         * <li>tree - The TreePanel</li>
31768         * <li>target - The node being targeted for the drop</li>
31769         * <li>data - The drag data from the drag source</li>
31770         * <li>point - The point of the drop - append, above or below</li>
31771         * <li>source - The drag source</li>
31772         * <li>rawEvent - Raw mouse event</li>
31773         * <li>dropNode - Dropped node(s).</li>
31774         * </ul>
31775         * @param {Object} dropEvent
31776         */
31777        "nodedrop" : true,
31778         /**
31779         * @event nodedragover
31780         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31781         * passed to handlers has the following properties:<br />
31782         * <ul style="padding:5px;padding-left:16px;">
31783         * <li>tree - The TreePanel</li>
31784         * <li>target - The node being targeted for the drop</li>
31785         * <li>data - The drag data from the drag source</li>
31786         * <li>point - The point of the drop - append, above or below</li>
31787         * <li>source - The drag source</li>
31788         * <li>rawEvent - Raw mouse event</li>
31789         * <li>dropNode - Drop node(s) provided by the source.</li>
31790         * <li>cancel - Set this to true to signal drop not allowed.</li>
31791         * </ul>
31792         * @param {Object} dragOverEvent
31793         */
31794        "nodedragover" : true
31795         
31796     });
31797     if(this.singleExpand){
31798        this.on("beforeexpand", this.restrictExpand, this);
31799     }
31800     if (this.editor) {
31801         this.editor.tree = this;
31802         this.editor = Roo.factory(this.editor, Roo.tree);
31803     }
31804     
31805     if (this.selModel) {
31806         this.selModel = Roo.factory(this.selModel, Roo.tree);
31807     }
31808    
31809 };
31810 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31811     rootVisible : true,
31812     animate: Roo.enableFx,
31813     lines : true,
31814     enableDD : false,
31815     hlDrop : Roo.enableFx,
31816   
31817     renderer: false,
31818     
31819     rendererTip: false,
31820     // private
31821     restrictExpand : function(node){
31822         var p = node.parentNode;
31823         if(p){
31824             if(p.expandedChild && p.expandedChild.parentNode == p){
31825                 p.expandedChild.collapse();
31826             }
31827             p.expandedChild = node;
31828         }
31829     },
31830
31831     // private override
31832     setRootNode : function(node){
31833         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31834         if(!this.rootVisible){
31835             node.ui = new Roo.tree.RootTreeNodeUI(node);
31836         }
31837         return node;
31838     },
31839
31840     /**
31841      * Returns the container element for this TreePanel
31842      */
31843     getEl : function(){
31844         return this.el;
31845     },
31846
31847     /**
31848      * Returns the default TreeLoader for this TreePanel
31849      */
31850     getLoader : function(){
31851         return this.loader;
31852     },
31853
31854     /**
31855      * Expand all nodes
31856      */
31857     expandAll : function(){
31858         this.root.expand(true);
31859     },
31860
31861     /**
31862      * Collapse all nodes
31863      */
31864     collapseAll : function(){
31865         this.root.collapse(true);
31866     },
31867
31868     /**
31869      * Returns the selection model used by this TreePanel
31870      */
31871     getSelectionModel : function(){
31872         if(!this.selModel){
31873             this.selModel = new Roo.tree.DefaultSelectionModel();
31874         }
31875         return this.selModel;
31876     },
31877
31878     /**
31879      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31880      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31881      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31882      * @return {Array}
31883      */
31884     getChecked : function(a, startNode){
31885         startNode = startNode || this.root;
31886         var r = [];
31887         var f = function(){
31888             if(this.attributes.checked){
31889                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31890             }
31891         }
31892         startNode.cascade(f);
31893         return r;
31894     },
31895
31896     /**
31897      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31898      * @param {String} path
31899      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31900      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31901      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31902      */
31903     expandPath : function(path, attr, callback){
31904         attr = attr || "id";
31905         var keys = path.split(this.pathSeparator);
31906         var curNode = this.root;
31907         if(curNode.attributes[attr] != keys[1]){ // invalid root
31908             if(callback){
31909                 callback(false, null);
31910             }
31911             return;
31912         }
31913         var index = 1;
31914         var f = function(){
31915             if(++index == keys.length){
31916                 if(callback){
31917                     callback(true, curNode);
31918                 }
31919                 return;
31920             }
31921             var c = curNode.findChild(attr, keys[index]);
31922             if(!c){
31923                 if(callback){
31924                     callback(false, curNode);
31925                 }
31926                 return;
31927             }
31928             curNode = c;
31929             c.expand(false, false, f);
31930         };
31931         curNode.expand(false, false, f);
31932     },
31933
31934     /**
31935      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31936      * @param {String} path
31937      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31938      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31939      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31940      */
31941     selectPath : function(path, attr, callback){
31942         attr = attr || "id";
31943         var keys = path.split(this.pathSeparator);
31944         var v = keys.pop();
31945         if(keys.length > 0){
31946             var f = function(success, node){
31947                 if(success && node){
31948                     var n = node.findChild(attr, v);
31949                     if(n){
31950                         n.select();
31951                         if(callback){
31952                             callback(true, n);
31953                         }
31954                     }else if(callback){
31955                         callback(false, n);
31956                     }
31957                 }else{
31958                     if(callback){
31959                         callback(false, n);
31960                     }
31961                 }
31962             };
31963             this.expandPath(keys.join(this.pathSeparator), attr, f);
31964         }else{
31965             this.root.select();
31966             if(callback){
31967                 callback(true, this.root);
31968             }
31969         }
31970     },
31971
31972     getTreeEl : function(){
31973         return this.el;
31974     },
31975
31976     /**
31977      * Trigger rendering of this TreePanel
31978      */
31979     render : function(){
31980         if (this.innerCt) {
31981             return this; // stop it rendering more than once!!
31982         }
31983         
31984         this.innerCt = this.el.createChild({tag:"ul",
31985                cls:"x-tree-root-ct " +
31986                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31987
31988         if(this.containerScroll){
31989             Roo.dd.ScrollManager.register(this.el);
31990         }
31991         if((this.enableDD || this.enableDrop) && !this.dropZone){
31992            /**
31993             * The dropZone used by this tree if drop is enabled
31994             * @type Roo.tree.TreeDropZone
31995             */
31996              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31997                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31998            });
31999         }
32000         if((this.enableDD || this.enableDrag) && !this.dragZone){
32001            /**
32002             * The dragZone used by this tree if drag is enabled
32003             * @type Roo.tree.TreeDragZone
32004             */
32005             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32006                ddGroup: this.ddGroup || "TreeDD",
32007                scroll: this.ddScroll
32008            });
32009         }
32010         this.getSelectionModel().init(this);
32011         if (!this.root) {
32012             Roo.log("ROOT not set in tree");
32013             return this;
32014         }
32015         this.root.render();
32016         if(!this.rootVisible){
32017             this.root.renderChildren();
32018         }
32019         return this;
32020     }
32021 });/*
32022  * Based on:
32023  * Ext JS Library 1.1.1
32024  * Copyright(c) 2006-2007, Ext JS, LLC.
32025  *
32026  * Originally Released Under LGPL - original licence link has changed is not relivant.
32027  *
32028  * Fork - LGPL
32029  * <script type="text/javascript">
32030  */
32031  
32032
32033 /**
32034  * @class Roo.tree.DefaultSelectionModel
32035  * @extends Roo.util.Observable
32036  * The default single selection for a TreePanel.
32037  * @param {Object} cfg Configuration
32038  */
32039 Roo.tree.DefaultSelectionModel = function(cfg){
32040    this.selNode = null;
32041    
32042    
32043    
32044    this.addEvents({
32045        /**
32046         * @event selectionchange
32047         * Fires when the selected node changes
32048         * @param {DefaultSelectionModel} this
32049         * @param {TreeNode} node the new selection
32050         */
32051        "selectionchange" : true,
32052
32053        /**
32054         * @event beforeselect
32055         * Fires before the selected node changes, return false to cancel the change
32056         * @param {DefaultSelectionModel} this
32057         * @param {TreeNode} node the new selection
32058         * @param {TreeNode} node the old selection
32059         */
32060        "beforeselect" : true
32061    });
32062    
32063     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32064 };
32065
32066 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32067     init : function(tree){
32068         this.tree = tree;
32069         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32070         tree.on("click", this.onNodeClick, this);
32071     },
32072     
32073     onNodeClick : function(node, e){
32074         if (e.ctrlKey && this.selNode == node)  {
32075             this.unselect(node);
32076             return;
32077         }
32078         this.select(node);
32079     },
32080     
32081     /**
32082      * Select a node.
32083      * @param {TreeNode} node The node to select
32084      * @return {TreeNode} The selected node
32085      */
32086     select : function(node){
32087         var last = this.selNode;
32088         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32089             if(last){
32090                 last.ui.onSelectedChange(false);
32091             }
32092             this.selNode = node;
32093             node.ui.onSelectedChange(true);
32094             this.fireEvent("selectionchange", this, node, last);
32095         }
32096         return node;
32097     },
32098     
32099     /**
32100      * Deselect a node.
32101      * @param {TreeNode} node The node to unselect
32102      */
32103     unselect : function(node){
32104         if(this.selNode == node){
32105             this.clearSelections();
32106         }    
32107     },
32108     
32109     /**
32110      * Clear all selections
32111      */
32112     clearSelections : function(){
32113         var n = this.selNode;
32114         if(n){
32115             n.ui.onSelectedChange(false);
32116             this.selNode = null;
32117             this.fireEvent("selectionchange", this, null);
32118         }
32119         return n;
32120     },
32121     
32122     /**
32123      * Get the selected node
32124      * @return {TreeNode} The selected node
32125      */
32126     getSelectedNode : function(){
32127         return this.selNode;    
32128     },
32129     
32130     /**
32131      * Returns true if the node is selected
32132      * @param {TreeNode} node The node to check
32133      * @return {Boolean}
32134      */
32135     isSelected : function(node){
32136         return this.selNode == node;  
32137     },
32138
32139     /**
32140      * Selects the node above the selected node in the tree, intelligently walking the nodes
32141      * @return TreeNode The new selection
32142      */
32143     selectPrevious : function(){
32144         var s = this.selNode || this.lastSelNode;
32145         if(!s){
32146             return null;
32147         }
32148         var ps = s.previousSibling;
32149         if(ps){
32150             if(!ps.isExpanded() || ps.childNodes.length < 1){
32151                 return this.select(ps);
32152             } else{
32153                 var lc = ps.lastChild;
32154                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32155                     lc = lc.lastChild;
32156                 }
32157                 return this.select(lc);
32158             }
32159         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32160             return this.select(s.parentNode);
32161         }
32162         return null;
32163     },
32164
32165     /**
32166      * Selects the node above the selected node in the tree, intelligently walking the nodes
32167      * @return TreeNode The new selection
32168      */
32169     selectNext : function(){
32170         var s = this.selNode || this.lastSelNode;
32171         if(!s){
32172             return null;
32173         }
32174         if(s.firstChild && s.isExpanded()){
32175              return this.select(s.firstChild);
32176          }else if(s.nextSibling){
32177              return this.select(s.nextSibling);
32178          }else if(s.parentNode){
32179             var newS = null;
32180             s.parentNode.bubble(function(){
32181                 if(this.nextSibling){
32182                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32183                     return false;
32184                 }
32185             });
32186             return newS;
32187          }
32188         return null;
32189     },
32190
32191     onKeyDown : function(e){
32192         var s = this.selNode || this.lastSelNode;
32193         // undesirable, but required
32194         var sm = this;
32195         if(!s){
32196             return;
32197         }
32198         var k = e.getKey();
32199         switch(k){
32200              case e.DOWN:
32201                  e.stopEvent();
32202                  this.selectNext();
32203              break;
32204              case e.UP:
32205                  e.stopEvent();
32206                  this.selectPrevious();
32207              break;
32208              case e.RIGHT:
32209                  e.preventDefault();
32210                  if(s.hasChildNodes()){
32211                      if(!s.isExpanded()){
32212                          s.expand();
32213                      }else if(s.firstChild){
32214                          this.select(s.firstChild, e);
32215                      }
32216                  }
32217              break;
32218              case e.LEFT:
32219                  e.preventDefault();
32220                  if(s.hasChildNodes() && s.isExpanded()){
32221                      s.collapse();
32222                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32223                      this.select(s.parentNode, e);
32224                  }
32225              break;
32226         };
32227     }
32228 });
32229
32230 /**
32231  * @class Roo.tree.MultiSelectionModel
32232  * @extends Roo.util.Observable
32233  * Multi selection for a TreePanel.
32234  * @param {Object} cfg Configuration
32235  */
32236 Roo.tree.MultiSelectionModel = function(){
32237    this.selNodes = [];
32238    this.selMap = {};
32239    this.addEvents({
32240        /**
32241         * @event selectionchange
32242         * Fires when the selected nodes change
32243         * @param {MultiSelectionModel} this
32244         * @param {Array} nodes Array of the selected nodes
32245         */
32246        "selectionchange" : true
32247    });
32248    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32249    
32250 };
32251
32252 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32253     init : function(tree){
32254         this.tree = tree;
32255         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32256         tree.on("click", this.onNodeClick, this);
32257     },
32258     
32259     onNodeClick : function(node, e){
32260         this.select(node, e, e.ctrlKey);
32261     },
32262     
32263     /**
32264      * Select a node.
32265      * @param {TreeNode} node The node to select
32266      * @param {EventObject} e (optional) An event associated with the selection
32267      * @param {Boolean} keepExisting True to retain existing selections
32268      * @return {TreeNode} The selected node
32269      */
32270     select : function(node, e, keepExisting){
32271         if(keepExisting !== true){
32272             this.clearSelections(true);
32273         }
32274         if(this.isSelected(node)){
32275             this.lastSelNode = node;
32276             return node;
32277         }
32278         this.selNodes.push(node);
32279         this.selMap[node.id] = node;
32280         this.lastSelNode = node;
32281         node.ui.onSelectedChange(true);
32282         this.fireEvent("selectionchange", this, this.selNodes);
32283         return node;
32284     },
32285     
32286     /**
32287      * Deselect a node.
32288      * @param {TreeNode} node The node to unselect
32289      */
32290     unselect : function(node){
32291         if(this.selMap[node.id]){
32292             node.ui.onSelectedChange(false);
32293             var sn = this.selNodes;
32294             var index = -1;
32295             if(sn.indexOf){
32296                 index = sn.indexOf(node);
32297             }else{
32298                 for(var i = 0, len = sn.length; i < len; i++){
32299                     if(sn[i] == node){
32300                         index = i;
32301                         break;
32302                     }
32303                 }
32304             }
32305             if(index != -1){
32306                 this.selNodes.splice(index, 1);
32307             }
32308             delete this.selMap[node.id];
32309             this.fireEvent("selectionchange", this, this.selNodes);
32310         }
32311     },
32312     
32313     /**
32314      * Clear all selections
32315      */
32316     clearSelections : function(suppressEvent){
32317         var sn = this.selNodes;
32318         if(sn.length > 0){
32319             for(var i = 0, len = sn.length; i < len; i++){
32320                 sn[i].ui.onSelectedChange(false);
32321             }
32322             this.selNodes = [];
32323             this.selMap = {};
32324             if(suppressEvent !== true){
32325                 this.fireEvent("selectionchange", this, this.selNodes);
32326             }
32327         }
32328     },
32329     
32330     /**
32331      * Returns true if the node is selected
32332      * @param {TreeNode} node The node to check
32333      * @return {Boolean}
32334      */
32335     isSelected : function(node){
32336         return this.selMap[node.id] ? true : false;  
32337     },
32338     
32339     /**
32340      * Returns an array of the selected nodes
32341      * @return {Array}
32342      */
32343     getSelectedNodes : function(){
32344         return this.selNodes;    
32345     },
32346
32347     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32348
32349     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32350
32351     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32352 });/*
32353  * Based on:
32354  * Ext JS Library 1.1.1
32355  * Copyright(c) 2006-2007, Ext JS, LLC.
32356  *
32357  * Originally Released Under LGPL - original licence link has changed is not relivant.
32358  *
32359  * Fork - LGPL
32360  * <script type="text/javascript">
32361  */
32362  
32363 /**
32364  * @class Roo.tree.TreeNode
32365  * @extends Roo.data.Node
32366  * @cfg {String} text The text for this node
32367  * @cfg {Boolean} expanded true to start the node expanded
32368  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32369  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32370  * @cfg {Boolean} disabled true to start the node disabled
32371  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32372  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32373  * @cfg {String} cls A css class to be added to the node
32374  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32375  * @cfg {String} href URL of the link used for the node (defaults to #)
32376  * @cfg {String} hrefTarget target frame for the link
32377  * @cfg {String} qtip An Ext QuickTip for the node
32378  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32379  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32380  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32381  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32382  * (defaults to undefined with no checkbox rendered)
32383  * @constructor
32384  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32385  */
32386 Roo.tree.TreeNode = function(attributes){
32387     attributes = attributes || {};
32388     if(typeof attributes == "string"){
32389         attributes = {text: attributes};
32390     }
32391     this.childrenRendered = false;
32392     this.rendered = false;
32393     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32394     this.expanded = attributes.expanded === true;
32395     this.isTarget = attributes.isTarget !== false;
32396     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32397     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32398
32399     /**
32400      * Read-only. The text for this node. To change it use setText().
32401      * @type String
32402      */
32403     this.text = attributes.text;
32404     /**
32405      * True if this node is disabled.
32406      * @type Boolean
32407      */
32408     this.disabled = attributes.disabled === true;
32409
32410     this.addEvents({
32411         /**
32412         * @event textchange
32413         * Fires when the text for this node is changed
32414         * @param {Node} this This node
32415         * @param {String} text The new text
32416         * @param {String} oldText The old text
32417         */
32418         "textchange" : true,
32419         /**
32420         * @event beforeexpand
32421         * Fires before this node is expanded, return false to cancel.
32422         * @param {Node} this This node
32423         * @param {Boolean} deep
32424         * @param {Boolean} anim
32425         */
32426         "beforeexpand" : true,
32427         /**
32428         * @event beforecollapse
32429         * Fires before this node is collapsed, return false to cancel.
32430         * @param {Node} this This node
32431         * @param {Boolean} deep
32432         * @param {Boolean} anim
32433         */
32434         "beforecollapse" : true,
32435         /**
32436         * @event expand
32437         * Fires when this node is expanded
32438         * @param {Node} this This node
32439         */
32440         "expand" : true,
32441         /**
32442         * @event disabledchange
32443         * Fires when the disabled status of this node changes
32444         * @param {Node} this This node
32445         * @param {Boolean} disabled
32446         */
32447         "disabledchange" : true,
32448         /**
32449         * @event collapse
32450         * Fires when this node is collapsed
32451         * @param {Node} this This node
32452         */
32453         "collapse" : true,
32454         /**
32455         * @event beforeclick
32456         * Fires before click processing. Return false to cancel the default action.
32457         * @param {Node} this This node
32458         * @param {Roo.EventObject} e The event object
32459         */
32460         "beforeclick":true,
32461         /**
32462         * @event checkchange
32463         * Fires when a node with a checkbox's checked property changes
32464         * @param {Node} this This node
32465         * @param {Boolean} checked
32466         */
32467         "checkchange":true,
32468         /**
32469         * @event click
32470         * Fires when this node is clicked
32471         * @param {Node} this This node
32472         * @param {Roo.EventObject} e The event object
32473         */
32474         "click":true,
32475         /**
32476         * @event dblclick
32477         * Fires when this node is double clicked
32478         * @param {Node} this This node
32479         * @param {Roo.EventObject} e The event object
32480         */
32481         "dblclick":true,
32482         /**
32483         * @event contextmenu
32484         * Fires when this node is right clicked
32485         * @param {Node} this This node
32486         * @param {Roo.EventObject} e The event object
32487         */
32488         "contextmenu":true,
32489         /**
32490         * @event beforechildrenrendered
32491         * Fires right before the child nodes for this node are rendered
32492         * @param {Node} this This node
32493         */
32494         "beforechildrenrendered":true
32495     });
32496
32497     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
32498
32499     /**
32500      * Read-only. The UI for this node
32501      * @type TreeNodeUI
32502      */
32503     this.ui = new uiClass(this);
32504     
32505     // finally support items[]
32506     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
32507         return;
32508     }
32509     
32510     
32511     Roo.each(this.attributes.items, function(c) {
32512         this.appendChild(Roo.factory(c,Roo.Tree));
32513     }, this);
32514     delete this.attributes.items;
32515     
32516     
32517     
32518 };
32519 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
32520     preventHScroll: true,
32521     /**
32522      * Returns true if this node is expanded
32523      * @return {Boolean}
32524      */
32525     isExpanded : function(){
32526         return this.expanded;
32527     },
32528
32529     /**
32530      * Returns the UI object for this node
32531      * @return {TreeNodeUI}
32532      */
32533     getUI : function(){
32534         return this.ui;
32535     },
32536
32537     // private override
32538     setFirstChild : function(node){
32539         var of = this.firstChild;
32540         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
32541         if(this.childrenRendered && of && node != of){
32542             of.renderIndent(true, true);
32543         }
32544         if(this.rendered){
32545             this.renderIndent(true, true);
32546         }
32547     },
32548
32549     // private override
32550     setLastChild : function(node){
32551         var ol = this.lastChild;
32552         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
32553         if(this.childrenRendered && ol && node != ol){
32554             ol.renderIndent(true, true);
32555         }
32556         if(this.rendered){
32557             this.renderIndent(true, true);
32558         }
32559     },
32560
32561     // these methods are overridden to provide lazy rendering support
32562     // private override
32563     appendChild : function()
32564     {
32565         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32566         if(node && this.childrenRendered){
32567             node.render();
32568         }
32569         this.ui.updateExpandIcon();
32570         return node;
32571     },
32572
32573     // private override
32574     removeChild : function(node){
32575         this.ownerTree.getSelectionModel().unselect(node);
32576         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32577         // if it's been rendered remove dom node
32578         if(this.childrenRendered){
32579             node.ui.remove();
32580         }
32581         if(this.childNodes.length < 1){
32582             this.collapse(false, false);
32583         }else{
32584             this.ui.updateExpandIcon();
32585         }
32586         if(!this.firstChild) {
32587             this.childrenRendered = false;
32588         }
32589         return node;
32590     },
32591
32592     // private override
32593     insertBefore : function(node, refNode){
32594         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32595         if(newNode && refNode && this.childrenRendered){
32596             node.render();
32597         }
32598         this.ui.updateExpandIcon();
32599         return newNode;
32600     },
32601
32602     /**
32603      * Sets the text for this node
32604      * @param {String} text
32605      */
32606     setText : function(text){
32607         var oldText = this.text;
32608         this.text = text;
32609         this.attributes.text = text;
32610         if(this.rendered){ // event without subscribing
32611             this.ui.onTextChange(this, text, oldText);
32612         }
32613         this.fireEvent("textchange", this, text, oldText);
32614     },
32615
32616     /**
32617      * Triggers selection of this node
32618      */
32619     select : function(){
32620         this.getOwnerTree().getSelectionModel().select(this);
32621     },
32622
32623     /**
32624      * Triggers deselection of this node
32625      */
32626     unselect : function(){
32627         this.getOwnerTree().getSelectionModel().unselect(this);
32628     },
32629
32630     /**
32631      * Returns true if this node is selected
32632      * @return {Boolean}
32633      */
32634     isSelected : function(){
32635         return this.getOwnerTree().getSelectionModel().isSelected(this);
32636     },
32637
32638     /**
32639      * Expand this node.
32640      * @param {Boolean} deep (optional) True to expand all children as well
32641      * @param {Boolean} anim (optional) false to cancel the default animation
32642      * @param {Function} callback (optional) A callback to be called when
32643      * expanding this node completes (does not wait for deep expand to complete).
32644      * Called with 1 parameter, this node.
32645      */
32646     expand : function(deep, anim, callback){
32647         if(!this.expanded){
32648             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32649                 return;
32650             }
32651             if(!this.childrenRendered){
32652                 this.renderChildren();
32653             }
32654             this.expanded = true;
32655             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32656                 this.ui.animExpand(function(){
32657                     this.fireEvent("expand", this);
32658                     if(typeof callback == "function"){
32659                         callback(this);
32660                     }
32661                     if(deep === true){
32662                         this.expandChildNodes(true);
32663                     }
32664                 }.createDelegate(this));
32665                 return;
32666             }else{
32667                 this.ui.expand();
32668                 this.fireEvent("expand", this);
32669                 if(typeof callback == "function"){
32670                     callback(this);
32671                 }
32672             }
32673         }else{
32674            if(typeof callback == "function"){
32675                callback(this);
32676            }
32677         }
32678         if(deep === true){
32679             this.expandChildNodes(true);
32680         }
32681     },
32682
32683     isHiddenRoot : function(){
32684         return this.isRoot && !this.getOwnerTree().rootVisible;
32685     },
32686
32687     /**
32688      * Collapse this node.
32689      * @param {Boolean} deep (optional) True to collapse all children as well
32690      * @param {Boolean} anim (optional) false to cancel the default animation
32691      */
32692     collapse : function(deep, anim){
32693         if(this.expanded && !this.isHiddenRoot()){
32694             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32695                 return;
32696             }
32697             this.expanded = false;
32698             if((this.getOwnerTree().animate && anim !== false) || anim){
32699                 this.ui.animCollapse(function(){
32700                     this.fireEvent("collapse", this);
32701                     if(deep === true){
32702                         this.collapseChildNodes(true);
32703                     }
32704                 }.createDelegate(this));
32705                 return;
32706             }else{
32707                 this.ui.collapse();
32708                 this.fireEvent("collapse", this);
32709             }
32710         }
32711         if(deep === true){
32712             var cs = this.childNodes;
32713             for(var i = 0, len = cs.length; i < len; i++) {
32714                 cs[i].collapse(true, false);
32715             }
32716         }
32717     },
32718
32719     // private
32720     delayedExpand : function(delay){
32721         if(!this.expandProcId){
32722             this.expandProcId = this.expand.defer(delay, this);
32723         }
32724     },
32725
32726     // private
32727     cancelExpand : function(){
32728         if(this.expandProcId){
32729             clearTimeout(this.expandProcId);
32730         }
32731         this.expandProcId = false;
32732     },
32733
32734     /**
32735      * Toggles expanded/collapsed state of the node
32736      */
32737     toggle : function(){
32738         if(this.expanded){
32739             this.collapse();
32740         }else{
32741             this.expand();
32742         }
32743     },
32744
32745     /**
32746      * Ensures all parent nodes are expanded
32747      */
32748     ensureVisible : function(callback){
32749         var tree = this.getOwnerTree();
32750         tree.expandPath(this.parentNode.getPath(), false, function(){
32751             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32752             Roo.callback(callback);
32753         }.createDelegate(this));
32754     },
32755
32756     /**
32757      * Expand all child nodes
32758      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32759      */
32760     expandChildNodes : function(deep){
32761         var cs = this.childNodes;
32762         for(var i = 0, len = cs.length; i < len; i++) {
32763                 cs[i].expand(deep);
32764         }
32765     },
32766
32767     /**
32768      * Collapse all child nodes
32769      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32770      */
32771     collapseChildNodes : function(deep){
32772         var cs = this.childNodes;
32773         for(var i = 0, len = cs.length; i < len; i++) {
32774                 cs[i].collapse(deep);
32775         }
32776     },
32777
32778     /**
32779      * Disables this node
32780      */
32781     disable : function(){
32782         this.disabled = true;
32783         this.unselect();
32784         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32785             this.ui.onDisableChange(this, true);
32786         }
32787         this.fireEvent("disabledchange", this, true);
32788     },
32789
32790     /**
32791      * Enables this node
32792      */
32793     enable : function(){
32794         this.disabled = false;
32795         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32796             this.ui.onDisableChange(this, false);
32797         }
32798         this.fireEvent("disabledchange", this, false);
32799     },
32800
32801     // private
32802     renderChildren : function(suppressEvent){
32803         if(suppressEvent !== false){
32804             this.fireEvent("beforechildrenrendered", this);
32805         }
32806         var cs = this.childNodes;
32807         for(var i = 0, len = cs.length; i < len; i++){
32808             cs[i].render(true);
32809         }
32810         this.childrenRendered = true;
32811     },
32812
32813     // private
32814     sort : function(fn, scope){
32815         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32816         if(this.childrenRendered){
32817             var cs = this.childNodes;
32818             for(var i = 0, len = cs.length; i < len; i++){
32819                 cs[i].render(true);
32820             }
32821         }
32822     },
32823
32824     // private
32825     render : function(bulkRender){
32826         this.ui.render(bulkRender);
32827         if(!this.rendered){
32828             this.rendered = true;
32829             if(this.expanded){
32830                 this.expanded = false;
32831                 this.expand(false, false);
32832             }
32833         }
32834     },
32835
32836     // private
32837     renderIndent : function(deep, refresh){
32838         if(refresh){
32839             this.ui.childIndent = null;
32840         }
32841         this.ui.renderIndent();
32842         if(deep === true && this.childrenRendered){
32843             var cs = this.childNodes;
32844             for(var i = 0, len = cs.length; i < len; i++){
32845                 cs[i].renderIndent(true, refresh);
32846             }
32847         }
32848     }
32849 });/*
32850  * Based on:
32851  * Ext JS Library 1.1.1
32852  * Copyright(c) 2006-2007, Ext JS, LLC.
32853  *
32854  * Originally Released Under LGPL - original licence link has changed is not relivant.
32855  *
32856  * Fork - LGPL
32857  * <script type="text/javascript">
32858  */
32859  
32860 /**
32861  * @class Roo.tree.AsyncTreeNode
32862  * @extends Roo.tree.TreeNode
32863  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32864  * @constructor
32865  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32866  */
32867  Roo.tree.AsyncTreeNode = function(config){
32868     this.loaded = false;
32869     this.loading = false;
32870     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32871     /**
32872     * @event beforeload
32873     * Fires before this node is loaded, return false to cancel
32874     * @param {Node} this This node
32875     */
32876     this.addEvents({'beforeload':true, 'load': true});
32877     /**
32878     * @event load
32879     * Fires when this node is loaded
32880     * @param {Node} this This node
32881     */
32882     /**
32883      * The loader used by this node (defaults to using the tree's defined loader)
32884      * @type TreeLoader
32885      * @property loader
32886      */
32887 };
32888 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32889     expand : function(deep, anim, callback){
32890         if(this.loading){ // if an async load is already running, waiting til it's done
32891             var timer;
32892             var f = function(){
32893                 if(!this.loading){ // done loading
32894                     clearInterval(timer);
32895                     this.expand(deep, anim, callback);
32896                 }
32897             }.createDelegate(this);
32898             timer = setInterval(f, 200);
32899             return;
32900         }
32901         if(!this.loaded){
32902             if(this.fireEvent("beforeload", this) === false){
32903                 return;
32904             }
32905             this.loading = true;
32906             this.ui.beforeLoad(this);
32907             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32908             if(loader){
32909                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32910                 return;
32911             }
32912         }
32913         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32914     },
32915     
32916     /**
32917      * Returns true if this node is currently loading
32918      * @return {Boolean}
32919      */
32920     isLoading : function(){
32921         return this.loading;  
32922     },
32923     
32924     loadComplete : function(deep, anim, callback){
32925         this.loading = false;
32926         this.loaded = true;
32927         this.ui.afterLoad(this);
32928         this.fireEvent("load", this);
32929         this.expand(deep, anim, callback);
32930     },
32931     
32932     /**
32933      * Returns true if this node has been loaded
32934      * @return {Boolean}
32935      */
32936     isLoaded : function(){
32937         return this.loaded;
32938     },
32939     
32940     hasChildNodes : function(){
32941         if(!this.isLeaf() && !this.loaded){
32942             return true;
32943         }else{
32944             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32945         }
32946     },
32947
32948     /**
32949      * Trigger a reload for this node
32950      * @param {Function} callback
32951      */
32952     reload : function(callback){
32953         this.collapse(false, false);
32954         while(this.firstChild){
32955             this.removeChild(this.firstChild);
32956         }
32957         this.childrenRendered = false;
32958         this.loaded = false;
32959         if(this.isHiddenRoot()){
32960             this.expanded = false;
32961         }
32962         this.expand(false, false, callback);
32963     }
32964 });/*
32965  * Based on:
32966  * Ext JS Library 1.1.1
32967  * Copyright(c) 2006-2007, Ext JS, LLC.
32968  *
32969  * Originally Released Under LGPL - original licence link has changed is not relivant.
32970  *
32971  * Fork - LGPL
32972  * <script type="text/javascript">
32973  */
32974  
32975 /**
32976  * @class Roo.tree.TreeNodeUI
32977  * @constructor
32978  * @param {Object} node The node to render
32979  * The TreeNode UI implementation is separate from the
32980  * tree implementation. Unless you are customizing the tree UI,
32981  * you should never have to use this directly.
32982  */
32983 Roo.tree.TreeNodeUI = function(node){
32984     this.node = node;
32985     this.rendered = false;
32986     this.animating = false;
32987     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32988 };
32989
32990 Roo.tree.TreeNodeUI.prototype = {
32991     removeChild : function(node){
32992         if(this.rendered){
32993             this.ctNode.removeChild(node.ui.getEl());
32994         }
32995     },
32996
32997     beforeLoad : function(){
32998          this.addClass("x-tree-node-loading");
32999     },
33000
33001     afterLoad : function(){
33002          this.removeClass("x-tree-node-loading");
33003     },
33004
33005     onTextChange : function(node, text, oldText){
33006         if(this.rendered){
33007             this.textNode.innerHTML = text;
33008         }
33009     },
33010
33011     onDisableChange : function(node, state){
33012         this.disabled = state;
33013         if(state){
33014             this.addClass("x-tree-node-disabled");
33015         }else{
33016             this.removeClass("x-tree-node-disabled");
33017         }
33018     },
33019
33020     onSelectedChange : function(state){
33021         if(state){
33022             this.focus();
33023             this.addClass("x-tree-selected");
33024         }else{
33025             //this.blur();
33026             this.removeClass("x-tree-selected");
33027         }
33028     },
33029
33030     onMove : function(tree, node, oldParent, newParent, index, refNode){
33031         this.childIndent = null;
33032         if(this.rendered){
33033             var targetNode = newParent.ui.getContainer();
33034             if(!targetNode){//target not rendered
33035                 this.holder = document.createElement("div");
33036                 this.holder.appendChild(this.wrap);
33037                 return;
33038             }
33039             var insertBefore = refNode ? refNode.ui.getEl() : null;
33040             if(insertBefore){
33041                 targetNode.insertBefore(this.wrap, insertBefore);
33042             }else{
33043                 targetNode.appendChild(this.wrap);
33044             }
33045             this.node.renderIndent(true);
33046         }
33047     },
33048
33049     addClass : function(cls){
33050         if(this.elNode){
33051             Roo.fly(this.elNode).addClass(cls);
33052         }
33053     },
33054
33055     removeClass : function(cls){
33056         if(this.elNode){
33057             Roo.fly(this.elNode).removeClass(cls);
33058         }
33059     },
33060
33061     remove : function(){
33062         if(this.rendered){
33063             this.holder = document.createElement("div");
33064             this.holder.appendChild(this.wrap);
33065         }
33066     },
33067
33068     fireEvent : function(){
33069         return this.node.fireEvent.apply(this.node, arguments);
33070     },
33071
33072     initEvents : function(){
33073         this.node.on("move", this.onMove, this);
33074         var E = Roo.EventManager;
33075         var a = this.anchor;
33076
33077         var el = Roo.fly(a, '_treeui');
33078
33079         if(Roo.isOpera){ // opera render bug ignores the CSS
33080             el.setStyle("text-decoration", "none");
33081         }
33082
33083         el.on("click", this.onClick, this);
33084         el.on("dblclick", this.onDblClick, this);
33085
33086         if(this.checkbox){
33087             Roo.EventManager.on(this.checkbox,
33088                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33089         }
33090
33091         el.on("contextmenu", this.onContextMenu, this);
33092
33093         var icon = Roo.fly(this.iconNode);
33094         icon.on("click", this.onClick, this);
33095         icon.on("dblclick", this.onDblClick, this);
33096         icon.on("contextmenu", this.onContextMenu, this);
33097         E.on(this.ecNode, "click", this.ecClick, this, true);
33098
33099         if(this.node.disabled){
33100             this.addClass("x-tree-node-disabled");
33101         }
33102         if(this.node.hidden){
33103             this.addClass("x-tree-node-disabled");
33104         }
33105         var ot = this.node.getOwnerTree();
33106         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33107         if(dd && (!this.node.isRoot || ot.rootVisible)){
33108             Roo.dd.Registry.register(this.elNode, {
33109                 node: this.node,
33110                 handles: this.getDDHandles(),
33111                 isHandle: false
33112             });
33113         }
33114     },
33115
33116     getDDHandles : function(){
33117         return [this.iconNode, this.textNode];
33118     },
33119
33120     hide : function(){
33121         if(this.rendered){
33122             this.wrap.style.display = "none";
33123         }
33124     },
33125
33126     show : function(){
33127         if(this.rendered){
33128             this.wrap.style.display = "";
33129         }
33130     },
33131
33132     onContextMenu : function(e){
33133         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33134             e.preventDefault();
33135             this.focus();
33136             this.fireEvent("contextmenu", this.node, e);
33137         }
33138     },
33139
33140     onClick : function(e){
33141         if(this.dropping){
33142             e.stopEvent();
33143             return;
33144         }
33145         if(this.fireEvent("beforeclick", this.node, e) !== false){
33146             if(!this.disabled && this.node.attributes.href){
33147                 this.fireEvent("click", this.node, e);
33148                 return;
33149             }
33150             e.preventDefault();
33151             if(this.disabled){
33152                 return;
33153             }
33154
33155             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33156                 this.node.toggle();
33157             }
33158
33159             this.fireEvent("click", this.node, e);
33160         }else{
33161             e.stopEvent();
33162         }
33163     },
33164
33165     onDblClick : function(e){
33166         e.preventDefault();
33167         if(this.disabled){
33168             return;
33169         }
33170         if(this.checkbox){
33171             this.toggleCheck();
33172         }
33173         if(!this.animating && this.node.hasChildNodes()){
33174             this.node.toggle();
33175         }
33176         this.fireEvent("dblclick", this.node, e);
33177     },
33178
33179     onCheckChange : function(){
33180         var checked = this.checkbox.checked;
33181         this.node.attributes.checked = checked;
33182         this.fireEvent('checkchange', this.node, checked);
33183     },
33184
33185     ecClick : function(e){
33186         if(!this.animating && this.node.hasChildNodes()){
33187             this.node.toggle();
33188         }
33189     },
33190
33191     startDrop : function(){
33192         this.dropping = true;
33193     },
33194
33195     // delayed drop so the click event doesn't get fired on a drop
33196     endDrop : function(){
33197        setTimeout(function(){
33198            this.dropping = false;
33199        }.createDelegate(this), 50);
33200     },
33201
33202     expand : function(){
33203         this.updateExpandIcon();
33204         this.ctNode.style.display = "";
33205     },
33206
33207     focus : function(){
33208         if(!this.node.preventHScroll){
33209             try{this.anchor.focus();
33210             }catch(e){}
33211         }else if(!Roo.isIE){
33212             try{
33213                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33214                 var l = noscroll.scrollLeft;
33215                 this.anchor.focus();
33216                 noscroll.scrollLeft = l;
33217             }catch(e){}
33218         }
33219     },
33220
33221     toggleCheck : function(value){
33222         var cb = this.checkbox;
33223         if(cb){
33224             cb.checked = (value === undefined ? !cb.checked : value);
33225         }
33226     },
33227
33228     blur : function(){
33229         try{
33230             this.anchor.blur();
33231         }catch(e){}
33232     },
33233
33234     animExpand : function(callback){
33235         var ct = Roo.get(this.ctNode);
33236         ct.stopFx();
33237         if(!this.node.hasChildNodes()){
33238             this.updateExpandIcon();
33239             this.ctNode.style.display = "";
33240             Roo.callback(callback);
33241             return;
33242         }
33243         this.animating = true;
33244         this.updateExpandIcon();
33245
33246         ct.slideIn('t', {
33247            callback : function(){
33248                this.animating = false;
33249                Roo.callback(callback);
33250             },
33251             scope: this,
33252             duration: this.node.ownerTree.duration || .25
33253         });
33254     },
33255
33256     highlight : function(){
33257         var tree = this.node.getOwnerTree();
33258         Roo.fly(this.wrap).highlight(
33259             tree.hlColor || "C3DAF9",
33260             {endColor: tree.hlBaseColor}
33261         );
33262     },
33263
33264     collapse : function(){
33265         this.updateExpandIcon();
33266         this.ctNode.style.display = "none";
33267     },
33268
33269     animCollapse : function(callback){
33270         var ct = Roo.get(this.ctNode);
33271         ct.enableDisplayMode('block');
33272         ct.stopFx();
33273
33274         this.animating = true;
33275         this.updateExpandIcon();
33276
33277         ct.slideOut('t', {
33278             callback : function(){
33279                this.animating = false;
33280                Roo.callback(callback);
33281             },
33282             scope: this,
33283             duration: this.node.ownerTree.duration || .25
33284         });
33285     },
33286
33287     getContainer : function(){
33288         return this.ctNode;
33289     },
33290
33291     getEl : function(){
33292         return this.wrap;
33293     },
33294
33295     appendDDGhost : function(ghostNode){
33296         ghostNode.appendChild(this.elNode.cloneNode(true));
33297     },
33298
33299     getDDRepairXY : function(){
33300         return Roo.lib.Dom.getXY(this.iconNode);
33301     },
33302
33303     onRender : function(){
33304         this.render();
33305     },
33306
33307     render : function(bulkRender){
33308         var n = this.node, a = n.attributes;
33309         var targetNode = n.parentNode ?
33310               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33311
33312         if(!this.rendered){
33313             this.rendered = true;
33314
33315             this.renderElements(n, a, targetNode, bulkRender);
33316
33317             if(a.qtip){
33318                if(this.textNode.setAttributeNS){
33319                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33320                    if(a.qtipTitle){
33321                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33322                    }
33323                }else{
33324                    this.textNode.setAttribute("ext:qtip", a.qtip);
33325                    if(a.qtipTitle){
33326                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33327                    }
33328                }
33329             }else if(a.qtipCfg){
33330                 a.qtipCfg.target = Roo.id(this.textNode);
33331                 Roo.QuickTips.register(a.qtipCfg);
33332             }
33333             this.initEvents();
33334             if(!this.node.expanded){
33335                 this.updateExpandIcon();
33336             }
33337         }else{
33338             if(bulkRender === true) {
33339                 targetNode.appendChild(this.wrap);
33340             }
33341         }
33342     },
33343
33344     renderElements : function(n, a, targetNode, bulkRender)
33345     {
33346         // add some indent caching, this helps performance when rendering a large tree
33347         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33348         var t = n.getOwnerTree();
33349         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33350         if (typeof(n.attributes.html) != 'undefined') {
33351             txt = n.attributes.html;
33352         }
33353         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33354         var cb = typeof a.checked == 'boolean';
33355         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33356         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33357             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33358             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33359             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33360             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33361             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33362              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33363                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33364             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33365             "</li>"];
33366
33367         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33368             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33369                                 n.nextSibling.ui.getEl(), buf.join(""));
33370         }else{
33371             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33372         }
33373
33374         this.elNode = this.wrap.childNodes[0];
33375         this.ctNode = this.wrap.childNodes[1];
33376         var cs = this.elNode.childNodes;
33377         this.indentNode = cs[0];
33378         this.ecNode = cs[1];
33379         this.iconNode = cs[2];
33380         var index = 3;
33381         if(cb){
33382             this.checkbox = cs[3];
33383             index++;
33384         }
33385         this.anchor = cs[index];
33386         this.textNode = cs[index].firstChild;
33387     },
33388
33389     getAnchor : function(){
33390         return this.anchor;
33391     },
33392
33393     getTextEl : function(){
33394         return this.textNode;
33395     },
33396
33397     getIconEl : function(){
33398         return this.iconNode;
33399     },
33400
33401     isChecked : function(){
33402         return this.checkbox ? this.checkbox.checked : false;
33403     },
33404
33405     updateExpandIcon : function(){
33406         if(this.rendered){
33407             var n = this.node, c1, c2;
33408             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33409             var hasChild = n.hasChildNodes();
33410             if(hasChild){
33411                 if(n.expanded){
33412                     cls += "-minus";
33413                     c1 = "x-tree-node-collapsed";
33414                     c2 = "x-tree-node-expanded";
33415                 }else{
33416                     cls += "-plus";
33417                     c1 = "x-tree-node-expanded";
33418                     c2 = "x-tree-node-collapsed";
33419                 }
33420                 if(this.wasLeaf){
33421                     this.removeClass("x-tree-node-leaf");
33422                     this.wasLeaf = false;
33423                 }
33424                 if(this.c1 != c1 || this.c2 != c2){
33425                     Roo.fly(this.elNode).replaceClass(c1, c2);
33426                     this.c1 = c1; this.c2 = c2;
33427                 }
33428             }else{
33429                 // this changes non-leafs into leafs if they have no children.
33430                 // it's not very rational behaviour..
33431                 
33432                 if(!this.wasLeaf && this.node.leaf){
33433                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
33434                     delete this.c1;
33435                     delete this.c2;
33436                     this.wasLeaf = true;
33437                 }
33438             }
33439             var ecc = "x-tree-ec-icon "+cls;
33440             if(this.ecc != ecc){
33441                 this.ecNode.className = ecc;
33442                 this.ecc = ecc;
33443             }
33444         }
33445     },
33446
33447     getChildIndent : function(){
33448         if(!this.childIndent){
33449             var buf = [];
33450             var p = this.node;
33451             while(p){
33452                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
33453                     if(!p.isLast()) {
33454                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
33455                     } else {
33456                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
33457                     }
33458                 }
33459                 p = p.parentNode;
33460             }
33461             this.childIndent = buf.join("");
33462         }
33463         return this.childIndent;
33464     },
33465
33466     renderIndent : function(){
33467         if(this.rendered){
33468             var indent = "";
33469             var p = this.node.parentNode;
33470             if(p){
33471                 indent = p.ui.getChildIndent();
33472             }
33473             if(this.indentMarkup != indent){ // don't rerender if not required
33474                 this.indentNode.innerHTML = indent;
33475                 this.indentMarkup = indent;
33476             }
33477             this.updateExpandIcon();
33478         }
33479     }
33480 };
33481
33482 Roo.tree.RootTreeNodeUI = function(){
33483     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
33484 };
33485 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
33486     render : function(){
33487         if(!this.rendered){
33488             var targetNode = this.node.ownerTree.innerCt.dom;
33489             this.node.expanded = true;
33490             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
33491             this.wrap = this.ctNode = targetNode.firstChild;
33492         }
33493     },
33494     collapse : function(){
33495     },
33496     expand : function(){
33497     }
33498 });/*
33499  * Based on:
33500  * Ext JS Library 1.1.1
33501  * Copyright(c) 2006-2007, Ext JS, LLC.
33502  *
33503  * Originally Released Under LGPL - original licence link has changed is not relivant.
33504  *
33505  * Fork - LGPL
33506  * <script type="text/javascript">
33507  */
33508 /**
33509  * @class Roo.tree.TreeLoader
33510  * @extends Roo.util.Observable
33511  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
33512  * nodes from a specified URL. The response must be a javascript Array definition
33513  * who's elements are node definition objects. eg:
33514  * <pre><code>
33515 {  success : true,
33516    data :      [
33517    
33518     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
33519     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
33520     ]
33521 }
33522
33523
33524 </code></pre>
33525  * <br><br>
33526  * The old style respose with just an array is still supported, but not recommended.
33527  * <br><br>
33528  *
33529  * A server request is sent, and child nodes are loaded only when a node is expanded.
33530  * The loading node's id is passed to the server under the parameter name "node" to
33531  * enable the server to produce the correct child nodes.
33532  * <br><br>
33533  * To pass extra parameters, an event handler may be attached to the "beforeload"
33534  * event, and the parameters specified in the TreeLoader's baseParams property:
33535  * <pre><code>
33536     myTreeLoader.on("beforeload", function(treeLoader, node) {
33537         this.baseParams.category = node.attributes.category;
33538     }, this);
33539 </code></pre><
33540  * This would pass an HTTP parameter called "category" to the server containing
33541  * the value of the Node's "category" attribute.
33542  * @constructor
33543  * Creates a new Treeloader.
33544  * @param {Object} config A config object containing config properties.
33545  */
33546 Roo.tree.TreeLoader = function(config){
33547     this.baseParams = {};
33548     this.requestMethod = "POST";
33549     Roo.apply(this, config);
33550
33551     this.addEvents({
33552     
33553         /**
33554          * @event beforeload
33555          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
33556          * @param {Object} This TreeLoader object.
33557          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33558          * @param {Object} callback The callback function specified in the {@link #load} call.
33559          */
33560         beforeload : true,
33561         /**
33562          * @event load
33563          * Fires when the node has been successfuly loaded.
33564          * @param {Object} This TreeLoader object.
33565          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33566          * @param {Object} response The response object containing the data from the server.
33567          */
33568         load : true,
33569         /**
33570          * @event loadexception
33571          * Fires if the network request failed.
33572          * @param {Object} This TreeLoader object.
33573          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33574          * @param {Object} response The response object containing the data from the server.
33575          */
33576         loadexception : true,
33577         /**
33578          * @event create
33579          * Fires before a node is created, enabling you to return custom Node types 
33580          * @param {Object} This TreeLoader object.
33581          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33582          */
33583         create : true
33584     });
33585
33586     Roo.tree.TreeLoader.superclass.constructor.call(this);
33587 };
33588
33589 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33590     /**
33591     * @cfg {String} dataUrl The URL from which to request a Json string which
33592     * specifies an array of node definition object representing the child nodes
33593     * to be loaded.
33594     */
33595     /**
33596     * @cfg {String} requestMethod either GET or POST
33597     * defaults to POST (due to BC)
33598     * to be loaded.
33599     */
33600     /**
33601     * @cfg {Object} baseParams (optional) An object containing properties which
33602     * specify HTTP parameters to be passed to each request for child nodes.
33603     */
33604     /**
33605     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33606     * created by this loader. If the attributes sent by the server have an attribute in this object,
33607     * they take priority.
33608     */
33609     /**
33610     * @cfg {Object} uiProviders (optional) An object containing properties which
33611     * 
33612     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33613     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33614     * <i>uiProvider</i> attribute of a returned child node is a string rather
33615     * than a reference to a TreeNodeUI implementation, this that string value
33616     * is used as a property name in the uiProviders object. You can define the provider named
33617     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33618     */
33619     uiProviders : {},
33620
33621     /**
33622     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33623     * child nodes before loading.
33624     */
33625     clearOnLoad : true,
33626
33627     /**
33628     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33629     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33630     * Grid query { data : [ .....] }
33631     */
33632     
33633     root : false,
33634      /**
33635     * @cfg {String} queryParam (optional) 
33636     * Name of the query as it will be passed on the querystring (defaults to 'node')
33637     * eg. the request will be ?node=[id]
33638     */
33639     
33640     
33641     queryParam: false,
33642     
33643     /**
33644      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33645      * This is called automatically when a node is expanded, but may be used to reload
33646      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33647      * @param {Roo.tree.TreeNode} node
33648      * @param {Function} callback
33649      */
33650     load : function(node, callback){
33651         if(this.clearOnLoad){
33652             while(node.firstChild){
33653                 node.removeChild(node.firstChild);
33654             }
33655         }
33656         if(node.attributes.children){ // preloaded json children
33657             var cs = node.attributes.children;
33658             for(var i = 0, len = cs.length; i < len; i++){
33659                 node.appendChild(this.createNode(cs[i]));
33660             }
33661             if(typeof callback == "function"){
33662                 callback();
33663             }
33664         }else if(this.dataUrl){
33665             this.requestData(node, callback);
33666         }
33667     },
33668
33669     getParams: function(node){
33670         var buf = [], bp = this.baseParams;
33671         for(var key in bp){
33672             if(typeof bp[key] != "function"){
33673                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33674             }
33675         }
33676         var n = this.queryParam === false ? 'node' : this.queryParam;
33677         buf.push(n + "=", encodeURIComponent(node.id));
33678         return buf.join("");
33679     },
33680
33681     requestData : function(node, callback){
33682         if(this.fireEvent("beforeload", this, node, callback) !== false){
33683             this.transId = Roo.Ajax.request({
33684                 method:this.requestMethod,
33685                 url: this.dataUrl||this.url,
33686                 success: this.handleResponse,
33687                 failure: this.handleFailure,
33688                 scope: this,
33689                 argument: {callback: callback, node: node},
33690                 params: this.getParams(node)
33691             });
33692         }else{
33693             // if the load is cancelled, make sure we notify
33694             // the node that we are done
33695             if(typeof callback == "function"){
33696                 callback();
33697             }
33698         }
33699     },
33700
33701     isLoading : function(){
33702         return this.transId ? true : false;
33703     },
33704
33705     abort : function(){
33706         if(this.isLoading()){
33707             Roo.Ajax.abort(this.transId);
33708         }
33709     },
33710
33711     // private
33712     createNode : function(attr)
33713     {
33714         // apply baseAttrs, nice idea Corey!
33715         if(this.baseAttrs){
33716             Roo.applyIf(attr, this.baseAttrs);
33717         }
33718         if(this.applyLoader !== false){
33719             attr.loader = this;
33720         }
33721         // uiProvider = depreciated..
33722         
33723         if(typeof(attr.uiProvider) == 'string'){
33724            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33725                 /**  eval:var:attr */ eval(attr.uiProvider);
33726         }
33727         if(typeof(this.uiProviders['default']) != 'undefined') {
33728             attr.uiProvider = this.uiProviders['default'];
33729         }
33730         
33731         this.fireEvent('create', this, attr);
33732         
33733         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33734         return(attr.leaf ?
33735                         new Roo.tree.TreeNode(attr) :
33736                         new Roo.tree.AsyncTreeNode(attr));
33737     },
33738
33739     processResponse : function(response, node, callback)
33740     {
33741         var json = response.responseText;
33742         try {
33743             
33744             var o = Roo.decode(json);
33745             
33746             if (this.root === false && typeof(o.success) != undefined) {
33747                 this.root = 'data'; // the default behaviour for list like data..
33748                 }
33749                 
33750             if (this.root !== false &&  !o.success) {
33751                 // it's a failure condition.
33752                 var a = response.argument;
33753                 this.fireEvent("loadexception", this, a.node, response);
33754                 Roo.log("Load failed - should have a handler really");
33755                 return;
33756             }
33757             
33758             
33759             
33760             if (this.root !== false) {
33761                  o = o[this.root];
33762             }
33763             
33764             for(var i = 0, len = o.length; i < len; i++){
33765                 var n = this.createNode(o[i]);
33766                 if(n){
33767                     node.appendChild(n);
33768                 }
33769             }
33770             if(typeof callback == "function"){
33771                 callback(this, node);
33772             }
33773         }catch(e){
33774             this.handleFailure(response);
33775         }
33776     },
33777
33778     handleResponse : function(response){
33779         this.transId = false;
33780         var a = response.argument;
33781         this.processResponse(response, a.node, a.callback);
33782         this.fireEvent("load", this, a.node, response);
33783     },
33784
33785     handleFailure : function(response)
33786     {
33787         // should handle failure better..
33788         this.transId = false;
33789         var a = response.argument;
33790         this.fireEvent("loadexception", this, a.node, response);
33791         if(typeof a.callback == "function"){
33792             a.callback(this, a.node);
33793         }
33794     }
33795 });/*
33796  * Based on:
33797  * Ext JS Library 1.1.1
33798  * Copyright(c) 2006-2007, Ext JS, LLC.
33799  *
33800  * Originally Released Under LGPL - original licence link has changed is not relivant.
33801  *
33802  * Fork - LGPL
33803  * <script type="text/javascript">
33804  */
33805
33806 /**
33807 * @class Roo.tree.TreeFilter
33808 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33809 * @param {TreePanel} tree
33810 * @param {Object} config (optional)
33811  */
33812 Roo.tree.TreeFilter = function(tree, config){
33813     this.tree = tree;
33814     this.filtered = {};
33815     Roo.apply(this, config);
33816 };
33817
33818 Roo.tree.TreeFilter.prototype = {
33819     clearBlank:false,
33820     reverse:false,
33821     autoClear:false,
33822     remove:false,
33823
33824      /**
33825      * Filter the data by a specific attribute.
33826      * @param {String/RegExp} value Either string that the attribute value
33827      * should start with or a RegExp to test against the attribute
33828      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33829      * @param {TreeNode} startNode (optional) The node to start the filter at.
33830      */
33831     filter : function(value, attr, startNode){
33832         attr = attr || "text";
33833         var f;
33834         if(typeof value == "string"){
33835             var vlen = value.length;
33836             // auto clear empty filter
33837             if(vlen == 0 && this.clearBlank){
33838                 this.clear();
33839                 return;
33840             }
33841             value = value.toLowerCase();
33842             f = function(n){
33843                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33844             };
33845         }else if(value.exec){ // regex?
33846             f = function(n){
33847                 return value.test(n.attributes[attr]);
33848             };
33849         }else{
33850             throw 'Illegal filter type, must be string or regex';
33851         }
33852         this.filterBy(f, null, startNode);
33853         },
33854
33855     /**
33856      * Filter by a function. The passed function will be called with each
33857      * node in the tree (or from the startNode). If the function returns true, the node is kept
33858      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33859      * @param {Function} fn The filter function
33860      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33861      */
33862     filterBy : function(fn, scope, startNode){
33863         startNode = startNode || this.tree.root;
33864         if(this.autoClear){
33865             this.clear();
33866         }
33867         var af = this.filtered, rv = this.reverse;
33868         var f = function(n){
33869             if(n == startNode){
33870                 return true;
33871             }
33872             if(af[n.id]){
33873                 return false;
33874             }
33875             var m = fn.call(scope || n, n);
33876             if(!m || rv){
33877                 af[n.id] = n;
33878                 n.ui.hide();
33879                 return false;
33880             }
33881             return true;
33882         };
33883         startNode.cascade(f);
33884         if(this.remove){
33885            for(var id in af){
33886                if(typeof id != "function"){
33887                    var n = af[id];
33888                    if(n && n.parentNode){
33889                        n.parentNode.removeChild(n);
33890                    }
33891                }
33892            }
33893         }
33894     },
33895
33896     /**
33897      * Clears the current filter. Note: with the "remove" option
33898      * set a filter cannot be cleared.
33899      */
33900     clear : function(){
33901         var t = this.tree;
33902         var af = this.filtered;
33903         for(var id in af){
33904             if(typeof id != "function"){
33905                 var n = af[id];
33906                 if(n){
33907                     n.ui.show();
33908                 }
33909             }
33910         }
33911         this.filtered = {};
33912     }
33913 };
33914 /*
33915  * Based on:
33916  * Ext JS Library 1.1.1
33917  * Copyright(c) 2006-2007, Ext JS, LLC.
33918  *
33919  * Originally Released Under LGPL - original licence link has changed is not relivant.
33920  *
33921  * Fork - LGPL
33922  * <script type="text/javascript">
33923  */
33924  
33925
33926 /**
33927  * @class Roo.tree.TreeSorter
33928  * Provides sorting of nodes in a TreePanel
33929  * 
33930  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33931  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33932  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33933  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33934  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33935  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33936  * @constructor
33937  * @param {TreePanel} tree
33938  * @param {Object} config
33939  */
33940 Roo.tree.TreeSorter = function(tree, config){
33941     Roo.apply(this, config);
33942     tree.on("beforechildrenrendered", this.doSort, this);
33943     tree.on("append", this.updateSort, this);
33944     tree.on("insert", this.updateSort, this);
33945     
33946     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33947     var p = this.property || "text";
33948     var sortType = this.sortType;
33949     var fs = this.folderSort;
33950     var cs = this.caseSensitive === true;
33951     var leafAttr = this.leafAttr || 'leaf';
33952
33953     this.sortFn = function(n1, n2){
33954         if(fs){
33955             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33956                 return 1;
33957             }
33958             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33959                 return -1;
33960             }
33961         }
33962         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33963         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33964         if(v1 < v2){
33965                         return dsc ? +1 : -1;
33966                 }else if(v1 > v2){
33967                         return dsc ? -1 : +1;
33968         }else{
33969                 return 0;
33970         }
33971     };
33972 };
33973
33974 Roo.tree.TreeSorter.prototype = {
33975     doSort : function(node){
33976         node.sort(this.sortFn);
33977     },
33978     
33979     compareNodes : function(n1, n2){
33980         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33981     },
33982     
33983     updateSort : function(tree, node){
33984         if(node.childrenRendered){
33985             this.doSort.defer(1, this, [node]);
33986         }
33987     }
33988 };/*
33989  * Based on:
33990  * Ext JS Library 1.1.1
33991  * Copyright(c) 2006-2007, Ext JS, LLC.
33992  *
33993  * Originally Released Under LGPL - original licence link has changed is not relivant.
33994  *
33995  * Fork - LGPL
33996  * <script type="text/javascript">
33997  */
33998
33999 if(Roo.dd.DropZone){
34000     
34001 Roo.tree.TreeDropZone = function(tree, config){
34002     this.allowParentInsert = false;
34003     this.allowContainerDrop = false;
34004     this.appendOnly = false;
34005     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34006     this.tree = tree;
34007     this.lastInsertClass = "x-tree-no-status";
34008     this.dragOverData = {};
34009 };
34010
34011 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34012     ddGroup : "TreeDD",
34013     scroll:  true,
34014     
34015     expandDelay : 1000,
34016     
34017     expandNode : function(node){
34018         if(node.hasChildNodes() && !node.isExpanded()){
34019             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34020         }
34021     },
34022     
34023     queueExpand : function(node){
34024         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34025     },
34026     
34027     cancelExpand : function(){
34028         if(this.expandProcId){
34029             clearTimeout(this.expandProcId);
34030             this.expandProcId = false;
34031         }
34032     },
34033     
34034     isValidDropPoint : function(n, pt, dd, e, data){
34035         if(!n || !data){ return false; }
34036         var targetNode = n.node;
34037         var dropNode = data.node;
34038         // default drop rules
34039         if(!(targetNode && targetNode.isTarget && pt)){
34040             return false;
34041         }
34042         if(pt == "append" && targetNode.allowChildren === false){
34043             return false;
34044         }
34045         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34046             return false;
34047         }
34048         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34049             return false;
34050         }
34051         // reuse the object
34052         var overEvent = this.dragOverData;
34053         overEvent.tree = this.tree;
34054         overEvent.target = targetNode;
34055         overEvent.data = data;
34056         overEvent.point = pt;
34057         overEvent.source = dd;
34058         overEvent.rawEvent = e;
34059         overEvent.dropNode = dropNode;
34060         overEvent.cancel = false;  
34061         var result = this.tree.fireEvent("nodedragover", overEvent);
34062         return overEvent.cancel === false && result !== false;
34063     },
34064     
34065     getDropPoint : function(e, n, dd)
34066     {
34067         var tn = n.node;
34068         if(tn.isRoot){
34069             return tn.allowChildren !== false ? "append" : false; // always append for root
34070         }
34071         var dragEl = n.ddel;
34072         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34073         var y = Roo.lib.Event.getPageY(e);
34074         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34075         
34076         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34077         var noAppend = tn.allowChildren === false;
34078         if(this.appendOnly || tn.parentNode.allowChildren === false){
34079             return noAppend ? false : "append";
34080         }
34081         var noBelow = false;
34082         if(!this.allowParentInsert){
34083             noBelow = tn.hasChildNodes() && tn.isExpanded();
34084         }
34085         var q = (b - t) / (noAppend ? 2 : 3);
34086         if(y >= t && y < (t + q)){
34087             return "above";
34088         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34089             return "below";
34090         }else{
34091             return "append";
34092         }
34093     },
34094     
34095     onNodeEnter : function(n, dd, e, data)
34096     {
34097         this.cancelExpand();
34098     },
34099     
34100     onNodeOver : function(n, dd, e, data)
34101     {
34102        
34103         var pt = this.getDropPoint(e, n, dd);
34104         var node = n.node;
34105         
34106         // auto node expand check
34107         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34108             this.queueExpand(node);
34109         }else if(pt != "append"){
34110             this.cancelExpand();
34111         }
34112         
34113         // set the insert point style on the target node
34114         var returnCls = this.dropNotAllowed;
34115         if(this.isValidDropPoint(n, pt, dd, e, data)){
34116            if(pt){
34117                var el = n.ddel;
34118                var cls;
34119                if(pt == "above"){
34120                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34121                    cls = "x-tree-drag-insert-above";
34122                }else if(pt == "below"){
34123                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34124                    cls = "x-tree-drag-insert-below";
34125                }else{
34126                    returnCls = "x-tree-drop-ok-append";
34127                    cls = "x-tree-drag-append";
34128                }
34129                if(this.lastInsertClass != cls){
34130                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34131                    this.lastInsertClass = cls;
34132                }
34133            }
34134        }
34135        return returnCls;
34136     },
34137     
34138     onNodeOut : function(n, dd, e, data){
34139         
34140         this.cancelExpand();
34141         this.removeDropIndicators(n);
34142     },
34143     
34144     onNodeDrop : function(n, dd, e, data){
34145         var point = this.getDropPoint(e, n, dd);
34146         var targetNode = n.node;
34147         targetNode.ui.startDrop();
34148         if(!this.isValidDropPoint(n, point, dd, e, data)){
34149             targetNode.ui.endDrop();
34150             return false;
34151         }
34152         // first try to find the drop node
34153         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34154         var dropEvent = {
34155             tree : this.tree,
34156             target: targetNode,
34157             data: data,
34158             point: point,
34159             source: dd,
34160             rawEvent: e,
34161             dropNode: dropNode,
34162             cancel: !dropNode   
34163         };
34164         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34165         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34166             targetNode.ui.endDrop();
34167             return false;
34168         }
34169         // allow target changing
34170         targetNode = dropEvent.target;
34171         if(point == "append" && !targetNode.isExpanded()){
34172             targetNode.expand(false, null, function(){
34173                 this.completeDrop(dropEvent);
34174             }.createDelegate(this));
34175         }else{
34176             this.completeDrop(dropEvent);
34177         }
34178         return true;
34179     },
34180     
34181     completeDrop : function(de){
34182         var ns = de.dropNode, p = de.point, t = de.target;
34183         if(!(ns instanceof Array)){
34184             ns = [ns];
34185         }
34186         var n;
34187         for(var i = 0, len = ns.length; i < len; i++){
34188             n = ns[i];
34189             if(p == "above"){
34190                 t.parentNode.insertBefore(n, t);
34191             }else if(p == "below"){
34192                 t.parentNode.insertBefore(n, t.nextSibling);
34193             }else{
34194                 t.appendChild(n);
34195             }
34196         }
34197         n.ui.focus();
34198         if(this.tree.hlDrop){
34199             n.ui.highlight();
34200         }
34201         t.ui.endDrop();
34202         this.tree.fireEvent("nodedrop", de);
34203     },
34204     
34205     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34206         if(this.tree.hlDrop){
34207             dropNode.ui.focus();
34208             dropNode.ui.highlight();
34209         }
34210         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34211     },
34212     
34213     getTree : function(){
34214         return this.tree;
34215     },
34216     
34217     removeDropIndicators : function(n){
34218         if(n && n.ddel){
34219             var el = n.ddel;
34220             Roo.fly(el).removeClass([
34221                     "x-tree-drag-insert-above",
34222                     "x-tree-drag-insert-below",
34223                     "x-tree-drag-append"]);
34224             this.lastInsertClass = "_noclass";
34225         }
34226     },
34227     
34228     beforeDragDrop : function(target, e, id){
34229         this.cancelExpand();
34230         return true;
34231     },
34232     
34233     afterRepair : function(data){
34234         if(data && Roo.enableFx){
34235             data.node.ui.highlight();
34236         }
34237         this.hideProxy();
34238     } 
34239     
34240 });
34241
34242 }
34243 /*
34244  * Based on:
34245  * Ext JS Library 1.1.1
34246  * Copyright(c) 2006-2007, Ext JS, LLC.
34247  *
34248  * Originally Released Under LGPL - original licence link has changed is not relivant.
34249  *
34250  * Fork - LGPL
34251  * <script type="text/javascript">
34252  */
34253  
34254
34255 if(Roo.dd.DragZone){
34256 Roo.tree.TreeDragZone = function(tree, config){
34257     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34258     this.tree = tree;
34259 };
34260
34261 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34262     ddGroup : "TreeDD",
34263    
34264     onBeforeDrag : function(data, e){
34265         var n = data.node;
34266         return n && n.draggable && !n.disabled;
34267     },
34268      
34269     
34270     onInitDrag : function(e){
34271         var data = this.dragData;
34272         this.tree.getSelectionModel().select(data.node);
34273         this.proxy.update("");
34274         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34275         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34276     },
34277     
34278     getRepairXY : function(e, data){
34279         return data.node.ui.getDDRepairXY();
34280     },
34281     
34282     onEndDrag : function(data, e){
34283         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34284         
34285         
34286     },
34287     
34288     onValidDrop : function(dd, e, id){
34289         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34290         this.hideProxy();
34291     },
34292     
34293     beforeInvalidDrop : function(e, id){
34294         // this scrolls the original position back into view
34295         var sm = this.tree.getSelectionModel();
34296         sm.clearSelections();
34297         sm.select(this.dragData.node);
34298     }
34299 });
34300 }/*
34301  * Based on:
34302  * Ext JS Library 1.1.1
34303  * Copyright(c) 2006-2007, Ext JS, LLC.
34304  *
34305  * Originally Released Under LGPL - original licence link has changed is not relivant.
34306  *
34307  * Fork - LGPL
34308  * <script type="text/javascript">
34309  */
34310 /**
34311  * @class Roo.tree.TreeEditor
34312  * @extends Roo.Editor
34313  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34314  * as the editor field.
34315  * @constructor
34316  * @param {Object} config (used to be the tree panel.)
34317  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34318  * 
34319  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34320  * @cfg {Roo.form.TextField|Object} field The field configuration
34321  *
34322  * 
34323  */
34324 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34325     var tree = config;
34326     var field;
34327     if (oldconfig) { // old style..
34328         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34329     } else {
34330         // new style..
34331         tree = config.tree;
34332         config.field = config.field  || {};
34333         config.field.xtype = 'TextField';
34334         field = Roo.factory(config.field, Roo.form);
34335     }
34336     config = config || {};
34337     
34338     
34339     this.addEvents({
34340         /**
34341          * @event beforenodeedit
34342          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34343          * false from the handler of this event.
34344          * @param {Editor} this
34345          * @param {Roo.tree.Node} node 
34346          */
34347         "beforenodeedit" : true
34348     });
34349     
34350     //Roo.log(config);
34351     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34352
34353     this.tree = tree;
34354
34355     tree.on('beforeclick', this.beforeNodeClick, this);
34356     tree.getTreeEl().on('mousedown', this.hide, this);
34357     this.on('complete', this.updateNode, this);
34358     this.on('beforestartedit', this.fitToTree, this);
34359     this.on('startedit', this.bindScroll, this, {delay:10});
34360     this.on('specialkey', this.onSpecialKey, this);
34361 };
34362
34363 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34364     /**
34365      * @cfg {String} alignment
34366      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34367      */
34368     alignment: "l-l",
34369     // inherit
34370     autoSize: false,
34371     /**
34372      * @cfg {Boolean} hideEl
34373      * True to hide the bound element while the editor is displayed (defaults to false)
34374      */
34375     hideEl : false,
34376     /**
34377      * @cfg {String} cls
34378      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34379      */
34380     cls: "x-small-editor x-tree-editor",
34381     /**
34382      * @cfg {Boolean} shim
34383      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34384      */
34385     shim:false,
34386     // inherit
34387     shadow:"frame",
34388     /**
34389      * @cfg {Number} maxWidth
34390      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34391      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34392      * scroll and client offsets into account prior to each edit.
34393      */
34394     maxWidth: 250,
34395
34396     editDelay : 350,
34397
34398     // private
34399     fitToTree : function(ed, el){
34400         var td = this.tree.getTreeEl().dom, nd = el.dom;
34401         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34402             td.scrollLeft = nd.offsetLeft;
34403         }
34404         var w = Math.min(
34405                 this.maxWidth,
34406                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34407         this.setSize(w, '');
34408         
34409         return this.fireEvent('beforenodeedit', this, this.editNode);
34410         
34411     },
34412
34413     // private
34414     triggerEdit : function(node){
34415         this.completeEdit();
34416         this.editNode = node;
34417         this.startEdit(node.ui.textNode, node.text);
34418     },
34419
34420     // private
34421     bindScroll : function(){
34422         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34423     },
34424
34425     // private
34426     beforeNodeClick : function(node, e){
34427         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34428         this.lastClick = new Date();
34429         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34430             e.stopEvent();
34431             this.triggerEdit(node);
34432             return false;
34433         }
34434         return true;
34435     },
34436
34437     // private
34438     updateNode : function(ed, value){
34439         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
34440         this.editNode.setText(value);
34441     },
34442
34443     // private
34444     onHide : function(){
34445         Roo.tree.TreeEditor.superclass.onHide.call(this);
34446         if(this.editNode){
34447             this.editNode.ui.focus();
34448         }
34449     },
34450
34451     // private
34452     onSpecialKey : function(field, e){
34453         var k = e.getKey();
34454         if(k == e.ESC){
34455             e.stopEvent();
34456             this.cancelEdit();
34457         }else if(k == e.ENTER && !e.hasModifier()){
34458             e.stopEvent();
34459             this.completeEdit();
34460         }
34461     }
34462 });//<Script type="text/javascript">
34463 /*
34464  * Based on:
34465  * Ext JS Library 1.1.1
34466  * Copyright(c) 2006-2007, Ext JS, LLC.
34467  *
34468  * Originally Released Under LGPL - original licence link has changed is not relivant.
34469  *
34470  * Fork - LGPL
34471  * <script type="text/javascript">
34472  */
34473  
34474 /**
34475  * Not documented??? - probably should be...
34476  */
34477
34478 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
34479     //focus: Roo.emptyFn, // prevent odd scrolling behavior
34480     
34481     renderElements : function(n, a, targetNode, bulkRender){
34482         //consel.log("renderElements?");
34483         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34484
34485         var t = n.getOwnerTree();
34486         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
34487         
34488         var cols = t.columns;
34489         var bw = t.borderWidth;
34490         var c = cols[0];
34491         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34492          var cb = typeof a.checked == "boolean";
34493         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34494         var colcls = 'x-t-' + tid + '-c0';
34495         var buf = [
34496             '<li class="x-tree-node">',
34497             
34498                 
34499                 '<div class="x-tree-node-el ', a.cls,'">',
34500                     // extran...
34501                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
34502                 
34503                 
34504                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
34505                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
34506                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
34507                            (a.icon ? ' x-tree-node-inline-icon' : ''),
34508                            (a.iconCls ? ' '+a.iconCls : ''),
34509                            '" unselectable="on" />',
34510                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
34511                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
34512                              
34513                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34514                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
34515                             '<span unselectable="on" qtip="' + tx + '">',
34516                              tx,
34517                              '</span></a>' ,
34518                     '</div>',
34519                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34520                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
34521                  ];
34522         for(var i = 1, len = cols.length; i < len; i++){
34523             c = cols[i];
34524             colcls = 'x-t-' + tid + '-c' +i;
34525             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34526             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
34527                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
34528                       "</div>");
34529          }
34530          
34531          buf.push(
34532             '</a>',
34533             '<div class="x-clear"></div></div>',
34534             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34535             "</li>");
34536         
34537         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34538             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34539                                 n.nextSibling.ui.getEl(), buf.join(""));
34540         }else{
34541             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34542         }
34543         var el = this.wrap.firstChild;
34544         this.elRow = el;
34545         this.elNode = el.firstChild;
34546         this.ranchor = el.childNodes[1];
34547         this.ctNode = this.wrap.childNodes[1];
34548         var cs = el.firstChild.childNodes;
34549         this.indentNode = cs[0];
34550         this.ecNode = cs[1];
34551         this.iconNode = cs[2];
34552         var index = 3;
34553         if(cb){
34554             this.checkbox = cs[3];
34555             index++;
34556         }
34557         this.anchor = cs[index];
34558         
34559         this.textNode = cs[index].firstChild;
34560         
34561         //el.on("click", this.onClick, this);
34562         //el.on("dblclick", this.onDblClick, this);
34563         
34564         
34565        // console.log(this);
34566     },
34567     initEvents : function(){
34568         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
34569         
34570             
34571         var a = this.ranchor;
34572
34573         var el = Roo.get(a);
34574
34575         if(Roo.isOpera){ // opera render bug ignores the CSS
34576             el.setStyle("text-decoration", "none");
34577         }
34578
34579         el.on("click", this.onClick, this);
34580         el.on("dblclick", this.onDblClick, this);
34581         el.on("contextmenu", this.onContextMenu, this);
34582         
34583     },
34584     
34585     /*onSelectedChange : function(state){
34586         if(state){
34587             this.focus();
34588             this.addClass("x-tree-selected");
34589         }else{
34590             //this.blur();
34591             this.removeClass("x-tree-selected");
34592         }
34593     },*/
34594     addClass : function(cls){
34595         if(this.elRow){
34596             Roo.fly(this.elRow).addClass(cls);
34597         }
34598         
34599     },
34600     
34601     
34602     removeClass : function(cls){
34603         if(this.elRow){
34604             Roo.fly(this.elRow).removeClass(cls);
34605         }
34606     }
34607
34608     
34609     
34610 });//<Script type="text/javascript">
34611
34612 /*
34613  * Based on:
34614  * Ext JS Library 1.1.1
34615  * Copyright(c) 2006-2007, Ext JS, LLC.
34616  *
34617  * Originally Released Under LGPL - original licence link has changed is not relivant.
34618  *
34619  * Fork - LGPL
34620  * <script type="text/javascript">
34621  */
34622  
34623
34624 /**
34625  * @class Roo.tree.ColumnTree
34626  * @extends Roo.data.TreePanel
34627  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34628  * @cfg {int} borderWidth  compined right/left border allowance
34629  * @constructor
34630  * @param {String/HTMLElement/Element} el The container element
34631  * @param {Object} config
34632  */
34633 Roo.tree.ColumnTree =  function(el, config)
34634 {
34635    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34636    this.addEvents({
34637         /**
34638         * @event resize
34639         * Fire this event on a container when it resizes
34640         * @param {int} w Width
34641         * @param {int} h Height
34642         */
34643        "resize" : true
34644     });
34645     this.on('resize', this.onResize, this);
34646 };
34647
34648 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34649     //lines:false,
34650     
34651     
34652     borderWidth: Roo.isBorderBox ? 0 : 2, 
34653     headEls : false,
34654     
34655     render : function(){
34656         // add the header.....
34657        
34658         Roo.tree.ColumnTree.superclass.render.apply(this);
34659         
34660         this.el.addClass('x-column-tree');
34661         
34662         this.headers = this.el.createChild(
34663             {cls:'x-tree-headers'},this.innerCt.dom);
34664    
34665         var cols = this.columns, c;
34666         var totalWidth = 0;
34667         this.headEls = [];
34668         var  len = cols.length;
34669         for(var i = 0; i < len; i++){
34670              c = cols[i];
34671              totalWidth += c.width;
34672             this.headEls.push(this.headers.createChild({
34673                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34674                  cn: {
34675                      cls:'x-tree-hd-text',
34676                      html: c.header
34677                  },
34678                  style:'width:'+(c.width-this.borderWidth)+'px;'
34679              }));
34680         }
34681         this.headers.createChild({cls:'x-clear'});
34682         // prevent floats from wrapping when clipped
34683         this.headers.setWidth(totalWidth);
34684         //this.innerCt.setWidth(totalWidth);
34685         this.innerCt.setStyle({ overflow: 'auto' });
34686         this.onResize(this.width, this.height);
34687              
34688         
34689     },
34690     onResize : function(w,h)
34691     {
34692         this.height = h;
34693         this.width = w;
34694         // resize cols..
34695         this.innerCt.setWidth(this.width);
34696         this.innerCt.setHeight(this.height-20);
34697         
34698         // headers...
34699         var cols = this.columns, c;
34700         var totalWidth = 0;
34701         var expEl = false;
34702         var len = cols.length;
34703         for(var i = 0; i < len; i++){
34704             c = cols[i];
34705             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34706                 // it's the expander..
34707                 expEl  = this.headEls[i];
34708                 continue;
34709             }
34710             totalWidth += c.width;
34711             
34712         }
34713         if (expEl) {
34714             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34715         }
34716         this.headers.setWidth(w-20);
34717
34718         
34719         
34720         
34721     }
34722 });
34723 /*
34724  * Based on:
34725  * Ext JS Library 1.1.1
34726  * Copyright(c) 2006-2007, Ext JS, LLC.
34727  *
34728  * Originally Released Under LGPL - original licence link has changed is not relivant.
34729  *
34730  * Fork - LGPL
34731  * <script type="text/javascript">
34732  */
34733  
34734 /**
34735  * @class Roo.menu.Menu
34736  * @extends Roo.util.Observable
34737  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34738  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34739  * @constructor
34740  * Creates a new Menu
34741  * @param {Object} config Configuration options
34742  */
34743 Roo.menu.Menu = function(config){
34744     Roo.apply(this, config);
34745     this.id = this.id || Roo.id();
34746     this.addEvents({
34747         /**
34748          * @event beforeshow
34749          * Fires before this menu is displayed
34750          * @param {Roo.menu.Menu} this
34751          */
34752         beforeshow : true,
34753         /**
34754          * @event beforehide
34755          * Fires before this menu is hidden
34756          * @param {Roo.menu.Menu} this
34757          */
34758         beforehide : true,
34759         /**
34760          * @event show
34761          * Fires after this menu is displayed
34762          * @param {Roo.menu.Menu} this
34763          */
34764         show : true,
34765         /**
34766          * @event hide
34767          * Fires after this menu is hidden
34768          * @param {Roo.menu.Menu} this
34769          */
34770         hide : true,
34771         /**
34772          * @event click
34773          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34774          * @param {Roo.menu.Menu} this
34775          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34776          * @param {Roo.EventObject} e
34777          */
34778         click : true,
34779         /**
34780          * @event mouseover
34781          * Fires when the mouse is hovering over this menu
34782          * @param {Roo.menu.Menu} this
34783          * @param {Roo.EventObject} e
34784          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34785          */
34786         mouseover : true,
34787         /**
34788          * @event mouseout
34789          * Fires when the mouse exits this menu
34790          * @param {Roo.menu.Menu} this
34791          * @param {Roo.EventObject} e
34792          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34793          */
34794         mouseout : true,
34795         /**
34796          * @event itemclick
34797          * Fires when a menu item contained in this menu is clicked
34798          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34799          * @param {Roo.EventObject} e
34800          */
34801         itemclick: true
34802     });
34803     if (this.registerMenu) {
34804         Roo.menu.MenuMgr.register(this);
34805     }
34806     
34807     var mis = this.items;
34808     this.items = new Roo.util.MixedCollection();
34809     if(mis){
34810         this.add.apply(this, mis);
34811     }
34812 };
34813
34814 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34815     /**
34816      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34817      */
34818     minWidth : 120,
34819     /**
34820      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34821      * for bottom-right shadow (defaults to "sides")
34822      */
34823     shadow : "sides",
34824     /**
34825      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34826      * this menu (defaults to "tl-tr?")
34827      */
34828     subMenuAlign : "tl-tr?",
34829     /**
34830      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34831      * relative to its element of origin (defaults to "tl-bl?")
34832      */
34833     defaultAlign : "tl-bl?",
34834     /**
34835      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34836      */
34837     allowOtherMenus : false,
34838     /**
34839      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34840      */
34841     registerMenu : true,
34842
34843     hidden:true,
34844
34845     // private
34846     render : function(){
34847         if(this.el){
34848             return;
34849         }
34850         var el = this.el = new Roo.Layer({
34851             cls: "x-menu",
34852             shadow:this.shadow,
34853             constrain: false,
34854             parentEl: this.parentEl || document.body,
34855             zindex:15000
34856         });
34857
34858         this.keyNav = new Roo.menu.MenuNav(this);
34859
34860         if(this.plain){
34861             el.addClass("x-menu-plain");
34862         }
34863         if(this.cls){
34864             el.addClass(this.cls);
34865         }
34866         // generic focus element
34867         this.focusEl = el.createChild({
34868             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34869         });
34870         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34871         ul.on("click", this.onClick, this);
34872         ul.on("mouseover", this.onMouseOver, this);
34873         ul.on("mouseout", this.onMouseOut, this);
34874         this.items.each(function(item){
34875             if (item.hidden) {
34876                 return;
34877             }
34878             
34879             var li = document.createElement("li");
34880             li.className = "x-menu-list-item";
34881             ul.dom.appendChild(li);
34882             item.render(li, this);
34883         }, this);
34884         this.ul = ul;
34885         this.autoWidth();
34886     },
34887
34888     // private
34889     autoWidth : function(){
34890         var el = this.el, ul = this.ul;
34891         if(!el){
34892             return;
34893         }
34894         var w = this.width;
34895         if(w){
34896             el.setWidth(w);
34897         }else if(Roo.isIE){
34898             el.setWidth(this.minWidth);
34899             var t = el.dom.offsetWidth; // force recalc
34900             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34901         }
34902     },
34903
34904     // private
34905     delayAutoWidth : function(){
34906         if(this.rendered){
34907             if(!this.awTask){
34908                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34909             }
34910             this.awTask.delay(20);
34911         }
34912     },
34913
34914     // private
34915     findTargetItem : function(e){
34916         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34917         if(t && t.menuItemId){
34918             return this.items.get(t.menuItemId);
34919         }
34920     },
34921
34922     // private
34923     onClick : function(e){
34924         var t;
34925         if(t = this.findTargetItem(e)){
34926             t.onClick(e);
34927             this.fireEvent("click", this, t, e);
34928         }
34929     },
34930
34931     // private
34932     setActiveItem : function(item, autoExpand){
34933         if(item != this.activeItem){
34934             if(this.activeItem){
34935                 this.activeItem.deactivate();
34936             }
34937             this.activeItem = item;
34938             item.activate(autoExpand);
34939         }else if(autoExpand){
34940             item.expandMenu();
34941         }
34942     },
34943
34944     // private
34945     tryActivate : function(start, step){
34946         var items = this.items;
34947         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34948             var item = items.get(i);
34949             if(!item.disabled && item.canActivate){
34950                 this.setActiveItem(item, false);
34951                 return item;
34952             }
34953         }
34954         return false;
34955     },
34956
34957     // private
34958     onMouseOver : function(e){
34959         var t;
34960         if(t = this.findTargetItem(e)){
34961             if(t.canActivate && !t.disabled){
34962                 this.setActiveItem(t, true);
34963             }
34964         }
34965         this.fireEvent("mouseover", this, e, t);
34966     },
34967
34968     // private
34969     onMouseOut : function(e){
34970         var t;
34971         if(t = this.findTargetItem(e)){
34972             if(t == this.activeItem && t.shouldDeactivate(e)){
34973                 this.activeItem.deactivate();
34974                 delete this.activeItem;
34975             }
34976         }
34977         this.fireEvent("mouseout", this, e, t);
34978     },
34979
34980     /**
34981      * Read-only.  Returns true if the menu is currently displayed, else false.
34982      * @type Boolean
34983      */
34984     isVisible : function(){
34985         return this.el && !this.hidden;
34986     },
34987
34988     /**
34989      * Displays this menu relative to another element
34990      * @param {String/HTMLElement/Roo.Element} element The element to align to
34991      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34992      * the element (defaults to this.defaultAlign)
34993      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34994      */
34995     show : function(el, pos, parentMenu){
34996         this.parentMenu = parentMenu;
34997         if(!this.el){
34998             this.render();
34999         }
35000         this.fireEvent("beforeshow", this);
35001         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35002     },
35003
35004     /**
35005      * Displays this menu at a specific xy position
35006      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35007      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35008      */
35009     showAt : function(xy, parentMenu, /* private: */_e){
35010         this.parentMenu = parentMenu;
35011         if(!this.el){
35012             this.render();
35013         }
35014         if(_e !== false){
35015             this.fireEvent("beforeshow", this);
35016             xy = this.el.adjustForConstraints(xy);
35017         }
35018         this.el.setXY(xy);
35019         this.el.show();
35020         this.hidden = false;
35021         this.focus();
35022         this.fireEvent("show", this);
35023     },
35024
35025     focus : function(){
35026         if(!this.hidden){
35027             this.doFocus.defer(50, this);
35028         }
35029     },
35030
35031     doFocus : function(){
35032         if(!this.hidden){
35033             this.focusEl.focus();
35034         }
35035     },
35036
35037     /**
35038      * Hides this menu and optionally all parent menus
35039      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35040      */
35041     hide : function(deep){
35042         if(this.el && this.isVisible()){
35043             this.fireEvent("beforehide", this);
35044             if(this.activeItem){
35045                 this.activeItem.deactivate();
35046                 this.activeItem = null;
35047             }
35048             this.el.hide();
35049             this.hidden = true;
35050             this.fireEvent("hide", this);
35051         }
35052         if(deep === true && this.parentMenu){
35053             this.parentMenu.hide(true);
35054         }
35055     },
35056
35057     /**
35058      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35059      * Any of the following are valid:
35060      * <ul>
35061      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35062      * <li>An HTMLElement object which will be converted to a menu item</li>
35063      * <li>A menu item config object that will be created as a new menu item</li>
35064      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35065      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35066      * </ul>
35067      * Usage:
35068      * <pre><code>
35069 // Create the menu
35070 var menu = new Roo.menu.Menu();
35071
35072 // Create a menu item to add by reference
35073 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35074
35075 // Add a bunch of items at once using different methods.
35076 // Only the last item added will be returned.
35077 var item = menu.add(
35078     menuItem,                // add existing item by ref
35079     'Dynamic Item',          // new TextItem
35080     '-',                     // new separator
35081     { text: 'Config Item' }  // new item by config
35082 );
35083 </code></pre>
35084      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35085      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35086      */
35087     add : function(){
35088         var a = arguments, l = a.length, item;
35089         for(var i = 0; i < l; i++){
35090             var el = a[i];
35091             if ((typeof(el) == "object") && el.xtype && el.xns) {
35092                 el = Roo.factory(el, Roo.menu);
35093             }
35094             
35095             if(el.render){ // some kind of Item
35096                 item = this.addItem(el);
35097             }else if(typeof el == "string"){ // string
35098                 if(el == "separator" || el == "-"){
35099                     item = this.addSeparator();
35100                 }else{
35101                     item = this.addText(el);
35102                 }
35103             }else if(el.tagName || el.el){ // element
35104                 item = this.addElement(el);
35105             }else if(typeof el == "object"){ // must be menu item config?
35106                 item = this.addMenuItem(el);
35107             }
35108         }
35109         return item;
35110     },
35111
35112     /**
35113      * Returns this menu's underlying {@link Roo.Element} object
35114      * @return {Roo.Element} The element
35115      */
35116     getEl : function(){
35117         if(!this.el){
35118             this.render();
35119         }
35120         return this.el;
35121     },
35122
35123     /**
35124      * Adds a separator bar to the menu
35125      * @return {Roo.menu.Item} The menu item that was added
35126      */
35127     addSeparator : function(){
35128         return this.addItem(new Roo.menu.Separator());
35129     },
35130
35131     /**
35132      * Adds an {@link Roo.Element} object to the menu
35133      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35134      * @return {Roo.menu.Item} The menu item that was added
35135      */
35136     addElement : function(el){
35137         return this.addItem(new Roo.menu.BaseItem(el));
35138     },
35139
35140     /**
35141      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35142      * @param {Roo.menu.Item} item The menu item to add
35143      * @return {Roo.menu.Item} The menu item that was added
35144      */
35145     addItem : function(item){
35146         this.items.add(item);
35147         if(this.ul){
35148             var li = document.createElement("li");
35149             li.className = "x-menu-list-item";
35150             this.ul.dom.appendChild(li);
35151             item.render(li, this);
35152             this.delayAutoWidth();
35153         }
35154         return item;
35155     },
35156
35157     /**
35158      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35159      * @param {Object} config A MenuItem config object
35160      * @return {Roo.menu.Item} The menu item that was added
35161      */
35162     addMenuItem : function(config){
35163         if(!(config instanceof Roo.menu.Item)){
35164             if(typeof config.checked == "boolean"){ // must be check menu item config?
35165                 config = new Roo.menu.CheckItem(config);
35166             }else{
35167                 config = new Roo.menu.Item(config);
35168             }
35169         }
35170         return this.addItem(config);
35171     },
35172
35173     /**
35174      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35175      * @param {String} text The text to display in the menu item
35176      * @return {Roo.menu.Item} The menu item that was added
35177      */
35178     addText : function(text){
35179         return this.addItem(new Roo.menu.TextItem({ text : text }));
35180     },
35181
35182     /**
35183      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35184      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35185      * @param {Roo.menu.Item} item The menu item to add
35186      * @return {Roo.menu.Item} The menu item that was added
35187      */
35188     insert : function(index, item){
35189         this.items.insert(index, item);
35190         if(this.ul){
35191             var li = document.createElement("li");
35192             li.className = "x-menu-list-item";
35193             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35194             item.render(li, this);
35195             this.delayAutoWidth();
35196         }
35197         return item;
35198     },
35199
35200     /**
35201      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35202      * @param {Roo.menu.Item} item The menu item to remove
35203      */
35204     remove : function(item){
35205         this.items.removeKey(item.id);
35206         item.destroy();
35207     },
35208
35209     /**
35210      * Removes and destroys all items in the menu
35211      */
35212     removeAll : function(){
35213         var f;
35214         while(f = this.items.first()){
35215             this.remove(f);
35216         }
35217     }
35218 });
35219
35220 // MenuNav is a private utility class used internally by the Menu
35221 Roo.menu.MenuNav = function(menu){
35222     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35223     this.scope = this.menu = menu;
35224 };
35225
35226 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35227     doRelay : function(e, h){
35228         var k = e.getKey();
35229         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35230             this.menu.tryActivate(0, 1);
35231             return false;
35232         }
35233         return h.call(this.scope || this, e, this.menu);
35234     },
35235
35236     up : function(e, m){
35237         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35238             m.tryActivate(m.items.length-1, -1);
35239         }
35240     },
35241
35242     down : function(e, m){
35243         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35244             m.tryActivate(0, 1);
35245         }
35246     },
35247
35248     right : function(e, m){
35249         if(m.activeItem){
35250             m.activeItem.expandMenu(true);
35251         }
35252     },
35253
35254     left : function(e, m){
35255         m.hide();
35256         if(m.parentMenu && m.parentMenu.activeItem){
35257             m.parentMenu.activeItem.activate();
35258         }
35259     },
35260
35261     enter : function(e, m){
35262         if(m.activeItem){
35263             e.stopPropagation();
35264             m.activeItem.onClick(e);
35265             m.fireEvent("click", this, m.activeItem);
35266             return true;
35267         }
35268     }
35269 });/*
35270  * Based on:
35271  * Ext JS Library 1.1.1
35272  * Copyright(c) 2006-2007, Ext JS, LLC.
35273  *
35274  * Originally Released Under LGPL - original licence link has changed is not relivant.
35275  *
35276  * Fork - LGPL
35277  * <script type="text/javascript">
35278  */
35279  
35280 /**
35281  * @class Roo.menu.MenuMgr
35282  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35283  * @singleton
35284  */
35285 Roo.menu.MenuMgr = function(){
35286    var menus, active, groups = {}, attached = false, lastShow = new Date();
35287
35288    // private - called when first menu is created
35289    function init(){
35290        menus = {};
35291        active = new Roo.util.MixedCollection();
35292        Roo.get(document).addKeyListener(27, function(){
35293            if(active.length > 0){
35294                hideAll();
35295            }
35296        });
35297    }
35298
35299    // private
35300    function hideAll(){
35301        if(active && active.length > 0){
35302            var c = active.clone();
35303            c.each(function(m){
35304                m.hide();
35305            });
35306        }
35307    }
35308
35309    // private
35310    function onHide(m){
35311        active.remove(m);
35312        if(active.length < 1){
35313            Roo.get(document).un("mousedown", onMouseDown);
35314            attached = false;
35315        }
35316    }
35317
35318    // private
35319    function onShow(m){
35320        var last = active.last();
35321        lastShow = new Date();
35322        active.add(m);
35323        if(!attached){
35324            Roo.get(document).on("mousedown", onMouseDown);
35325            attached = true;
35326        }
35327        if(m.parentMenu){
35328           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35329           m.parentMenu.activeChild = m;
35330        }else if(last && last.isVisible()){
35331           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35332        }
35333    }
35334
35335    // private
35336    function onBeforeHide(m){
35337        if(m.activeChild){
35338            m.activeChild.hide();
35339        }
35340        if(m.autoHideTimer){
35341            clearTimeout(m.autoHideTimer);
35342            delete m.autoHideTimer;
35343        }
35344    }
35345
35346    // private
35347    function onBeforeShow(m){
35348        var pm = m.parentMenu;
35349        if(!pm && !m.allowOtherMenus){
35350            hideAll();
35351        }else if(pm && pm.activeChild && active != m){
35352            pm.activeChild.hide();
35353        }
35354    }
35355
35356    // private
35357    function onMouseDown(e){
35358        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35359            hideAll();
35360        }
35361    }
35362
35363    // private
35364    function onBeforeCheck(mi, state){
35365        if(state){
35366            var g = groups[mi.group];
35367            for(var i = 0, l = g.length; i < l; i++){
35368                if(g[i] != mi){
35369                    g[i].setChecked(false);
35370                }
35371            }
35372        }
35373    }
35374
35375    return {
35376
35377        /**
35378         * Hides all menus that are currently visible
35379         */
35380        hideAll : function(){
35381             hideAll();  
35382        },
35383
35384        // private
35385        register : function(menu){
35386            if(!menus){
35387                init();
35388            }
35389            menus[menu.id] = menu;
35390            menu.on("beforehide", onBeforeHide);
35391            menu.on("hide", onHide);
35392            menu.on("beforeshow", onBeforeShow);
35393            menu.on("show", onShow);
35394            var g = menu.group;
35395            if(g && menu.events["checkchange"]){
35396                if(!groups[g]){
35397                    groups[g] = [];
35398                }
35399                groups[g].push(menu);
35400                menu.on("checkchange", onCheck);
35401            }
35402        },
35403
35404         /**
35405          * Returns a {@link Roo.menu.Menu} object
35406          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35407          * be used to generate and return a new Menu instance.
35408          */
35409        get : function(menu){
35410            if(typeof menu == "string"){ // menu id
35411                return menus[menu];
35412            }else if(menu.events){  // menu instance
35413                return menu;
35414            }else if(typeof menu.length == 'number'){ // array of menu items?
35415                return new Roo.menu.Menu({items:menu});
35416            }else{ // otherwise, must be a config
35417                return new Roo.menu.Menu(menu);
35418            }
35419        },
35420
35421        // private
35422        unregister : function(menu){
35423            delete menus[menu.id];
35424            menu.un("beforehide", onBeforeHide);
35425            menu.un("hide", onHide);
35426            menu.un("beforeshow", onBeforeShow);
35427            menu.un("show", onShow);
35428            var g = menu.group;
35429            if(g && menu.events["checkchange"]){
35430                groups[g].remove(menu);
35431                menu.un("checkchange", onCheck);
35432            }
35433        },
35434
35435        // private
35436        registerCheckable : function(menuItem){
35437            var g = menuItem.group;
35438            if(g){
35439                if(!groups[g]){
35440                    groups[g] = [];
35441                }
35442                groups[g].push(menuItem);
35443                menuItem.on("beforecheckchange", onBeforeCheck);
35444            }
35445        },
35446
35447        // private
35448        unregisterCheckable : function(menuItem){
35449            var g = menuItem.group;
35450            if(g){
35451                groups[g].remove(menuItem);
35452                menuItem.un("beforecheckchange", onBeforeCheck);
35453            }
35454        }
35455    };
35456 }();/*
35457  * Based on:
35458  * Ext JS Library 1.1.1
35459  * Copyright(c) 2006-2007, Ext JS, LLC.
35460  *
35461  * Originally Released Under LGPL - original licence link has changed is not relivant.
35462  *
35463  * Fork - LGPL
35464  * <script type="text/javascript">
35465  */
35466  
35467
35468 /**
35469  * @class Roo.menu.BaseItem
35470  * @extends Roo.Component
35471  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
35472  * management and base configuration options shared by all menu components.
35473  * @constructor
35474  * Creates a new BaseItem
35475  * @param {Object} config Configuration options
35476  */
35477 Roo.menu.BaseItem = function(config){
35478     Roo.menu.BaseItem.superclass.constructor.call(this, config);
35479
35480     this.addEvents({
35481         /**
35482          * @event click
35483          * Fires when this item is clicked
35484          * @param {Roo.menu.BaseItem} this
35485          * @param {Roo.EventObject} e
35486          */
35487         click: true,
35488         /**
35489          * @event activate
35490          * Fires when this item is activated
35491          * @param {Roo.menu.BaseItem} this
35492          */
35493         activate : true,
35494         /**
35495          * @event deactivate
35496          * Fires when this item is deactivated
35497          * @param {Roo.menu.BaseItem} this
35498          */
35499         deactivate : true
35500     });
35501
35502     if(this.handler){
35503         this.on("click", this.handler, this.scope, true);
35504     }
35505 };
35506
35507 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
35508     /**
35509      * @cfg {Function} handler
35510      * A function that will handle the click event of this menu item (defaults to undefined)
35511      */
35512     /**
35513      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
35514      */
35515     canActivate : false,
35516     
35517      /**
35518      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
35519      */
35520     hidden: false,
35521     
35522     /**
35523      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
35524      */
35525     activeClass : "x-menu-item-active",
35526     /**
35527      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
35528      */
35529     hideOnClick : true,
35530     /**
35531      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
35532      */
35533     hideDelay : 100,
35534
35535     // private
35536     ctype: "Roo.menu.BaseItem",
35537
35538     // private
35539     actionMode : "container",
35540
35541     // private
35542     render : function(container, parentMenu){
35543         this.parentMenu = parentMenu;
35544         Roo.menu.BaseItem.superclass.render.call(this, container);
35545         this.container.menuItemId = this.id;
35546     },
35547
35548     // private
35549     onRender : function(container, position){
35550         this.el = Roo.get(this.el);
35551         container.dom.appendChild(this.el.dom);
35552     },
35553
35554     // private
35555     onClick : function(e){
35556         if(!this.disabled && this.fireEvent("click", this, e) !== false
35557                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
35558             this.handleClick(e);
35559         }else{
35560             e.stopEvent();
35561         }
35562     },
35563
35564     // private
35565     activate : function(){
35566         if(this.disabled){
35567             return false;
35568         }
35569         var li = this.container;
35570         li.addClass(this.activeClass);
35571         this.region = li.getRegion().adjust(2, 2, -2, -2);
35572         this.fireEvent("activate", this);
35573         return true;
35574     },
35575
35576     // private
35577     deactivate : function(){
35578         this.container.removeClass(this.activeClass);
35579         this.fireEvent("deactivate", this);
35580     },
35581
35582     // private
35583     shouldDeactivate : function(e){
35584         return !this.region || !this.region.contains(e.getPoint());
35585     },
35586
35587     // private
35588     handleClick : function(e){
35589         if(this.hideOnClick){
35590             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35591         }
35592     },
35593
35594     // private
35595     expandMenu : function(autoActivate){
35596         // do nothing
35597     },
35598
35599     // private
35600     hideMenu : function(){
35601         // do nothing
35602     }
35603 });/*
35604  * Based on:
35605  * Ext JS Library 1.1.1
35606  * Copyright(c) 2006-2007, Ext JS, LLC.
35607  *
35608  * Originally Released Under LGPL - original licence link has changed is not relivant.
35609  *
35610  * Fork - LGPL
35611  * <script type="text/javascript">
35612  */
35613  
35614 /**
35615  * @class Roo.menu.Adapter
35616  * @extends Roo.menu.BaseItem
35617  * 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.
35618  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35619  * @constructor
35620  * Creates a new Adapter
35621  * @param {Object} config Configuration options
35622  */
35623 Roo.menu.Adapter = function(component, config){
35624     Roo.menu.Adapter.superclass.constructor.call(this, config);
35625     this.component = component;
35626 };
35627 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35628     // private
35629     canActivate : true,
35630
35631     // private
35632     onRender : function(container, position){
35633         this.component.render(container);
35634         this.el = this.component.getEl();
35635     },
35636
35637     // private
35638     activate : function(){
35639         if(this.disabled){
35640             return false;
35641         }
35642         this.component.focus();
35643         this.fireEvent("activate", this);
35644         return true;
35645     },
35646
35647     // private
35648     deactivate : function(){
35649         this.fireEvent("deactivate", this);
35650     },
35651
35652     // private
35653     disable : function(){
35654         this.component.disable();
35655         Roo.menu.Adapter.superclass.disable.call(this);
35656     },
35657
35658     // private
35659     enable : function(){
35660         this.component.enable();
35661         Roo.menu.Adapter.superclass.enable.call(this);
35662     }
35663 });/*
35664  * Based on:
35665  * Ext JS Library 1.1.1
35666  * Copyright(c) 2006-2007, Ext JS, LLC.
35667  *
35668  * Originally Released Under LGPL - original licence link has changed is not relivant.
35669  *
35670  * Fork - LGPL
35671  * <script type="text/javascript">
35672  */
35673
35674 /**
35675  * @class Roo.menu.TextItem
35676  * @extends Roo.menu.BaseItem
35677  * Adds a static text string to a menu, usually used as either a heading or group separator.
35678  * Note: old style constructor with text is still supported.
35679  * 
35680  * @constructor
35681  * Creates a new TextItem
35682  * @param {Object} cfg Configuration
35683  */
35684 Roo.menu.TextItem = function(cfg){
35685     if (typeof(cfg) == 'string') {
35686         this.text = cfg;
35687     } else {
35688         Roo.apply(this,cfg);
35689     }
35690     
35691     Roo.menu.TextItem.superclass.constructor.call(this);
35692 };
35693
35694 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35695     /**
35696      * @cfg {Boolean} text Text to show on item.
35697      */
35698     text : '',
35699     
35700     /**
35701      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35702      */
35703     hideOnClick : false,
35704     /**
35705      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35706      */
35707     itemCls : "x-menu-text",
35708
35709     // private
35710     onRender : function(){
35711         var s = document.createElement("span");
35712         s.className = this.itemCls;
35713         s.innerHTML = this.text;
35714         this.el = s;
35715         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35716     }
35717 });/*
35718  * Based on:
35719  * Ext JS Library 1.1.1
35720  * Copyright(c) 2006-2007, Ext JS, LLC.
35721  *
35722  * Originally Released Under LGPL - original licence link has changed is not relivant.
35723  *
35724  * Fork - LGPL
35725  * <script type="text/javascript">
35726  */
35727
35728 /**
35729  * @class Roo.menu.Separator
35730  * @extends Roo.menu.BaseItem
35731  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35732  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35733  * @constructor
35734  * @param {Object} config Configuration options
35735  */
35736 Roo.menu.Separator = function(config){
35737     Roo.menu.Separator.superclass.constructor.call(this, config);
35738 };
35739
35740 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35741     /**
35742      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35743      */
35744     itemCls : "x-menu-sep",
35745     /**
35746      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35747      */
35748     hideOnClick : false,
35749
35750     // private
35751     onRender : function(li){
35752         var s = document.createElement("span");
35753         s.className = this.itemCls;
35754         s.innerHTML = "&#160;";
35755         this.el = s;
35756         li.addClass("x-menu-sep-li");
35757         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35758     }
35759 });/*
35760  * Based on:
35761  * Ext JS Library 1.1.1
35762  * Copyright(c) 2006-2007, Ext JS, LLC.
35763  *
35764  * Originally Released Under LGPL - original licence link has changed is not relivant.
35765  *
35766  * Fork - LGPL
35767  * <script type="text/javascript">
35768  */
35769 /**
35770  * @class Roo.menu.Item
35771  * @extends Roo.menu.BaseItem
35772  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35773  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35774  * activation and click handling.
35775  * @constructor
35776  * Creates a new Item
35777  * @param {Object} config Configuration options
35778  */
35779 Roo.menu.Item = function(config){
35780     Roo.menu.Item.superclass.constructor.call(this, config);
35781     if(this.menu){
35782         this.menu = Roo.menu.MenuMgr.get(this.menu);
35783     }
35784 };
35785 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35786     
35787     /**
35788      * @cfg {String} text
35789      * The text to show on the menu item.
35790      */
35791     text: '',
35792      /**
35793      * @cfg {String} HTML to render in menu
35794      * The text to show on the menu item (HTML version).
35795      */
35796     html: '',
35797     /**
35798      * @cfg {String} icon
35799      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35800      */
35801     icon: undefined,
35802     /**
35803      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35804      */
35805     itemCls : "x-menu-item",
35806     /**
35807      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35808      */
35809     canActivate : true,
35810     /**
35811      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35812      */
35813     showDelay: 200,
35814     // doc'd in BaseItem
35815     hideDelay: 200,
35816
35817     // private
35818     ctype: "Roo.menu.Item",
35819     
35820     // private
35821     onRender : function(container, position){
35822         var el = document.createElement("a");
35823         el.hideFocus = true;
35824         el.unselectable = "on";
35825         el.href = this.href || "#";
35826         if(this.hrefTarget){
35827             el.target = this.hrefTarget;
35828         }
35829         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35830         
35831         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35832         
35833         el.innerHTML = String.format(
35834                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35835                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35836         this.el = el;
35837         Roo.menu.Item.superclass.onRender.call(this, container, position);
35838     },
35839
35840     /**
35841      * Sets the text to display in this menu item
35842      * @param {String} text The text to display
35843      * @param {Boolean} isHTML true to indicate text is pure html.
35844      */
35845     setText : function(text, isHTML){
35846         if (isHTML) {
35847             this.html = text;
35848         } else {
35849             this.text = text;
35850             this.html = '';
35851         }
35852         if(this.rendered){
35853             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35854      
35855             this.el.update(String.format(
35856                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35857                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35858             this.parentMenu.autoWidth();
35859         }
35860     },
35861
35862     // private
35863     handleClick : function(e){
35864         if(!this.href){ // if no link defined, stop the event automatically
35865             e.stopEvent();
35866         }
35867         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35868     },
35869
35870     // private
35871     activate : function(autoExpand){
35872         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35873             this.focus();
35874             if(autoExpand){
35875                 this.expandMenu();
35876             }
35877         }
35878         return true;
35879     },
35880
35881     // private
35882     shouldDeactivate : function(e){
35883         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35884             if(this.menu && this.menu.isVisible()){
35885                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35886             }
35887             return true;
35888         }
35889         return false;
35890     },
35891
35892     // private
35893     deactivate : function(){
35894         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35895         this.hideMenu();
35896     },
35897
35898     // private
35899     expandMenu : function(autoActivate){
35900         if(!this.disabled && this.menu){
35901             clearTimeout(this.hideTimer);
35902             delete this.hideTimer;
35903             if(!this.menu.isVisible() && !this.showTimer){
35904                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35905             }else if (this.menu.isVisible() && autoActivate){
35906                 this.menu.tryActivate(0, 1);
35907             }
35908         }
35909     },
35910
35911     // private
35912     deferExpand : function(autoActivate){
35913         delete this.showTimer;
35914         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35915         if(autoActivate){
35916             this.menu.tryActivate(0, 1);
35917         }
35918     },
35919
35920     // private
35921     hideMenu : function(){
35922         clearTimeout(this.showTimer);
35923         delete this.showTimer;
35924         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35925             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35926         }
35927     },
35928
35929     // private
35930     deferHide : function(){
35931         delete this.hideTimer;
35932         this.menu.hide();
35933     }
35934 });/*
35935  * Based on:
35936  * Ext JS Library 1.1.1
35937  * Copyright(c) 2006-2007, Ext JS, LLC.
35938  *
35939  * Originally Released Under LGPL - original licence link has changed is not relivant.
35940  *
35941  * Fork - LGPL
35942  * <script type="text/javascript">
35943  */
35944  
35945 /**
35946  * @class Roo.menu.CheckItem
35947  * @extends Roo.menu.Item
35948  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35949  * @constructor
35950  * Creates a new CheckItem
35951  * @param {Object} config Configuration options
35952  */
35953 Roo.menu.CheckItem = function(config){
35954     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35955     this.addEvents({
35956         /**
35957          * @event beforecheckchange
35958          * Fires before the checked value is set, providing an opportunity to cancel if needed
35959          * @param {Roo.menu.CheckItem} this
35960          * @param {Boolean} checked The new checked value that will be set
35961          */
35962         "beforecheckchange" : true,
35963         /**
35964          * @event checkchange
35965          * Fires after the checked value has been set
35966          * @param {Roo.menu.CheckItem} this
35967          * @param {Boolean} checked The checked value that was set
35968          */
35969         "checkchange" : true
35970     });
35971     if(this.checkHandler){
35972         this.on('checkchange', this.checkHandler, this.scope);
35973     }
35974 };
35975 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35976     /**
35977      * @cfg {String} group
35978      * All check items with the same group name will automatically be grouped into a single-select
35979      * radio button group (defaults to '')
35980      */
35981     /**
35982      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35983      */
35984     itemCls : "x-menu-item x-menu-check-item",
35985     /**
35986      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35987      */
35988     groupClass : "x-menu-group-item",
35989
35990     /**
35991      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35992      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35993      * initialized with checked = true will be rendered as checked.
35994      */
35995     checked: false,
35996
35997     // private
35998     ctype: "Roo.menu.CheckItem",
35999
36000     // private
36001     onRender : function(c){
36002         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36003         if(this.group){
36004             this.el.addClass(this.groupClass);
36005         }
36006         Roo.menu.MenuMgr.registerCheckable(this);
36007         if(this.checked){
36008             this.checked = false;
36009             this.setChecked(true, true);
36010         }
36011     },
36012
36013     // private
36014     destroy : function(){
36015         if(this.rendered){
36016             Roo.menu.MenuMgr.unregisterCheckable(this);
36017         }
36018         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36019     },
36020
36021     /**
36022      * Set the checked state of this item
36023      * @param {Boolean} checked The new checked value
36024      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36025      */
36026     setChecked : function(state, suppressEvent){
36027         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36028             if(this.container){
36029                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36030             }
36031             this.checked = state;
36032             if(suppressEvent !== true){
36033                 this.fireEvent("checkchange", this, state);
36034             }
36035         }
36036     },
36037
36038     // private
36039     handleClick : function(e){
36040        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36041            this.setChecked(!this.checked);
36042        }
36043        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36044     }
36045 });/*
36046  * Based on:
36047  * Ext JS Library 1.1.1
36048  * Copyright(c) 2006-2007, Ext JS, LLC.
36049  *
36050  * Originally Released Under LGPL - original licence link has changed is not relivant.
36051  *
36052  * Fork - LGPL
36053  * <script type="text/javascript">
36054  */
36055  
36056 /**
36057  * @class Roo.menu.DateItem
36058  * @extends Roo.menu.Adapter
36059  * A menu item that wraps the {@link Roo.DatPicker} component.
36060  * @constructor
36061  * Creates a new DateItem
36062  * @param {Object} config Configuration options
36063  */
36064 Roo.menu.DateItem = function(config){
36065     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36066     /** The Roo.DatePicker object @type Roo.DatePicker */
36067     this.picker = this.component;
36068     this.addEvents({select: true});
36069     
36070     this.picker.on("render", function(picker){
36071         picker.getEl().swallowEvent("click");
36072         picker.container.addClass("x-menu-date-item");
36073     });
36074
36075     this.picker.on("select", this.onSelect, this);
36076 };
36077
36078 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36079     // private
36080     onSelect : function(picker, date){
36081         this.fireEvent("select", this, date, picker);
36082         Roo.menu.DateItem.superclass.handleClick.call(this);
36083     }
36084 });/*
36085  * Based on:
36086  * Ext JS Library 1.1.1
36087  * Copyright(c) 2006-2007, Ext JS, LLC.
36088  *
36089  * Originally Released Under LGPL - original licence link has changed is not relivant.
36090  *
36091  * Fork - LGPL
36092  * <script type="text/javascript">
36093  */
36094  
36095 /**
36096  * @class Roo.menu.ColorItem
36097  * @extends Roo.menu.Adapter
36098  * A menu item that wraps the {@link Roo.ColorPalette} component.
36099  * @constructor
36100  * Creates a new ColorItem
36101  * @param {Object} config Configuration options
36102  */
36103 Roo.menu.ColorItem = function(config){
36104     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36105     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36106     this.palette = this.component;
36107     this.relayEvents(this.palette, ["select"]);
36108     if(this.selectHandler){
36109         this.on('select', this.selectHandler, this.scope);
36110     }
36111 };
36112 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36113  * Based on:
36114  * Ext JS Library 1.1.1
36115  * Copyright(c) 2006-2007, Ext JS, LLC.
36116  *
36117  * Originally Released Under LGPL - original licence link has changed is not relivant.
36118  *
36119  * Fork - LGPL
36120  * <script type="text/javascript">
36121  */
36122  
36123
36124 /**
36125  * @class Roo.menu.DateMenu
36126  * @extends Roo.menu.Menu
36127  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36128  * @constructor
36129  * Creates a new DateMenu
36130  * @param {Object} config Configuration options
36131  */
36132 Roo.menu.DateMenu = function(config){
36133     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36134     this.plain = true;
36135     var di = new Roo.menu.DateItem(config);
36136     this.add(di);
36137     /**
36138      * The {@link Roo.DatePicker} instance for this DateMenu
36139      * @type DatePicker
36140      */
36141     this.picker = di.picker;
36142     /**
36143      * @event select
36144      * @param {DatePicker} picker
36145      * @param {Date} date
36146      */
36147     this.relayEvents(di, ["select"]);
36148     this.on('beforeshow', function(){
36149         if(this.picker){
36150             this.picker.hideMonthPicker(false);
36151         }
36152     }, this);
36153 };
36154 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36155     cls:'x-date-menu'
36156 });/*
36157  * Based on:
36158  * Ext JS Library 1.1.1
36159  * Copyright(c) 2006-2007, Ext JS, LLC.
36160  *
36161  * Originally Released Under LGPL - original licence link has changed is not relivant.
36162  *
36163  * Fork - LGPL
36164  * <script type="text/javascript">
36165  */
36166  
36167
36168 /**
36169  * @class Roo.menu.ColorMenu
36170  * @extends Roo.menu.Menu
36171  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36172  * @constructor
36173  * Creates a new ColorMenu
36174  * @param {Object} config Configuration options
36175  */
36176 Roo.menu.ColorMenu = function(config){
36177     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36178     this.plain = true;
36179     var ci = new Roo.menu.ColorItem(config);
36180     this.add(ci);
36181     /**
36182      * The {@link Roo.ColorPalette} instance for this ColorMenu
36183      * @type ColorPalette
36184      */
36185     this.palette = ci.palette;
36186     /**
36187      * @event select
36188      * @param {ColorPalette} palette
36189      * @param {String} color
36190      */
36191     this.relayEvents(ci, ["select"]);
36192 };
36193 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36194  * Based on:
36195  * Ext JS Library 1.1.1
36196  * Copyright(c) 2006-2007, Ext JS, LLC.
36197  *
36198  * Originally Released Under LGPL - original licence link has changed is not relivant.
36199  *
36200  * Fork - LGPL
36201  * <script type="text/javascript">
36202  */
36203  
36204 /**
36205  * @class Roo.form.Field
36206  * @extends Roo.BoxComponent
36207  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36208  * @constructor
36209  * Creates a new Field
36210  * @param {Object} config Configuration options
36211  */
36212 Roo.form.Field = function(config){
36213     Roo.form.Field.superclass.constructor.call(this, config);
36214 };
36215
36216 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36217     /**
36218      * @cfg {String} fieldLabel Label to use when rendering a form.
36219      */
36220        /**
36221      * @cfg {String} qtip Mouse over tip
36222      */
36223      
36224     /**
36225      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36226      */
36227     invalidClass : "x-form-invalid",
36228     /**
36229      * @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")
36230      */
36231     invalidText : "The value in this field is invalid",
36232     /**
36233      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36234      */
36235     focusClass : "x-form-focus",
36236     /**
36237      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36238       automatic validation (defaults to "keyup").
36239      */
36240     validationEvent : "keyup",
36241     /**
36242      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36243      */
36244     validateOnBlur : true,
36245     /**
36246      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36247      */
36248     validationDelay : 250,
36249     /**
36250      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36251      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36252      */
36253     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36254     /**
36255      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36256      */
36257     fieldClass : "x-form-field",
36258     /**
36259      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36260      *<pre>
36261 Value         Description
36262 -----------   ----------------------------------------------------------------------
36263 qtip          Display a quick tip when the user hovers over the field
36264 title         Display a default browser title attribute popup
36265 under         Add a block div beneath the field containing the error text
36266 side          Add an error icon to the right of the field with a popup on hover
36267 [element id]  Add the error text directly to the innerHTML of the specified element
36268 </pre>
36269      */
36270     msgTarget : 'qtip',
36271     /**
36272      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36273      */
36274     msgFx : 'normal',
36275
36276     /**
36277      * @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.
36278      */
36279     readOnly : false,
36280
36281     /**
36282      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36283      */
36284     disabled : false,
36285
36286     /**
36287      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36288      */
36289     inputType : undefined,
36290     
36291     /**
36292      * @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).
36293          */
36294         tabIndex : undefined,
36295         
36296     // private
36297     isFormField : true,
36298
36299     // private
36300     hasFocus : false,
36301     /**
36302      * @property {Roo.Element} fieldEl
36303      * Element Containing the rendered Field (with label etc.)
36304      */
36305     /**
36306      * @cfg {Mixed} value A value to initialize this field with.
36307      */
36308     value : undefined,
36309
36310     /**
36311      * @cfg {String} name The field's HTML name attribute.
36312      */
36313     /**
36314      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36315      */
36316
36317         // private ??
36318         initComponent : function(){
36319         Roo.form.Field.superclass.initComponent.call(this);
36320         this.addEvents({
36321             /**
36322              * @event focus
36323              * Fires when this field receives input focus.
36324              * @param {Roo.form.Field} this
36325              */
36326             focus : true,
36327             /**
36328              * @event blur
36329              * Fires when this field loses input focus.
36330              * @param {Roo.form.Field} this
36331              */
36332             blur : true,
36333             /**
36334              * @event specialkey
36335              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36336              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36337              * @param {Roo.form.Field} this
36338              * @param {Roo.EventObject} e The event object
36339              */
36340             specialkey : true,
36341             /**
36342              * @event change
36343              * Fires just before the field blurs if the field value has changed.
36344              * @param {Roo.form.Field} this
36345              * @param {Mixed} newValue The new value
36346              * @param {Mixed} oldValue The original value
36347              */
36348             change : true,
36349             /**
36350              * @event invalid
36351              * Fires after the field has been marked as invalid.
36352              * @param {Roo.form.Field} this
36353              * @param {String} msg The validation message
36354              */
36355             invalid : true,
36356             /**
36357              * @event valid
36358              * Fires after the field has been validated with no errors.
36359              * @param {Roo.form.Field} this
36360              */
36361             valid : true,
36362              /**
36363              * @event keyup
36364              * Fires after the key up
36365              * @param {Roo.form.Field} this
36366              * @param {Roo.EventObject}  e The event Object
36367              */
36368             keyup : true
36369         });
36370     },
36371
36372     /**
36373      * Returns the name attribute of the field if available
36374      * @return {String} name The field name
36375      */
36376     getName: function(){
36377          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36378     },
36379
36380     // private
36381     onRender : function(ct, position){
36382         Roo.form.Field.superclass.onRender.call(this, ct, position);
36383         if(!this.el){
36384             var cfg = this.getAutoCreate();
36385             if(!cfg.name){
36386                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36387             }
36388             if (!cfg.name.length) {
36389                 delete cfg.name;
36390             }
36391             if(this.inputType){
36392                 cfg.type = this.inputType;
36393             }
36394             this.el = ct.createChild(cfg, position);
36395         }
36396         var type = this.el.dom.type;
36397         if(type){
36398             if(type == 'password'){
36399                 type = 'text';
36400             }
36401             this.el.addClass('x-form-'+type);
36402         }
36403         if(this.readOnly){
36404             this.el.dom.readOnly = true;
36405         }
36406         if(this.tabIndex !== undefined){
36407             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36408         }
36409
36410         this.el.addClass([this.fieldClass, this.cls]);
36411         this.initValue();
36412     },
36413
36414     /**
36415      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36416      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36417      * @return {Roo.form.Field} this
36418      */
36419     applyTo : function(target){
36420         this.allowDomMove = false;
36421         this.el = Roo.get(target);
36422         this.render(this.el.dom.parentNode);
36423         return this;
36424     },
36425
36426     // private
36427     initValue : function(){
36428         if(this.value !== undefined){
36429             this.setValue(this.value);
36430         }else if(this.el.dom.value.length > 0){
36431             this.setValue(this.el.dom.value);
36432         }
36433     },
36434
36435     /**
36436      * Returns true if this field has been changed since it was originally loaded and is not disabled.
36437      */
36438     isDirty : function() {
36439         if(this.disabled) {
36440             return false;
36441         }
36442         return String(this.getValue()) !== String(this.originalValue);
36443     },
36444
36445     // private
36446     afterRender : function(){
36447         Roo.form.Field.superclass.afterRender.call(this);
36448         this.initEvents();
36449     },
36450
36451     // private
36452     fireKey : function(e){
36453         //Roo.log('field ' + e.getKey());
36454         if(e.isNavKeyPress()){
36455             this.fireEvent("specialkey", this, e);
36456         }
36457     },
36458
36459     /**
36460      * Resets the current field value to the originally loaded value and clears any validation messages
36461      */
36462     reset : function(){
36463         this.setValue(this.originalValue);
36464         this.clearInvalid();
36465     },
36466
36467     // private
36468     initEvents : function(){
36469         // safari killled keypress - so keydown is now used..
36470         this.el.on("keydown" , this.fireKey,  this);
36471         this.el.on("focus", this.onFocus,  this);
36472         this.el.on("blur", this.onBlur,  this);
36473         this.el.relayEvent('keyup', this);
36474
36475         // reference to original value for reset
36476         this.originalValue = this.getValue();
36477     },
36478
36479     // private
36480     onFocus : function(){
36481         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36482             this.el.addClass(this.focusClass);
36483         }
36484         if(!this.hasFocus){
36485             this.hasFocus = true;
36486             this.startValue = this.getValue();
36487             this.fireEvent("focus", this);
36488         }
36489     },
36490
36491     beforeBlur : Roo.emptyFn,
36492
36493     // private
36494     onBlur : function(){
36495         this.beforeBlur();
36496         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36497             this.el.removeClass(this.focusClass);
36498         }
36499         this.hasFocus = false;
36500         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
36501             this.validate();
36502         }
36503         var v = this.getValue();
36504         if(String(v) !== String(this.startValue)){
36505             this.fireEvent('change', this, v, this.startValue);
36506         }
36507         this.fireEvent("blur", this);
36508     },
36509
36510     /**
36511      * Returns whether or not the field value is currently valid
36512      * @param {Boolean} preventMark True to disable marking the field invalid
36513      * @return {Boolean} True if the value is valid, else false
36514      */
36515     isValid : function(preventMark){
36516         if(this.disabled){
36517             return true;
36518         }
36519         var restore = this.preventMark;
36520         this.preventMark = preventMark === true;
36521         var v = this.validateValue(this.processValue(this.getRawValue()));
36522         this.preventMark = restore;
36523         return v;
36524     },
36525
36526     /**
36527      * Validates the field value
36528      * @return {Boolean} True if the value is valid, else false
36529      */
36530     validate : function(){
36531         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
36532             this.clearInvalid();
36533             return true;
36534         }
36535         return false;
36536     },
36537
36538     processValue : function(value){
36539         return value;
36540     },
36541
36542     // private
36543     // Subclasses should provide the validation implementation by overriding this
36544     validateValue : function(value){
36545         return true;
36546     },
36547
36548     /**
36549      * Mark this field as invalid
36550      * @param {String} msg The validation message
36551      */
36552     markInvalid : function(msg){
36553         if(!this.rendered || this.preventMark){ // not rendered
36554             return;
36555         }
36556         this.el.addClass(this.invalidClass);
36557         msg = msg || this.invalidText;
36558         switch(this.msgTarget){
36559             case 'qtip':
36560                 this.el.dom.qtip = msg;
36561                 this.el.dom.qclass = 'x-form-invalid-tip';
36562                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
36563                     Roo.QuickTips.enable();
36564                 }
36565                 break;
36566             case 'title':
36567                 this.el.dom.title = msg;
36568                 break;
36569             case 'under':
36570                 if(!this.errorEl){
36571                     var elp = this.el.findParent('.x-form-element', 5, true);
36572                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
36573                     this.errorEl.setWidth(elp.getWidth(true)-20);
36574                 }
36575                 this.errorEl.update(msg);
36576                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
36577                 break;
36578             case 'side':
36579                 if(!this.errorIcon){
36580                     var elp = this.el.findParent('.x-form-element', 5, true);
36581                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
36582                 }
36583                 this.alignErrorIcon();
36584                 this.errorIcon.dom.qtip = msg;
36585                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36586                 this.errorIcon.show();
36587                 this.on('resize', this.alignErrorIcon, this);
36588                 break;
36589             default:
36590                 var t = Roo.getDom(this.msgTarget);
36591                 t.innerHTML = msg;
36592                 t.style.display = this.msgDisplay;
36593                 break;
36594         }
36595         this.fireEvent('invalid', this, msg);
36596     },
36597
36598     // private
36599     alignErrorIcon : function(){
36600         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36601     },
36602
36603     /**
36604      * Clear any invalid styles/messages for this field
36605      */
36606     clearInvalid : function(){
36607         if(!this.rendered || this.preventMark){ // not rendered
36608             return;
36609         }
36610         this.el.removeClass(this.invalidClass);
36611         switch(this.msgTarget){
36612             case 'qtip':
36613                 this.el.dom.qtip = '';
36614                 break;
36615             case 'title':
36616                 this.el.dom.title = '';
36617                 break;
36618             case 'under':
36619                 if(this.errorEl){
36620                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36621                 }
36622                 break;
36623             case 'side':
36624                 if(this.errorIcon){
36625                     this.errorIcon.dom.qtip = '';
36626                     this.errorIcon.hide();
36627                     this.un('resize', this.alignErrorIcon, this);
36628                 }
36629                 break;
36630             default:
36631                 var t = Roo.getDom(this.msgTarget);
36632                 t.innerHTML = '';
36633                 t.style.display = 'none';
36634                 break;
36635         }
36636         this.fireEvent('valid', this);
36637     },
36638
36639     /**
36640      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36641      * @return {Mixed} value The field value
36642      */
36643     getRawValue : function(){
36644         var v = this.el.getValue();
36645         
36646         return v;
36647     },
36648
36649     /**
36650      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36651      * @return {Mixed} value The field value
36652      */
36653     getValue : function(){
36654         var v = this.el.getValue();
36655          
36656         return v;
36657     },
36658
36659     /**
36660      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36661      * @param {Mixed} value The value to set
36662      */
36663     setRawValue : function(v){
36664         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36665     },
36666
36667     /**
36668      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36669      * @param {Mixed} value The value to set
36670      */
36671     setValue : function(v){
36672         this.value = v;
36673         if(this.rendered){
36674             this.el.dom.value = (v === null || v === undefined ? '' : v);
36675              this.validate();
36676         }
36677     },
36678
36679     adjustSize : function(w, h){
36680         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36681         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36682         return s;
36683     },
36684
36685     adjustWidth : function(tag, w){
36686         tag = tag.toLowerCase();
36687         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36688             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36689                 if(tag == 'input'){
36690                     return w + 2;
36691                 }
36692                 if(tag == 'textarea'){
36693                     return w-2;
36694                 }
36695             }else if(Roo.isOpera){
36696                 if(tag == 'input'){
36697                     return w + 2;
36698                 }
36699                 if(tag == 'textarea'){
36700                     return w-2;
36701                 }
36702             }
36703         }
36704         return w;
36705     }
36706 });
36707
36708
36709 // anything other than normal should be considered experimental
36710 Roo.form.Field.msgFx = {
36711     normal : {
36712         show: function(msgEl, f){
36713             msgEl.setDisplayed('block');
36714         },
36715
36716         hide : function(msgEl, f){
36717             msgEl.setDisplayed(false).update('');
36718         }
36719     },
36720
36721     slide : {
36722         show: function(msgEl, f){
36723             msgEl.slideIn('t', {stopFx:true});
36724         },
36725
36726         hide : function(msgEl, f){
36727             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36728         }
36729     },
36730
36731     slideRight : {
36732         show: function(msgEl, f){
36733             msgEl.fixDisplay();
36734             msgEl.alignTo(f.el, 'tl-tr');
36735             msgEl.slideIn('l', {stopFx:true});
36736         },
36737
36738         hide : function(msgEl, f){
36739             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36740         }
36741     }
36742 };/*
36743  * Based on:
36744  * Ext JS Library 1.1.1
36745  * Copyright(c) 2006-2007, Ext JS, LLC.
36746  *
36747  * Originally Released Under LGPL - original licence link has changed is not relivant.
36748  *
36749  * Fork - LGPL
36750  * <script type="text/javascript">
36751  */
36752  
36753
36754 /**
36755  * @class Roo.form.TextField
36756  * @extends Roo.form.Field
36757  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36758  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36759  * @constructor
36760  * Creates a new TextField
36761  * @param {Object} config Configuration options
36762  */
36763 Roo.form.TextField = function(config){
36764     Roo.form.TextField.superclass.constructor.call(this, config);
36765     this.addEvents({
36766         /**
36767          * @event autosize
36768          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36769          * according to the default logic, but this event provides a hook for the developer to apply additional
36770          * logic at runtime to resize the field if needed.
36771              * @param {Roo.form.Field} this This text field
36772              * @param {Number} width The new field width
36773              */
36774         autosize : true
36775     });
36776 };
36777
36778 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36779     /**
36780      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36781      */
36782     grow : false,
36783     /**
36784      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36785      */
36786     growMin : 30,
36787     /**
36788      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36789      */
36790     growMax : 800,
36791     /**
36792      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36793      */
36794     vtype : null,
36795     /**
36796      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36797      */
36798     maskRe : null,
36799     /**
36800      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36801      */
36802     disableKeyFilter : false,
36803     /**
36804      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36805      */
36806     allowBlank : true,
36807     /**
36808      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36809      */
36810     minLength : 0,
36811     /**
36812      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36813      */
36814     maxLength : Number.MAX_VALUE,
36815     /**
36816      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36817      */
36818     minLengthText : "The minimum length for this field is {0}",
36819     /**
36820      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36821      */
36822     maxLengthText : "The maximum length for this field is {0}",
36823     /**
36824      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36825      */
36826     selectOnFocus : false,
36827     /**
36828      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36829      */
36830     blankText : "This field is required",
36831     /**
36832      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36833      * If available, this function will be called only after the basic validators all return true, and will be passed the
36834      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36835      */
36836     validator : null,
36837     /**
36838      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36839      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36840      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36841      */
36842     regex : null,
36843     /**
36844      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36845      */
36846     regexText : "",
36847     /**
36848      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
36849      */
36850     emptyText : null,
36851    
36852
36853     // private
36854     initEvents : function()
36855     {
36856         if (this.emptyText) {
36857             this.el.attr('placeholder', this.emptyText);
36858         }
36859         
36860         Roo.form.TextField.superclass.initEvents.call(this);
36861         if(this.validationEvent == 'keyup'){
36862             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36863             this.el.on('keyup', this.filterValidation, this);
36864         }
36865         else if(this.validationEvent !== false){
36866             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36867         }
36868         
36869         if(this.selectOnFocus){
36870             this.on("focus", this.preFocus, this);
36871             
36872         }
36873         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36874             this.el.on("keypress", this.filterKeys, this);
36875         }
36876         if(this.grow){
36877             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36878             this.el.on("click", this.autoSize,  this);
36879         }
36880         if(this.el.is('input[type=password]') && Roo.isSafari){
36881             this.el.on('keydown', this.SafariOnKeyDown, this);
36882         }
36883     },
36884
36885     processValue : function(value){
36886         if(this.stripCharsRe){
36887             var newValue = value.replace(this.stripCharsRe, '');
36888             if(newValue !== value){
36889                 this.setRawValue(newValue);
36890                 return newValue;
36891             }
36892         }
36893         return value;
36894     },
36895
36896     filterValidation : function(e){
36897         if(!e.isNavKeyPress()){
36898             this.validationTask.delay(this.validationDelay);
36899         }
36900     },
36901
36902     // private
36903     onKeyUp : function(e){
36904         if(!e.isNavKeyPress()){
36905             this.autoSize();
36906         }
36907     },
36908
36909     /**
36910      * Resets the current field value to the originally-loaded value and clears any validation messages.
36911      *  
36912      */
36913     reset : function(){
36914         Roo.form.TextField.superclass.reset.call(this);
36915        
36916     },
36917
36918     
36919     // private
36920     preFocus : function(){
36921         
36922         if(this.selectOnFocus){
36923             this.el.dom.select();
36924         }
36925     },
36926
36927     
36928     // private
36929     filterKeys : function(e){
36930         var k = e.getKey();
36931         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36932             return;
36933         }
36934         var c = e.getCharCode(), cc = String.fromCharCode(c);
36935         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36936             return;
36937         }
36938         if(!this.maskRe.test(cc)){
36939             e.stopEvent();
36940         }
36941     },
36942
36943     setValue : function(v){
36944         
36945         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36946         
36947         this.autoSize();
36948     },
36949
36950     /**
36951      * Validates a value according to the field's validation rules and marks the field as invalid
36952      * if the validation fails
36953      * @param {Mixed} value The value to validate
36954      * @return {Boolean} True if the value is valid, else false
36955      */
36956     validateValue : function(value){
36957         if(value.length < 1)  { // if it's blank
36958              if(this.allowBlank){
36959                 this.clearInvalid();
36960                 return true;
36961              }else{
36962                 this.markInvalid(this.blankText);
36963                 return false;
36964              }
36965         }
36966         if(value.length < this.minLength){
36967             this.markInvalid(String.format(this.minLengthText, this.minLength));
36968             return false;
36969         }
36970         if(value.length > this.maxLength){
36971             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36972             return false;
36973         }
36974         if(this.vtype){
36975             var vt = Roo.form.VTypes;
36976             if(!vt[this.vtype](value, this)){
36977                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36978                 return false;
36979             }
36980         }
36981         if(typeof this.validator == "function"){
36982             var msg = this.validator(value);
36983             if(msg !== true){
36984                 this.markInvalid(msg);
36985                 return false;
36986             }
36987         }
36988         if(this.regex && !this.regex.test(value)){
36989             this.markInvalid(this.regexText);
36990             return false;
36991         }
36992         return true;
36993     },
36994
36995     /**
36996      * Selects text in this field
36997      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36998      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36999      */
37000     selectText : function(start, end){
37001         var v = this.getRawValue();
37002         if(v.length > 0){
37003             start = start === undefined ? 0 : start;
37004             end = end === undefined ? v.length : end;
37005             var d = this.el.dom;
37006             if(d.setSelectionRange){
37007                 d.setSelectionRange(start, end);
37008             }else if(d.createTextRange){
37009                 var range = d.createTextRange();
37010                 range.moveStart("character", start);
37011                 range.moveEnd("character", v.length-end);
37012                 range.select();
37013             }
37014         }
37015     },
37016
37017     /**
37018      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37019      * This only takes effect if grow = true, and fires the autosize event.
37020      */
37021     autoSize : function(){
37022         if(!this.grow || !this.rendered){
37023             return;
37024         }
37025         if(!this.metrics){
37026             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37027         }
37028         var el = this.el;
37029         var v = el.dom.value;
37030         var d = document.createElement('div');
37031         d.appendChild(document.createTextNode(v));
37032         v = d.innerHTML;
37033         d = null;
37034         v += "&#160;";
37035         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37036         this.el.setWidth(w);
37037         this.fireEvent("autosize", this, w);
37038     },
37039     
37040     // private
37041     SafariOnKeyDown : function(event)
37042     {
37043         // this is a workaround for a password hang bug on chrome/ webkit.
37044         
37045         var isSelectAll = false;
37046         
37047         if(this.el.dom.selectionEnd > 0){
37048             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37049         }
37050         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37051             event.preventDefault();
37052             this.setValue('');
37053             return;
37054         }
37055         
37056         if(isSelectAll){ // backspace and delete key
37057             
37058             event.preventDefault();
37059             // this is very hacky as keydown always get's upper case.
37060             //
37061             var cc = String.fromCharCode(event.getCharCode());
37062             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37063             
37064         }
37065         
37066         
37067     }
37068 });/*
37069  * Based on:
37070  * Ext JS Library 1.1.1
37071  * Copyright(c) 2006-2007, Ext JS, LLC.
37072  *
37073  * Originally Released Under LGPL - original licence link has changed is not relivant.
37074  *
37075  * Fork - LGPL
37076  * <script type="text/javascript">
37077  */
37078  
37079 /**
37080  * @class Roo.form.Hidden
37081  * @extends Roo.form.TextField
37082  * Simple Hidden element used on forms 
37083  * 
37084  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37085  * 
37086  * @constructor
37087  * Creates a new Hidden form element.
37088  * @param {Object} config Configuration options
37089  */
37090
37091
37092
37093 // easy hidden field...
37094 Roo.form.Hidden = function(config){
37095     Roo.form.Hidden.superclass.constructor.call(this, config);
37096 };
37097   
37098 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37099     fieldLabel:      '',
37100     inputType:      'hidden',
37101     width:          50,
37102     allowBlank:     true,
37103     labelSeparator: '',
37104     hidden:         true,
37105     itemCls :       'x-form-item-display-none'
37106
37107
37108 });
37109
37110
37111 /*
37112  * Based on:
37113  * Ext JS Library 1.1.1
37114  * Copyright(c) 2006-2007, Ext JS, LLC.
37115  *
37116  * Originally Released Under LGPL - original licence link has changed is not relivant.
37117  *
37118  * Fork - LGPL
37119  * <script type="text/javascript">
37120  */
37121  
37122 /**
37123  * @class Roo.form.TriggerField
37124  * @extends Roo.form.TextField
37125  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37126  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37127  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37128  * for which you can provide a custom implementation.  For example:
37129  * <pre><code>
37130 var trigger = new Roo.form.TriggerField();
37131 trigger.onTriggerClick = myTriggerFn;
37132 trigger.applyTo('my-field');
37133 </code></pre>
37134  *
37135  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37136  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37137  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37138  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37139  * @constructor
37140  * Create a new TriggerField.
37141  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37142  * to the base TextField)
37143  */
37144 Roo.form.TriggerField = function(config){
37145     this.mimicing = false;
37146     Roo.form.TriggerField.superclass.constructor.call(this, config);
37147 };
37148
37149 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37150     /**
37151      * @cfg {String} triggerClass A CSS class to apply to the trigger
37152      */
37153     /**
37154      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37155      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37156      */
37157     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37158     /**
37159      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37160      */
37161     hideTrigger:false,
37162
37163     /** @cfg {Boolean} grow @hide */
37164     /** @cfg {Number} growMin @hide */
37165     /** @cfg {Number} growMax @hide */
37166
37167     /**
37168      * @hide 
37169      * @method
37170      */
37171     autoSize: Roo.emptyFn,
37172     // private
37173     monitorTab : true,
37174     // private
37175     deferHeight : true,
37176
37177     
37178     actionMode : 'wrap',
37179     // private
37180     onResize : function(w, h){
37181         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37182         if(typeof w == 'number'){
37183             var x = w - this.trigger.getWidth();
37184             this.el.setWidth(this.adjustWidth('input', x));
37185             this.trigger.setStyle('left', x+'px');
37186         }
37187     },
37188
37189     // private
37190     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37191
37192     // private
37193     getResizeEl : function(){
37194         return this.wrap;
37195     },
37196
37197     // private
37198     getPositionEl : function(){
37199         return this.wrap;
37200     },
37201
37202     // private
37203     alignErrorIcon : function(){
37204         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37205     },
37206
37207     // private
37208     onRender : function(ct, position){
37209         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37210         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37211         this.trigger = this.wrap.createChild(this.triggerConfig ||
37212                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37213         if(this.hideTrigger){
37214             this.trigger.setDisplayed(false);
37215         }
37216         this.initTrigger();
37217         if(!this.width){
37218             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37219         }
37220     },
37221
37222     // private
37223     initTrigger : function(){
37224         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37225         this.trigger.addClassOnOver('x-form-trigger-over');
37226         this.trigger.addClassOnClick('x-form-trigger-click');
37227     },
37228
37229     // private
37230     onDestroy : function(){
37231         if(this.trigger){
37232             this.trigger.removeAllListeners();
37233             this.trigger.remove();
37234         }
37235         if(this.wrap){
37236             this.wrap.remove();
37237         }
37238         Roo.form.TriggerField.superclass.onDestroy.call(this);
37239     },
37240
37241     // private
37242     onFocus : function(){
37243         Roo.form.TriggerField.superclass.onFocus.call(this);
37244         if(!this.mimicing){
37245             this.wrap.addClass('x-trigger-wrap-focus');
37246             this.mimicing = true;
37247             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37248             if(this.monitorTab){
37249                 this.el.on("keydown", this.checkTab, this);
37250             }
37251         }
37252     },
37253
37254     // private
37255     checkTab : function(e){
37256         if(e.getKey() == e.TAB){
37257             this.triggerBlur();
37258         }
37259     },
37260
37261     // private
37262     onBlur : function(){
37263         // do nothing
37264     },
37265
37266     // private
37267     mimicBlur : function(e, t){
37268         if(!this.wrap.contains(t) && this.validateBlur()){
37269             this.triggerBlur();
37270         }
37271     },
37272
37273     // private
37274     triggerBlur : function(){
37275         this.mimicing = false;
37276         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37277         if(this.monitorTab){
37278             this.el.un("keydown", this.checkTab, this);
37279         }
37280         this.wrap.removeClass('x-trigger-wrap-focus');
37281         Roo.form.TriggerField.superclass.onBlur.call(this);
37282     },
37283
37284     // private
37285     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37286     validateBlur : function(e, t){
37287         return true;
37288     },
37289
37290     // private
37291     onDisable : function(){
37292         Roo.form.TriggerField.superclass.onDisable.call(this);
37293         if(this.wrap){
37294             this.wrap.addClass('x-item-disabled');
37295         }
37296     },
37297
37298     // private
37299     onEnable : function(){
37300         Roo.form.TriggerField.superclass.onEnable.call(this);
37301         if(this.wrap){
37302             this.wrap.removeClass('x-item-disabled');
37303         }
37304     },
37305
37306     // private
37307     onShow : function(){
37308         var ae = this.getActionEl();
37309         
37310         if(ae){
37311             ae.dom.style.display = '';
37312             ae.dom.style.visibility = 'visible';
37313         }
37314     },
37315
37316     // private
37317     
37318     onHide : function(){
37319         var ae = this.getActionEl();
37320         ae.dom.style.display = 'none';
37321     },
37322
37323     /**
37324      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37325      * by an implementing function.
37326      * @method
37327      * @param {EventObject} e
37328      */
37329     onTriggerClick : Roo.emptyFn
37330 });
37331
37332 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37333 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37334 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37335 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37336     initComponent : function(){
37337         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37338
37339         this.triggerConfig = {
37340             tag:'span', cls:'x-form-twin-triggers', cn:[
37341             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37342             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37343         ]};
37344     },
37345
37346     getTrigger : function(index){
37347         return this.triggers[index];
37348     },
37349
37350     initTrigger : function(){
37351         var ts = this.trigger.select('.x-form-trigger', true);
37352         this.wrap.setStyle('overflow', 'hidden');
37353         var triggerField = this;
37354         ts.each(function(t, all, index){
37355             t.hide = function(){
37356                 var w = triggerField.wrap.getWidth();
37357                 this.dom.style.display = 'none';
37358                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37359             };
37360             t.show = function(){
37361                 var w = triggerField.wrap.getWidth();
37362                 this.dom.style.display = '';
37363                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37364             };
37365             var triggerIndex = 'Trigger'+(index+1);
37366
37367             if(this['hide'+triggerIndex]){
37368                 t.dom.style.display = 'none';
37369             }
37370             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37371             t.addClassOnOver('x-form-trigger-over');
37372             t.addClassOnClick('x-form-trigger-click');
37373         }, this);
37374         this.triggers = ts.elements;
37375     },
37376
37377     onTrigger1Click : Roo.emptyFn,
37378     onTrigger2Click : Roo.emptyFn
37379 });/*
37380  * Based on:
37381  * Ext JS Library 1.1.1
37382  * Copyright(c) 2006-2007, Ext JS, LLC.
37383  *
37384  * Originally Released Under LGPL - original licence link has changed is not relivant.
37385  *
37386  * Fork - LGPL
37387  * <script type="text/javascript">
37388  */
37389  
37390 /**
37391  * @class Roo.form.TextArea
37392  * @extends Roo.form.TextField
37393  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37394  * support for auto-sizing.
37395  * @constructor
37396  * Creates a new TextArea
37397  * @param {Object} config Configuration options
37398  */
37399 Roo.form.TextArea = function(config){
37400     Roo.form.TextArea.superclass.constructor.call(this, config);
37401     // these are provided exchanges for backwards compat
37402     // minHeight/maxHeight were replaced by growMin/growMax to be
37403     // compatible with TextField growing config values
37404     if(this.minHeight !== undefined){
37405         this.growMin = this.minHeight;
37406     }
37407     if(this.maxHeight !== undefined){
37408         this.growMax = this.maxHeight;
37409     }
37410 };
37411
37412 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37413     /**
37414      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37415      */
37416     growMin : 60,
37417     /**
37418      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37419      */
37420     growMax: 1000,
37421     /**
37422      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37423      * in the field (equivalent to setting overflow: hidden, defaults to false)
37424      */
37425     preventScrollbars: false,
37426     /**
37427      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37428      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
37429      */
37430
37431     // private
37432     onRender : function(ct, position){
37433         if(!this.el){
37434             this.defaultAutoCreate = {
37435                 tag: "textarea",
37436                 style:"width:300px;height:60px;",
37437                 autocomplete: "off"
37438             };
37439         }
37440         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
37441         if(this.grow){
37442             this.textSizeEl = Roo.DomHelper.append(document.body, {
37443                 tag: "pre", cls: "x-form-grow-sizer"
37444             });
37445             if(this.preventScrollbars){
37446                 this.el.setStyle("overflow", "hidden");
37447             }
37448             this.el.setHeight(this.growMin);
37449         }
37450     },
37451
37452     onDestroy : function(){
37453         if(this.textSizeEl){
37454             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
37455         }
37456         Roo.form.TextArea.superclass.onDestroy.call(this);
37457     },
37458
37459     // private
37460     onKeyUp : function(e){
37461         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
37462             this.autoSize();
37463         }
37464     },
37465
37466     /**
37467      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
37468      * This only takes effect if grow = true, and fires the autosize event if the height changes.
37469      */
37470     autoSize : function(){
37471         if(!this.grow || !this.textSizeEl){
37472             return;
37473         }
37474         var el = this.el;
37475         var v = el.dom.value;
37476         var ts = this.textSizeEl;
37477
37478         ts.innerHTML = '';
37479         ts.appendChild(document.createTextNode(v));
37480         v = ts.innerHTML;
37481
37482         Roo.fly(ts).setWidth(this.el.getWidth());
37483         if(v.length < 1){
37484             v = "&#160;&#160;";
37485         }else{
37486             if(Roo.isIE){
37487                 v = v.replace(/\n/g, '<p>&#160;</p>');
37488             }
37489             v += "&#160;\n&#160;";
37490         }
37491         ts.innerHTML = v;
37492         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
37493         if(h != this.lastHeight){
37494             this.lastHeight = h;
37495             this.el.setHeight(h);
37496             this.fireEvent("autosize", this, h);
37497         }
37498     }
37499 });/*
37500  * Based on:
37501  * Ext JS Library 1.1.1
37502  * Copyright(c) 2006-2007, Ext JS, LLC.
37503  *
37504  * Originally Released Under LGPL - original licence link has changed is not relivant.
37505  *
37506  * Fork - LGPL
37507  * <script type="text/javascript">
37508  */
37509  
37510
37511 /**
37512  * @class Roo.form.NumberField
37513  * @extends Roo.form.TextField
37514  * Numeric text field that provides automatic keystroke filtering and numeric validation.
37515  * @constructor
37516  * Creates a new NumberField
37517  * @param {Object} config Configuration options
37518  */
37519 Roo.form.NumberField = function(config){
37520     Roo.form.NumberField.superclass.constructor.call(this, config);
37521 };
37522
37523 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
37524     /**
37525      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
37526      */
37527     fieldClass: "x-form-field x-form-num-field",
37528     /**
37529      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
37530      */
37531     allowDecimals : true,
37532     /**
37533      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
37534      */
37535     decimalSeparator : ".",
37536     /**
37537      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37538      */
37539     decimalPrecision : 2,
37540     /**
37541      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37542      */
37543     allowNegative : true,
37544     /**
37545      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37546      */
37547     minValue : Number.NEGATIVE_INFINITY,
37548     /**
37549      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37550      */
37551     maxValue : Number.MAX_VALUE,
37552     /**
37553      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37554      */
37555     minText : "The minimum value for this field is {0}",
37556     /**
37557      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37558      */
37559     maxText : "The maximum value for this field is {0}",
37560     /**
37561      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37562      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37563      */
37564     nanText : "{0} is not a valid number",
37565
37566     // private
37567     initEvents : function(){
37568         Roo.form.NumberField.superclass.initEvents.call(this);
37569         var allowed = "0123456789";
37570         if(this.allowDecimals){
37571             allowed += this.decimalSeparator;
37572         }
37573         if(this.allowNegative){
37574             allowed += "-";
37575         }
37576         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37577         var keyPress = function(e){
37578             var k = e.getKey();
37579             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37580                 return;
37581             }
37582             var c = e.getCharCode();
37583             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37584                 e.stopEvent();
37585             }
37586         };
37587         this.el.on("keypress", keyPress, this);
37588     },
37589
37590     // private
37591     validateValue : function(value){
37592         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
37593             return false;
37594         }
37595         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37596              return true;
37597         }
37598         var num = this.parseValue(value);
37599         if(isNaN(num)){
37600             this.markInvalid(String.format(this.nanText, value));
37601             return false;
37602         }
37603         if(num < this.minValue){
37604             this.markInvalid(String.format(this.minText, this.minValue));
37605             return false;
37606         }
37607         if(num > this.maxValue){
37608             this.markInvalid(String.format(this.maxText, this.maxValue));
37609             return false;
37610         }
37611         return true;
37612     },
37613
37614     getValue : function(){
37615         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37616     },
37617
37618     // private
37619     parseValue : function(value){
37620         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37621         return isNaN(value) ? '' : value;
37622     },
37623
37624     // private
37625     fixPrecision : function(value){
37626         var nan = isNaN(value);
37627         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37628             return nan ? '' : value;
37629         }
37630         return parseFloat(value).toFixed(this.decimalPrecision);
37631     },
37632
37633     setValue : function(v){
37634         v = this.fixPrecision(v);
37635         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37636     },
37637
37638     // private
37639     decimalPrecisionFcn : function(v){
37640         return Math.floor(v);
37641     },
37642
37643     beforeBlur : function(){
37644         var v = this.parseValue(this.getRawValue());
37645         if(v){
37646             this.setValue(v);
37647         }
37648     }
37649 });/*
37650  * Based on:
37651  * Ext JS Library 1.1.1
37652  * Copyright(c) 2006-2007, Ext JS, LLC.
37653  *
37654  * Originally Released Under LGPL - original licence link has changed is not relivant.
37655  *
37656  * Fork - LGPL
37657  * <script type="text/javascript">
37658  */
37659  
37660 /**
37661  * @class Roo.form.DateField
37662  * @extends Roo.form.TriggerField
37663  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37664 * @constructor
37665 * Create a new DateField
37666 * @param {Object} config
37667  */
37668 Roo.form.DateField = function(config){
37669     Roo.form.DateField.superclass.constructor.call(this, config);
37670     
37671       this.addEvents({
37672          
37673         /**
37674          * @event select
37675          * Fires when a date is selected
37676              * @param {Roo.form.DateField} combo This combo box
37677              * @param {Date} date The date selected
37678              */
37679         'select' : true
37680          
37681     });
37682     
37683     
37684     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37685     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37686     this.ddMatch = null;
37687     if(this.disabledDates){
37688         var dd = this.disabledDates;
37689         var re = "(?:";
37690         for(var i = 0; i < dd.length; i++){
37691             re += dd[i];
37692             if(i != dd.length-1) re += "|";
37693         }
37694         this.ddMatch = new RegExp(re + ")");
37695     }
37696 };
37697
37698 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37699     /**
37700      * @cfg {String} format
37701      * The default date format string which can be overriden for localization support.  The format must be
37702      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37703      */
37704     format : "m/d/y",
37705     /**
37706      * @cfg {String} altFormats
37707      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37708      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37709      */
37710     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37711     /**
37712      * @cfg {Array} disabledDays
37713      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37714      */
37715     disabledDays : null,
37716     /**
37717      * @cfg {String} disabledDaysText
37718      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37719      */
37720     disabledDaysText : "Disabled",
37721     /**
37722      * @cfg {Array} disabledDates
37723      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37724      * expression so they are very powerful. Some examples:
37725      * <ul>
37726      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37727      * <li>["03/08", "09/16"] would disable those days for every year</li>
37728      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37729      * <li>["03/../2006"] would disable every day in March 2006</li>
37730      * <li>["^03"] would disable every day in every March</li>
37731      * </ul>
37732      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37733      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37734      */
37735     disabledDates : null,
37736     /**
37737      * @cfg {String} disabledDatesText
37738      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37739      */
37740     disabledDatesText : "Disabled",
37741     /**
37742      * @cfg {Date/String} minValue
37743      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37744      * valid format (defaults to null).
37745      */
37746     minValue : null,
37747     /**
37748      * @cfg {Date/String} maxValue
37749      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37750      * valid format (defaults to null).
37751      */
37752     maxValue : null,
37753     /**
37754      * @cfg {String} minText
37755      * The error text to display when the date in the cell is before minValue (defaults to
37756      * 'The date in this field must be after {minValue}').
37757      */
37758     minText : "The date in this field must be equal to or after {0}",
37759     /**
37760      * @cfg {String} maxText
37761      * The error text to display when the date in the cell is after maxValue (defaults to
37762      * 'The date in this field must be before {maxValue}').
37763      */
37764     maxText : "The date in this field must be equal to or before {0}",
37765     /**
37766      * @cfg {String} invalidText
37767      * The error text to display when the date in the field is invalid (defaults to
37768      * '{value} is not a valid date - it must be in the format {format}').
37769      */
37770     invalidText : "{0} is not a valid date - it must be in the format {1}",
37771     /**
37772      * @cfg {String} triggerClass
37773      * An additional CSS class used to style the trigger button.  The trigger will always get the
37774      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37775      * which displays a calendar icon).
37776      */
37777     triggerClass : 'x-form-date-trigger',
37778     
37779
37780     /**
37781      * @cfg {Boolean} useIso
37782      * if enabled, then the date field will use a hidden field to store the 
37783      * real value as iso formated date. default (false)
37784      */ 
37785     useIso : false,
37786     /**
37787      * @cfg {String/Object} autoCreate
37788      * A DomHelper element spec, or true for a default element spec (defaults to
37789      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37790      */ 
37791     // private
37792     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37793     
37794     // private
37795     hiddenField: false,
37796     
37797     onRender : function(ct, position)
37798     {
37799         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37800         if (this.useIso) {
37801             //this.el.dom.removeAttribute('name'); 
37802             Roo.log("Changing name?");
37803             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
37804             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37805                     'before', true);
37806             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
37807             // prevent input submission
37808             this.hiddenName = this.name;
37809         }
37810             
37811             
37812     },
37813     
37814     // private
37815     validateValue : function(value)
37816     {
37817         value = this.formatDate(value);
37818         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37819             Roo.log('super failed');
37820             return false;
37821         }
37822         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37823              return true;
37824         }
37825         var svalue = value;
37826         value = this.parseDate(value);
37827         if(!value){
37828             Roo.log('parse date failed' + svalue);
37829             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37830             return false;
37831         }
37832         var time = value.getTime();
37833         if(this.minValue && time < this.minValue.getTime()){
37834             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37835             return false;
37836         }
37837         if(this.maxValue && time > this.maxValue.getTime()){
37838             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37839             return false;
37840         }
37841         if(this.disabledDays){
37842             var day = value.getDay();
37843             for(var i = 0; i < this.disabledDays.length; i++) {
37844                 if(day === this.disabledDays[i]){
37845                     this.markInvalid(this.disabledDaysText);
37846                     return false;
37847                 }
37848             }
37849         }
37850         var fvalue = this.formatDate(value);
37851         if(this.ddMatch && this.ddMatch.test(fvalue)){
37852             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37853             return false;
37854         }
37855         return true;
37856     },
37857
37858     // private
37859     // Provides logic to override the default TriggerField.validateBlur which just returns true
37860     validateBlur : function(){
37861         return !this.menu || !this.menu.isVisible();
37862     },
37863     
37864     getName: function()
37865     {
37866         // returns hidden if it's set..
37867         if (!this.rendered) {return ''};
37868         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37869         
37870     },
37871
37872     /**
37873      * Returns the current date value of the date field.
37874      * @return {Date} The date value
37875      */
37876     getValue : function(){
37877         
37878         return  this.hiddenField ?
37879                 this.hiddenField.value :
37880                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37881     },
37882
37883     /**
37884      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37885      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37886      * (the default format used is "m/d/y").
37887      * <br />Usage:
37888      * <pre><code>
37889 //All of these calls set the same date value (May 4, 2006)
37890
37891 //Pass a date object:
37892 var dt = new Date('5/4/06');
37893 dateField.setValue(dt);
37894
37895 //Pass a date string (default format):
37896 dateField.setValue('5/4/06');
37897
37898 //Pass a date string (custom format):
37899 dateField.format = 'Y-m-d';
37900 dateField.setValue('2006-5-4');
37901 </code></pre>
37902      * @param {String/Date} date The date or valid date string
37903      */
37904     setValue : function(date){
37905         if (this.hiddenField) {
37906             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37907         }
37908         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37909         // make sure the value field is always stored as a date..
37910         this.value = this.parseDate(date);
37911         
37912         
37913     },
37914
37915     // private
37916     parseDate : function(value){
37917         if(!value || value instanceof Date){
37918             return value;
37919         }
37920         var v = Date.parseDate(value, this.format);
37921          if (!v && this.useIso) {
37922             v = Date.parseDate(value, 'Y-m-d');
37923         }
37924         if(!v && this.altFormats){
37925             if(!this.altFormatsArray){
37926                 this.altFormatsArray = this.altFormats.split("|");
37927             }
37928             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37929                 v = Date.parseDate(value, this.altFormatsArray[i]);
37930             }
37931         }
37932         return v;
37933     },
37934
37935     // private
37936     formatDate : function(date, fmt){
37937         return (!date || !(date instanceof Date)) ?
37938                date : date.dateFormat(fmt || this.format);
37939     },
37940
37941     // private
37942     menuListeners : {
37943         select: function(m, d){
37944             
37945             this.setValue(d);
37946             this.fireEvent('select', this, d);
37947         },
37948         show : function(){ // retain focus styling
37949             this.onFocus();
37950         },
37951         hide : function(){
37952             this.focus.defer(10, this);
37953             var ml = this.menuListeners;
37954             this.menu.un("select", ml.select,  this);
37955             this.menu.un("show", ml.show,  this);
37956             this.menu.un("hide", ml.hide,  this);
37957         }
37958     },
37959
37960     // private
37961     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37962     onTriggerClick : function(){
37963         if(this.disabled){
37964             return;
37965         }
37966         if(this.menu == null){
37967             this.menu = new Roo.menu.DateMenu();
37968         }
37969         Roo.apply(this.menu.picker,  {
37970             showClear: this.allowBlank,
37971             minDate : this.minValue,
37972             maxDate : this.maxValue,
37973             disabledDatesRE : this.ddMatch,
37974             disabledDatesText : this.disabledDatesText,
37975             disabledDays : this.disabledDays,
37976             disabledDaysText : this.disabledDaysText,
37977             format : this.useIso ? 'Y-m-d' : this.format,
37978             minText : String.format(this.minText, this.formatDate(this.minValue)),
37979             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37980         });
37981         this.menu.on(Roo.apply({}, this.menuListeners, {
37982             scope:this
37983         }));
37984         this.menu.picker.setValue(this.getValue() || new Date());
37985         this.menu.show(this.el, "tl-bl?");
37986     },
37987
37988     beforeBlur : function(){
37989         var v = this.parseDate(this.getRawValue());
37990         if(v){
37991             this.setValue(v);
37992         }
37993     }
37994
37995     /** @cfg {Boolean} grow @hide */
37996     /** @cfg {Number} growMin @hide */
37997     /** @cfg {Number} growMax @hide */
37998     /**
37999      * @hide
38000      * @method autoSize
38001      */
38002 });/*
38003  * Based on:
38004  * Ext JS Library 1.1.1
38005  * Copyright(c) 2006-2007, Ext JS, LLC.
38006  *
38007  * Originally Released Under LGPL - original licence link has changed is not relivant.
38008  *
38009  * Fork - LGPL
38010  * <script type="text/javascript">
38011  */
38012  
38013 /**
38014  * @class Roo.form.MonthField
38015  * @extends Roo.form.TriggerField
38016  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38017 * @constructor
38018 * Create a new MonthField
38019 * @param {Object} config
38020  */
38021 Roo.form.MonthField = function(config){
38022     
38023     Roo.form.MonthField.superclass.constructor.call(this, config);
38024     
38025       this.addEvents({
38026          
38027         /**
38028          * @event select
38029          * Fires when a date is selected
38030              * @param {Roo.form.MonthFieeld} combo This combo box
38031              * @param {Date} date The date selected
38032              */
38033         'select' : true
38034          
38035     });
38036     
38037     
38038     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38039     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38040     this.ddMatch = null;
38041     if(this.disabledDates){
38042         var dd = this.disabledDates;
38043         var re = "(?:";
38044         for(var i = 0; i < dd.length; i++){
38045             re += dd[i];
38046             if(i != dd.length-1) re += "|";
38047         }
38048         this.ddMatch = new RegExp(re + ")");
38049     }
38050 };
38051
38052 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38053     /**
38054      * @cfg {String} format
38055      * The default date format string which can be overriden for localization support.  The format must be
38056      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38057      */
38058     format : "M Y",
38059     /**
38060      * @cfg {String} altFormats
38061      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38062      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38063      */
38064     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38065     /**
38066      * @cfg {Array} disabledDays
38067      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38068      */
38069     disabledDays : [0,1,2,3,4,5,6],
38070     /**
38071      * @cfg {String} disabledDaysText
38072      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38073      */
38074     disabledDaysText : "Disabled",
38075     /**
38076      * @cfg {Array} disabledDates
38077      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38078      * expression so they are very powerful. Some examples:
38079      * <ul>
38080      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38081      * <li>["03/08", "09/16"] would disable those days for every year</li>
38082      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38083      * <li>["03/../2006"] would disable every day in March 2006</li>
38084      * <li>["^03"] would disable every day in every March</li>
38085      * </ul>
38086      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38087      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38088      */
38089     disabledDates : null,
38090     /**
38091      * @cfg {String} disabledDatesText
38092      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38093      */
38094     disabledDatesText : "Disabled",
38095     /**
38096      * @cfg {Date/String} minValue
38097      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38098      * valid format (defaults to null).
38099      */
38100     minValue : null,
38101     /**
38102      * @cfg {Date/String} maxValue
38103      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38104      * valid format (defaults to null).
38105      */
38106     maxValue : null,
38107     /**
38108      * @cfg {String} minText
38109      * The error text to display when the date in the cell is before minValue (defaults to
38110      * 'The date in this field must be after {minValue}').
38111      */
38112     minText : "The date in this field must be equal to or after {0}",
38113     /**
38114      * @cfg {String} maxTextf
38115      * The error text to display when the date in the cell is after maxValue (defaults to
38116      * 'The date in this field must be before {maxValue}').
38117      */
38118     maxText : "The date in this field must be equal to or before {0}",
38119     /**
38120      * @cfg {String} invalidText
38121      * The error text to display when the date in the field is invalid (defaults to
38122      * '{value} is not a valid date - it must be in the format {format}').
38123      */
38124     invalidText : "{0} is not a valid date - it must be in the format {1}",
38125     /**
38126      * @cfg {String} triggerClass
38127      * An additional CSS class used to style the trigger button.  The trigger will always get the
38128      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38129      * which displays a calendar icon).
38130      */
38131     triggerClass : 'x-form-date-trigger',
38132     
38133
38134     /**
38135      * @cfg {Boolean} useIso
38136      * if enabled, then the date field will use a hidden field to store the 
38137      * real value as iso formated date. default (true)
38138      */ 
38139     useIso : true,
38140     /**
38141      * @cfg {String/Object} autoCreate
38142      * A DomHelper element spec, or true for a default element spec (defaults to
38143      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38144      */ 
38145     // private
38146     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38147     
38148     // private
38149     hiddenField: false,
38150     
38151     hideMonthPicker : false,
38152     
38153     onRender : function(ct, position)
38154     {
38155         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38156         if (this.useIso) {
38157             this.el.dom.removeAttribute('name'); 
38158             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38159                     'before', true);
38160             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38161             // prevent input submission
38162             this.hiddenName = this.name;
38163         }
38164             
38165             
38166     },
38167     
38168     // private
38169     validateValue : function(value)
38170     {
38171         value = this.formatDate(value);
38172         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38173             return false;
38174         }
38175         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38176              return true;
38177         }
38178         var svalue = value;
38179         value = this.parseDate(value);
38180         if(!value){
38181             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38182             return false;
38183         }
38184         var time = value.getTime();
38185         if(this.minValue && time < this.minValue.getTime()){
38186             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38187             return false;
38188         }
38189         if(this.maxValue && time > this.maxValue.getTime()){
38190             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38191             return false;
38192         }
38193         /*if(this.disabledDays){
38194             var day = value.getDay();
38195             for(var i = 0; i < this.disabledDays.length; i++) {
38196                 if(day === this.disabledDays[i]){
38197                     this.markInvalid(this.disabledDaysText);
38198                     return false;
38199                 }
38200             }
38201         }
38202         */
38203         var fvalue = this.formatDate(value);
38204         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38205             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38206             return false;
38207         }
38208         */
38209         return true;
38210     },
38211
38212     // private
38213     // Provides logic to override the default TriggerField.validateBlur which just returns true
38214     validateBlur : function(){
38215         return !this.menu || !this.menu.isVisible();
38216     },
38217
38218     /**
38219      * Returns the current date value of the date field.
38220      * @return {Date} The date value
38221      */
38222     getValue : function(){
38223         
38224         
38225         
38226         return  this.hiddenField ?
38227                 this.hiddenField.value :
38228                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38229     },
38230
38231     /**
38232      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38233      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38234      * (the default format used is "m/d/y").
38235      * <br />Usage:
38236      * <pre><code>
38237 //All of these calls set the same date value (May 4, 2006)
38238
38239 //Pass a date object:
38240 var dt = new Date('5/4/06');
38241 monthField.setValue(dt);
38242
38243 //Pass a date string (default format):
38244 monthField.setValue('5/4/06');
38245
38246 //Pass a date string (custom format):
38247 monthField.format = 'Y-m-d';
38248 monthField.setValue('2006-5-4');
38249 </code></pre>
38250      * @param {String/Date} date The date or valid date string
38251      */
38252     setValue : function(date){
38253         Roo.log('month setValue' + date);
38254         // can only be first of month..
38255         
38256         var val = this.parseDate(date);
38257         
38258         if (this.hiddenField) {
38259             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38260         }
38261         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38262         this.value = this.parseDate(date);
38263     },
38264
38265     // private
38266     parseDate : function(value){
38267         if(!value || value instanceof Date){
38268             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38269             return value;
38270         }
38271         var v = Date.parseDate(value, this.format);
38272         if (!v && this.useIso) {
38273             v = Date.parseDate(value, 'Y-m-d');
38274         }
38275         if (v) {
38276             // 
38277             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38278         }
38279         
38280         
38281         if(!v && this.altFormats){
38282             if(!this.altFormatsArray){
38283                 this.altFormatsArray = this.altFormats.split("|");
38284             }
38285             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38286                 v = Date.parseDate(value, this.altFormatsArray[i]);
38287             }
38288         }
38289         return v;
38290     },
38291
38292     // private
38293     formatDate : function(date, fmt){
38294         return (!date || !(date instanceof Date)) ?
38295                date : date.dateFormat(fmt || this.format);
38296     },
38297
38298     // private
38299     menuListeners : {
38300         select: function(m, d){
38301             this.setValue(d);
38302             this.fireEvent('select', this, d);
38303         },
38304         show : function(){ // retain focus styling
38305             this.onFocus();
38306         },
38307         hide : function(){
38308             this.focus.defer(10, this);
38309             var ml = this.menuListeners;
38310             this.menu.un("select", ml.select,  this);
38311             this.menu.un("show", ml.show,  this);
38312             this.menu.un("hide", ml.hide,  this);
38313         }
38314     },
38315     // private
38316     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38317     onTriggerClick : function(){
38318         if(this.disabled){
38319             return;
38320         }
38321         if(this.menu == null){
38322             this.menu = new Roo.menu.DateMenu();
38323            
38324         }
38325         
38326         Roo.apply(this.menu.picker,  {
38327             
38328             showClear: this.allowBlank,
38329             minDate : this.minValue,
38330             maxDate : this.maxValue,
38331             disabledDatesRE : this.ddMatch,
38332             disabledDatesText : this.disabledDatesText,
38333             
38334             format : this.useIso ? 'Y-m-d' : this.format,
38335             minText : String.format(this.minText, this.formatDate(this.minValue)),
38336             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38337             
38338         });
38339          this.menu.on(Roo.apply({}, this.menuListeners, {
38340             scope:this
38341         }));
38342        
38343         
38344         var m = this.menu;
38345         var p = m.picker;
38346         
38347         // hide month picker get's called when we called by 'before hide';
38348         
38349         var ignorehide = true;
38350         p.hideMonthPicker  = function(disableAnim){
38351             if (ignorehide) {
38352                 return;
38353             }
38354              if(this.monthPicker){
38355                 Roo.log("hideMonthPicker called");
38356                 if(disableAnim === true){
38357                     this.monthPicker.hide();
38358                 }else{
38359                     this.monthPicker.slideOut('t', {duration:.2});
38360                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38361                     p.fireEvent("select", this, this.value);
38362                     m.hide();
38363                 }
38364             }
38365         }
38366         
38367         Roo.log('picker set value');
38368         Roo.log(this.getValue());
38369         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38370         m.show(this.el, 'tl-bl?');
38371         ignorehide  = false;
38372         // this will trigger hideMonthPicker..
38373         
38374         
38375         // hidden the day picker
38376         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38377         
38378         
38379         
38380       
38381         
38382         p.showMonthPicker.defer(100, p);
38383     
38384         
38385        
38386     },
38387
38388     beforeBlur : function(){
38389         var v = this.parseDate(this.getRawValue());
38390         if(v){
38391             this.setValue(v);
38392         }
38393     }
38394
38395     /** @cfg {Boolean} grow @hide */
38396     /** @cfg {Number} growMin @hide */
38397     /** @cfg {Number} growMax @hide */
38398     /**
38399      * @hide
38400      * @method autoSize
38401      */
38402 });/*
38403  * Based on:
38404  * Ext JS Library 1.1.1
38405  * Copyright(c) 2006-2007, Ext JS, LLC.
38406  *
38407  * Originally Released Under LGPL - original licence link has changed is not relivant.
38408  *
38409  * Fork - LGPL
38410  * <script type="text/javascript">
38411  */
38412  
38413
38414 /**
38415  * @class Roo.form.ComboBox
38416  * @extends Roo.form.TriggerField
38417  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38418  * @constructor
38419  * Create a new ComboBox.
38420  * @param {Object} config Configuration options
38421  */
38422 Roo.form.ComboBox = function(config){
38423     Roo.form.ComboBox.superclass.constructor.call(this, config);
38424     this.addEvents({
38425         /**
38426          * @event expand
38427          * Fires when the dropdown list is expanded
38428              * @param {Roo.form.ComboBox} combo This combo box
38429              */
38430         'expand' : true,
38431         /**
38432          * @event collapse
38433          * Fires when the dropdown list is collapsed
38434              * @param {Roo.form.ComboBox} combo This combo box
38435              */
38436         'collapse' : true,
38437         /**
38438          * @event beforeselect
38439          * Fires before a list item is selected. Return false to cancel the selection.
38440              * @param {Roo.form.ComboBox} combo This combo box
38441              * @param {Roo.data.Record} record The data record returned from the underlying store
38442              * @param {Number} index The index of the selected item in the dropdown list
38443              */
38444         'beforeselect' : true,
38445         /**
38446          * @event select
38447          * Fires when a list item is selected
38448              * @param {Roo.form.ComboBox} combo This combo box
38449              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
38450              * @param {Number} index The index of the selected item in the dropdown list
38451              */
38452         'select' : true,
38453         /**
38454          * @event beforequery
38455          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
38456          * The event object passed has these properties:
38457              * @param {Roo.form.ComboBox} combo This combo box
38458              * @param {String} query The query
38459              * @param {Boolean} forceAll true to force "all" query
38460              * @param {Boolean} cancel true to cancel the query
38461              * @param {Object} e The query event object
38462              */
38463         'beforequery': true,
38464          /**
38465          * @event add
38466          * Fires when the 'add' icon is pressed (add a listener to enable add button)
38467              * @param {Roo.form.ComboBox} combo This combo box
38468              */
38469         'add' : true,
38470         /**
38471          * @event edit
38472          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
38473              * @param {Roo.form.ComboBox} combo This combo box
38474              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
38475              */
38476         'edit' : true
38477         
38478         
38479     });
38480     if(this.transform){
38481         this.allowDomMove = false;
38482         var s = Roo.getDom(this.transform);
38483         if(!this.hiddenName){
38484             this.hiddenName = s.name;
38485         }
38486         if(!this.store){
38487             this.mode = 'local';
38488             var d = [], opts = s.options;
38489             for(var i = 0, len = opts.length;i < len; i++){
38490                 var o = opts[i];
38491                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
38492                 if(o.selected) {
38493                     this.value = value;
38494                 }
38495                 d.push([value, o.text]);
38496             }
38497             this.store = new Roo.data.SimpleStore({
38498                 'id': 0,
38499                 fields: ['value', 'text'],
38500                 data : d
38501             });
38502             this.valueField = 'value';
38503             this.displayField = 'text';
38504         }
38505         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
38506         if(!this.lazyRender){
38507             this.target = true;
38508             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
38509             s.parentNode.removeChild(s); // remove it
38510             this.render(this.el.parentNode);
38511         }else{
38512             s.parentNode.removeChild(s); // remove it
38513         }
38514
38515     }
38516     if (this.store) {
38517         this.store = Roo.factory(this.store, Roo.data);
38518     }
38519     
38520     this.selectedIndex = -1;
38521     if(this.mode == 'local'){
38522         if(config.queryDelay === undefined){
38523             this.queryDelay = 10;
38524         }
38525         if(config.minChars === undefined){
38526             this.minChars = 0;
38527         }
38528     }
38529 };
38530
38531 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
38532     /**
38533      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
38534      */
38535     /**
38536      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
38537      * rendering into an Roo.Editor, defaults to false)
38538      */
38539     /**
38540      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
38541      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
38542      */
38543     /**
38544      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
38545      */
38546     /**
38547      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
38548      * the dropdown list (defaults to undefined, with no header element)
38549      */
38550
38551      /**
38552      * @cfg {String/Roo.Template} tpl The template to use to render the output
38553      */
38554      
38555     // private
38556     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
38557     /**
38558      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
38559      */
38560     listWidth: undefined,
38561     /**
38562      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
38563      * mode = 'remote' or 'text' if mode = 'local')
38564      */
38565     displayField: undefined,
38566     /**
38567      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
38568      * mode = 'remote' or 'value' if mode = 'local'). 
38569      * Note: use of a valueField requires the user make a selection
38570      * in order for a value to be mapped.
38571      */
38572     valueField: undefined,
38573     
38574     
38575     /**
38576      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
38577      * field's data value (defaults to the underlying DOM element's name)
38578      */
38579     hiddenName: undefined,
38580     /**
38581      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
38582      */
38583     listClass: '',
38584     /**
38585      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
38586      */
38587     selectedClass: 'x-combo-selected',
38588     /**
38589      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38590      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
38591      * which displays a downward arrow icon).
38592      */
38593     triggerClass : 'x-form-arrow-trigger',
38594     /**
38595      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
38596      */
38597     shadow:'sides',
38598     /**
38599      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
38600      * anchor positions (defaults to 'tl-bl')
38601      */
38602     listAlign: 'tl-bl?',
38603     /**
38604      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
38605      */
38606     maxHeight: 300,
38607     /**
38608      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
38609      * query specified by the allQuery config option (defaults to 'query')
38610      */
38611     triggerAction: 'query',
38612     /**
38613      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
38614      * (defaults to 4, does not apply if editable = false)
38615      */
38616     minChars : 4,
38617     /**
38618      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
38619      * delay (typeAheadDelay) if it matches a known value (defaults to false)
38620      */
38621     typeAhead: false,
38622     /**
38623      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
38624      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
38625      */
38626     queryDelay: 500,
38627     /**
38628      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
38629      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
38630      */
38631     pageSize: 0,
38632     /**
38633      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
38634      * when editable = true (defaults to false)
38635      */
38636     selectOnFocus:false,
38637     /**
38638      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
38639      */
38640     queryParam: 'query',
38641     /**
38642      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
38643      * when mode = 'remote' (defaults to 'Loading...')
38644      */
38645     loadingText: 'Loading...',
38646     /**
38647      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
38648      */
38649     resizable: false,
38650     /**
38651      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
38652      */
38653     handleHeight : 8,
38654     /**
38655      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
38656      * traditional select (defaults to true)
38657      */
38658     editable: true,
38659     /**
38660      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
38661      */
38662     allQuery: '',
38663     /**
38664      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
38665      */
38666     mode: 'remote',
38667     /**
38668      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
38669      * listWidth has a higher value)
38670      */
38671     minListWidth : 70,
38672     /**
38673      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
38674      * allow the user to set arbitrary text into the field (defaults to false)
38675      */
38676     forceSelection:false,
38677     /**
38678      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
38679      * if typeAhead = true (defaults to 250)
38680      */
38681     typeAheadDelay : 250,
38682     /**
38683      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
38684      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
38685      */
38686     valueNotFoundText : undefined,
38687     /**
38688      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
38689      */
38690     blockFocus : false,
38691     
38692     /**
38693      * @cfg {Boolean} disableClear Disable showing of clear button.
38694      */
38695     disableClear : false,
38696     /**
38697      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
38698      */
38699     alwaysQuery : false,
38700     
38701     //private
38702     addicon : false,
38703     editicon: false,
38704     
38705     // element that contains real text value.. (when hidden is used..)
38706      
38707     // private
38708     onRender : function(ct, position){
38709         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
38710         if(this.hiddenName){
38711             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
38712                     'before', true);
38713             this.hiddenField.value =
38714                 this.hiddenValue !== undefined ? this.hiddenValue :
38715                 this.value !== undefined ? this.value : '';
38716
38717             // prevent input submission
38718             this.el.dom.removeAttribute('name');
38719              
38720              
38721         }
38722         if(Roo.isGecko){
38723             this.el.dom.setAttribute('autocomplete', 'off');
38724         }
38725
38726         var cls = 'x-combo-list';
38727
38728         this.list = new Roo.Layer({
38729             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
38730         });
38731
38732         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
38733         this.list.setWidth(lw);
38734         this.list.swallowEvent('mousewheel');
38735         this.assetHeight = 0;
38736
38737         if(this.title){
38738             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38739             this.assetHeight += this.header.getHeight();
38740         }
38741
38742         this.innerList = this.list.createChild({cls:cls+'-inner'});
38743         this.innerList.on('mouseover', this.onViewOver, this);
38744         this.innerList.on('mousemove', this.onViewMove, this);
38745         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38746         
38747         if(this.allowBlank && !this.pageSize && !this.disableClear){
38748             this.footer = this.list.createChild({cls:cls+'-ft'});
38749             this.pageTb = new Roo.Toolbar(this.footer);
38750            
38751         }
38752         if(this.pageSize){
38753             this.footer = this.list.createChild({cls:cls+'-ft'});
38754             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38755                     {pageSize: this.pageSize});
38756             
38757         }
38758         
38759         if (this.pageTb && this.allowBlank && !this.disableClear) {
38760             var _this = this;
38761             this.pageTb.add(new Roo.Toolbar.Fill(), {
38762                 cls: 'x-btn-icon x-btn-clear',
38763                 text: '&#160;',
38764                 handler: function()
38765                 {
38766                     _this.collapse();
38767                     _this.clearValue();
38768                     _this.onSelect(false, -1);
38769                 }
38770             });
38771         }
38772         if (this.footer) {
38773             this.assetHeight += this.footer.getHeight();
38774         }
38775         
38776
38777         if(!this.tpl){
38778             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
38779         }
38780
38781         this.view = new Roo.View(this.innerList, this.tpl, {
38782             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38783         });
38784
38785         this.view.on('click', this.onViewClick, this);
38786
38787         this.store.on('beforeload', this.onBeforeLoad, this);
38788         this.store.on('load', this.onLoad, this);
38789         this.store.on('loadexception', this.onLoadException, this);
38790
38791         if(this.resizable){
38792             this.resizer = new Roo.Resizable(this.list,  {
38793                pinned:true, handles:'se'
38794             });
38795             this.resizer.on('resize', function(r, w, h){
38796                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38797                 this.listWidth = w;
38798                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38799                 this.restrictHeight();
38800             }, this);
38801             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38802         }
38803         if(!this.editable){
38804             this.editable = true;
38805             this.setEditable(false);
38806         }  
38807         
38808         
38809         if (typeof(this.events.add.listeners) != 'undefined') {
38810             
38811             this.addicon = this.wrap.createChild(
38812                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38813        
38814             this.addicon.on('click', function(e) {
38815                 this.fireEvent('add', this);
38816             }, this);
38817         }
38818         if (typeof(this.events.edit.listeners) != 'undefined') {
38819             
38820             this.editicon = this.wrap.createChild(
38821                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38822             if (this.addicon) {
38823                 this.editicon.setStyle('margin-left', '40px');
38824             }
38825             this.editicon.on('click', function(e) {
38826                 
38827                 // we fire even  if inothing is selected..
38828                 this.fireEvent('edit', this, this.lastData );
38829                 
38830             }, this);
38831         }
38832         
38833         
38834         
38835     },
38836
38837     // private
38838     initEvents : function(){
38839         Roo.form.ComboBox.superclass.initEvents.call(this);
38840
38841         this.keyNav = new Roo.KeyNav(this.el, {
38842             "up" : function(e){
38843                 this.inKeyMode = true;
38844                 this.selectPrev();
38845             },
38846
38847             "down" : function(e){
38848                 if(!this.isExpanded()){
38849                     this.onTriggerClick();
38850                 }else{
38851                     this.inKeyMode = true;
38852                     this.selectNext();
38853                 }
38854             },
38855
38856             "enter" : function(e){
38857                 this.onViewClick();
38858                 //return true;
38859             },
38860
38861             "esc" : function(e){
38862                 this.collapse();
38863             },
38864
38865             "tab" : function(e){
38866                 this.onViewClick(false);
38867                 this.fireEvent("specialkey", this, e);
38868                 return true;
38869             },
38870
38871             scope : this,
38872
38873             doRelay : function(foo, bar, hname){
38874                 if(hname == 'down' || this.scope.isExpanded()){
38875                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38876                 }
38877                 return true;
38878             },
38879
38880             forceKeyDown: true
38881         });
38882         this.queryDelay = Math.max(this.queryDelay || 10,
38883                 this.mode == 'local' ? 10 : 250);
38884         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38885         if(this.typeAhead){
38886             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38887         }
38888         if(this.editable !== false){
38889             this.el.on("keyup", this.onKeyUp, this);
38890         }
38891         if(this.forceSelection){
38892             this.on('blur', this.doForce, this);
38893         }
38894     },
38895
38896     onDestroy : function(){
38897         if(this.view){
38898             this.view.setStore(null);
38899             this.view.el.removeAllListeners();
38900             this.view.el.remove();
38901             this.view.purgeListeners();
38902         }
38903         if(this.list){
38904             this.list.destroy();
38905         }
38906         if(this.store){
38907             this.store.un('beforeload', this.onBeforeLoad, this);
38908             this.store.un('load', this.onLoad, this);
38909             this.store.un('loadexception', this.onLoadException, this);
38910         }
38911         Roo.form.ComboBox.superclass.onDestroy.call(this);
38912     },
38913
38914     // private
38915     fireKey : function(e){
38916         if(e.isNavKeyPress() && !this.list.isVisible()){
38917             this.fireEvent("specialkey", this, e);
38918         }
38919     },
38920
38921     // private
38922     onResize: function(w, h){
38923         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
38924         
38925         if(typeof w != 'number'){
38926             // we do not handle it!?!?
38927             return;
38928         }
38929         var tw = this.trigger.getWidth();
38930         tw += this.addicon ? this.addicon.getWidth() : 0;
38931         tw += this.editicon ? this.editicon.getWidth() : 0;
38932         var x = w - tw;
38933         this.el.setWidth( this.adjustWidth('input', x));
38934             
38935         this.trigger.setStyle('left', x+'px');
38936         
38937         if(this.list && this.listWidth === undefined){
38938             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
38939             this.list.setWidth(lw);
38940             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38941         }
38942         
38943     
38944         
38945     },
38946
38947     /**
38948      * Allow or prevent the user from directly editing the field text.  If false is passed,
38949      * the user will only be able to select from the items defined in the dropdown list.  This method
38950      * is the runtime equivalent of setting the 'editable' config option at config time.
38951      * @param {Boolean} value True to allow the user to directly edit the field text
38952      */
38953     setEditable : function(value){
38954         if(value == this.editable){
38955             return;
38956         }
38957         this.editable = value;
38958         if(!value){
38959             this.el.dom.setAttribute('readOnly', true);
38960             this.el.on('mousedown', this.onTriggerClick,  this);
38961             this.el.addClass('x-combo-noedit');
38962         }else{
38963             this.el.dom.setAttribute('readOnly', false);
38964             this.el.un('mousedown', this.onTriggerClick,  this);
38965             this.el.removeClass('x-combo-noedit');
38966         }
38967     },
38968
38969     // private
38970     onBeforeLoad : function(){
38971         if(!this.hasFocus){
38972             return;
38973         }
38974         this.innerList.update(this.loadingText ?
38975                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
38976         this.restrictHeight();
38977         this.selectedIndex = -1;
38978     },
38979
38980     // private
38981     onLoad : function(){
38982         if(!this.hasFocus){
38983             return;
38984         }
38985         if(this.store.getCount() > 0){
38986             this.expand();
38987             this.restrictHeight();
38988             if(this.lastQuery == this.allQuery){
38989                 if(this.editable){
38990                     this.el.dom.select();
38991                 }
38992                 if(!this.selectByValue(this.value, true)){
38993                     this.select(0, true);
38994                 }
38995             }else{
38996                 this.selectNext();
38997                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
38998                     this.taTask.delay(this.typeAheadDelay);
38999                 }
39000             }
39001         }else{
39002             this.onEmptyResults();
39003         }
39004         //this.el.focus();
39005     },
39006     // private
39007     onLoadException : function()
39008     {
39009         this.collapse();
39010         Roo.log(this.store.reader.jsonData);
39011         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39012             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39013         }
39014         
39015         
39016     },
39017     // private
39018     onTypeAhead : function(){
39019         if(this.store.getCount() > 0){
39020             var r = this.store.getAt(0);
39021             var newValue = r.data[this.displayField];
39022             var len = newValue.length;
39023             var selStart = this.getRawValue().length;
39024             if(selStart != len){
39025                 this.setRawValue(newValue);
39026                 this.selectText(selStart, newValue.length);
39027             }
39028         }
39029     },
39030
39031     // private
39032     onSelect : function(record, index){
39033         if(this.fireEvent('beforeselect', this, record, index) !== false){
39034             this.setFromData(index > -1 ? record.data : false);
39035             this.collapse();
39036             this.fireEvent('select', this, record, index);
39037         }
39038     },
39039
39040     /**
39041      * Returns the currently selected field value or empty string if no value is set.
39042      * @return {String} value The selected value
39043      */
39044     getValue : function(){
39045         if(this.valueField){
39046             return typeof this.value != 'undefined' ? this.value : '';
39047         }else{
39048             return Roo.form.ComboBox.superclass.getValue.call(this);
39049         }
39050     },
39051
39052     /**
39053      * Clears any text/value currently set in the field
39054      */
39055     clearValue : function(){
39056         if(this.hiddenField){
39057             this.hiddenField.value = '';
39058         }
39059         this.value = '';
39060         this.setRawValue('');
39061         this.lastSelectionText = '';
39062         
39063     },
39064
39065     /**
39066      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39067      * will be displayed in the field.  If the value does not match the data value of an existing item,
39068      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39069      * Otherwise the field will be blank (although the value will still be set).
39070      * @param {String} value The value to match
39071      */
39072     setValue : function(v){
39073         var text = v;
39074         if(this.valueField){
39075             var r = this.findRecord(this.valueField, v);
39076             if(r){
39077                 text = r.data[this.displayField];
39078             }else if(this.valueNotFoundText !== undefined){
39079                 text = this.valueNotFoundText;
39080             }
39081         }
39082         this.lastSelectionText = text;
39083         if(this.hiddenField){
39084             this.hiddenField.value = v;
39085         }
39086         Roo.form.ComboBox.superclass.setValue.call(this, text);
39087         this.value = v;
39088     },
39089     /**
39090      * @property {Object} the last set data for the element
39091      */
39092     
39093     lastData : false,
39094     /**
39095      * Sets the value of the field based on a object which is related to the record format for the store.
39096      * @param {Object} value the value to set as. or false on reset?
39097      */
39098     setFromData : function(o){
39099         var dv = ''; // display value
39100         var vv = ''; // value value..
39101         this.lastData = o;
39102         if (this.displayField) {
39103             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39104         } else {
39105             // this is an error condition!!!
39106             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39107         }
39108         
39109         if(this.valueField){
39110             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39111         }
39112         if(this.hiddenField){
39113             this.hiddenField.value = vv;
39114             
39115             this.lastSelectionText = dv;
39116             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39117             this.value = vv;
39118             return;
39119         }
39120         // no hidden field.. - we store the value in 'value', but still display
39121         // display field!!!!
39122         this.lastSelectionText = dv;
39123         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39124         this.value = vv;
39125         
39126         
39127     },
39128     // private
39129     reset : function(){
39130         // overridden so that last data is reset..
39131         this.setValue(this.originalValue);
39132         this.clearInvalid();
39133         this.lastData = false;
39134         if (this.view) {
39135             this.view.clearSelections();
39136         }
39137     },
39138     // private
39139     findRecord : function(prop, value){
39140         var record;
39141         if(this.store.getCount() > 0){
39142             this.store.each(function(r){
39143                 if(r.data[prop] == value){
39144                     record = r;
39145                     return false;
39146                 }
39147                 return true;
39148             });
39149         }
39150         return record;
39151     },
39152     
39153     getName: function()
39154     {
39155         // returns hidden if it's set..
39156         if (!this.rendered) {return ''};
39157         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39158         
39159     },
39160     // private
39161     onViewMove : function(e, t){
39162         this.inKeyMode = false;
39163     },
39164
39165     // private
39166     onViewOver : function(e, t){
39167         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39168             return;
39169         }
39170         var item = this.view.findItemFromChild(t);
39171         if(item){
39172             var index = this.view.indexOf(item);
39173             this.select(index, false);
39174         }
39175     },
39176
39177     // private
39178     onViewClick : function(doFocus)
39179     {
39180         var index = this.view.getSelectedIndexes()[0];
39181         var r = this.store.getAt(index);
39182         if(r){
39183             this.onSelect(r, index);
39184         }
39185         if(doFocus !== false && !this.blockFocus){
39186             this.el.focus();
39187         }
39188     },
39189
39190     // private
39191     restrictHeight : function(){
39192         this.innerList.dom.style.height = '';
39193         var inner = this.innerList.dom;
39194         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39195         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39196         this.list.beginUpdate();
39197         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39198         this.list.alignTo(this.el, this.listAlign);
39199         this.list.endUpdate();
39200     },
39201
39202     // private
39203     onEmptyResults : function(){
39204         this.collapse();
39205     },
39206
39207     /**
39208      * Returns true if the dropdown list is expanded, else false.
39209      */
39210     isExpanded : function(){
39211         return this.list.isVisible();
39212     },
39213
39214     /**
39215      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39216      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39217      * @param {String} value The data value of the item to select
39218      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39219      * selected item if it is not currently in view (defaults to true)
39220      * @return {Boolean} True if the value matched an item in the list, else false
39221      */
39222     selectByValue : function(v, scrollIntoView){
39223         if(v !== undefined && v !== null){
39224             var r = this.findRecord(this.valueField || this.displayField, v);
39225             if(r){
39226                 this.select(this.store.indexOf(r), scrollIntoView);
39227                 return true;
39228             }
39229         }
39230         return false;
39231     },
39232
39233     /**
39234      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39235      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39236      * @param {Number} index The zero-based index of the list item to select
39237      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39238      * selected item if it is not currently in view (defaults to true)
39239      */
39240     select : function(index, scrollIntoView){
39241         this.selectedIndex = index;
39242         this.view.select(index);
39243         if(scrollIntoView !== false){
39244             var el = this.view.getNode(index);
39245             if(el){
39246                 this.innerList.scrollChildIntoView(el, false);
39247             }
39248         }
39249     },
39250
39251     // private
39252     selectNext : function(){
39253         var ct = this.store.getCount();
39254         if(ct > 0){
39255             if(this.selectedIndex == -1){
39256                 this.select(0);
39257             }else if(this.selectedIndex < ct-1){
39258                 this.select(this.selectedIndex+1);
39259             }
39260         }
39261     },
39262
39263     // private
39264     selectPrev : function(){
39265         var ct = this.store.getCount();
39266         if(ct > 0){
39267             if(this.selectedIndex == -1){
39268                 this.select(0);
39269             }else if(this.selectedIndex != 0){
39270                 this.select(this.selectedIndex-1);
39271             }
39272         }
39273     },
39274
39275     // private
39276     onKeyUp : function(e){
39277         if(this.editable !== false && !e.isSpecialKey()){
39278             this.lastKey = e.getKey();
39279             this.dqTask.delay(this.queryDelay);
39280         }
39281     },
39282
39283     // private
39284     validateBlur : function(){
39285         return !this.list || !this.list.isVisible();   
39286     },
39287
39288     // private
39289     initQuery : function(){
39290         this.doQuery(this.getRawValue());
39291     },
39292
39293     // private
39294     doForce : function(){
39295         if(this.el.dom.value.length > 0){
39296             this.el.dom.value =
39297                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39298              
39299         }
39300     },
39301
39302     /**
39303      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39304      * query allowing the query action to be canceled if needed.
39305      * @param {String} query The SQL query to execute
39306      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39307      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39308      * saved in the current store (defaults to false)
39309      */
39310     doQuery : function(q, forceAll){
39311         if(q === undefined || q === null){
39312             q = '';
39313         }
39314         var qe = {
39315             query: q,
39316             forceAll: forceAll,
39317             combo: this,
39318             cancel:false
39319         };
39320         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39321             return false;
39322         }
39323         q = qe.query;
39324         forceAll = qe.forceAll;
39325         if(forceAll === true || (q.length >= this.minChars)){
39326             if(this.lastQuery != q || this.alwaysQuery){
39327                 this.lastQuery = q;
39328                 if(this.mode == 'local'){
39329                     this.selectedIndex = -1;
39330                     if(forceAll){
39331                         this.store.clearFilter();
39332                     }else{
39333                         this.store.filter(this.displayField, q);
39334                     }
39335                     this.onLoad();
39336                 }else{
39337                     this.store.baseParams[this.queryParam] = q;
39338                     this.store.load({
39339                         params: this.getParams(q)
39340                     });
39341                     this.expand();
39342                 }
39343             }else{
39344                 this.selectedIndex = -1;
39345                 this.onLoad();   
39346             }
39347         }
39348     },
39349
39350     // private
39351     getParams : function(q){
39352         var p = {};
39353         //p[this.queryParam] = q;
39354         if(this.pageSize){
39355             p.start = 0;
39356             p.limit = this.pageSize;
39357         }
39358         return p;
39359     },
39360
39361     /**
39362      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39363      */
39364     collapse : function(){
39365         if(!this.isExpanded()){
39366             return;
39367         }
39368         this.list.hide();
39369         Roo.get(document).un('mousedown', this.collapseIf, this);
39370         Roo.get(document).un('mousewheel', this.collapseIf, this);
39371         if (!this.editable) {
39372             Roo.get(document).un('keydown', this.listKeyPress, this);
39373         }
39374         this.fireEvent('collapse', this);
39375     },
39376
39377     // private
39378     collapseIf : function(e){
39379         if(!e.within(this.wrap) && !e.within(this.list)){
39380             this.collapse();
39381         }
39382     },
39383
39384     /**
39385      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39386      */
39387     expand : function(){
39388         if(this.isExpanded() || !this.hasFocus){
39389             return;
39390         }
39391         this.list.alignTo(this.el, this.listAlign);
39392         this.list.show();
39393         Roo.get(document).on('mousedown', this.collapseIf, this);
39394         Roo.get(document).on('mousewheel', this.collapseIf, this);
39395         if (!this.editable) {
39396             Roo.get(document).on('keydown', this.listKeyPress, this);
39397         }
39398         
39399         this.fireEvent('expand', this);
39400     },
39401
39402     // private
39403     // Implements the default empty TriggerField.onTriggerClick function
39404     onTriggerClick : function(){
39405         if(this.disabled){
39406             return;
39407         }
39408         if(this.isExpanded()){
39409             this.collapse();
39410             if (!this.blockFocus) {
39411                 this.el.focus();
39412             }
39413             
39414         }else {
39415             this.hasFocus = true;
39416             if(this.triggerAction == 'all') {
39417                 this.doQuery(this.allQuery, true);
39418             } else {
39419                 this.doQuery(this.getRawValue());
39420             }
39421             if (!this.blockFocus) {
39422                 this.el.focus();
39423             }
39424         }
39425     },
39426     listKeyPress : function(e)
39427     {
39428         //Roo.log('listkeypress');
39429         // scroll to first matching element based on key pres..
39430         if (e.isSpecialKey()) {
39431             return false;
39432         }
39433         var k = String.fromCharCode(e.getKey()).toUpperCase();
39434         //Roo.log(k);
39435         var match  = false;
39436         var csel = this.view.getSelectedNodes();
39437         var cselitem = false;
39438         if (csel.length) {
39439             var ix = this.view.indexOf(csel[0]);
39440             cselitem  = this.store.getAt(ix);
39441             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
39442                 cselitem = false;
39443             }
39444             
39445         }
39446         
39447         this.store.each(function(v) { 
39448             if (cselitem) {
39449                 // start at existing selection.
39450                 if (cselitem.id == v.id) {
39451                     cselitem = false;
39452                 }
39453                 return;
39454             }
39455                 
39456             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
39457                 match = this.store.indexOf(v);
39458                 return false;
39459             }
39460         }, this);
39461         
39462         if (match === false) {
39463             return true; // no more action?
39464         }
39465         // scroll to?
39466         this.view.select(match);
39467         var sn = Roo.get(this.view.getSelectedNodes()[0])
39468         sn.scrollIntoView(sn.dom.parentNode, false);
39469     }
39470
39471     /** 
39472     * @cfg {Boolean} grow 
39473     * @hide 
39474     */
39475     /** 
39476     * @cfg {Number} growMin 
39477     * @hide 
39478     */
39479     /** 
39480     * @cfg {Number} growMax 
39481     * @hide 
39482     */
39483     /**
39484      * @hide
39485      * @method autoSize
39486      */
39487 });/*
39488  * Copyright(c) 2010-2012, Roo J Solutions Limited
39489  *
39490  * Licence LGPL
39491  *
39492  */
39493
39494 /**
39495  * @class Roo.form.ComboBoxArray
39496  * @extends Roo.form.TextField
39497  * A facebook style adder... for lists of email / people / countries  etc...
39498  * pick multiple items from a combo box, and shows each one.
39499  *
39500  *  Fred [x]  Brian [x]  [Pick another |v]
39501  *
39502  *
39503  *  For this to work: it needs various extra information
39504  *    - normal combo problay has
39505  *      name, hiddenName
39506  *    + displayField, valueField
39507  *
39508  *    For our purpose...
39509  *
39510  *
39511  *   If we change from 'extends' to wrapping...
39512  *   
39513  *  
39514  *
39515  
39516  
39517  * @constructor
39518  * Create a new ComboBoxArray.
39519  * @param {Object} config Configuration options
39520  */
39521  
39522
39523 Roo.form.ComboBoxArray = function(config)
39524 {
39525     
39526     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
39527     
39528     this.items = new Roo.util.MixedCollection(false);
39529     
39530     // construct the child combo...
39531     
39532     
39533     
39534     
39535    
39536     
39537 }
39538
39539  
39540 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
39541
39542     /**
39543      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
39544      */
39545     
39546     lastData : false,
39547     
39548     // behavies liek a hiddne field
39549     inputType:      'hidden',
39550     /**
39551      * @cfg {Number} width The width of the box that displays the selected element
39552      */ 
39553     width:          300,
39554
39555     
39556     
39557     /**
39558      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
39559      */
39560     name : false,
39561     /**
39562      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
39563      */
39564     hiddenName : false,
39565     
39566     
39567     // private the array of items that are displayed..
39568     items  : false,
39569     // private - the hidden field el.
39570     hiddenEl : false,
39571     // private - the filed el..
39572     el : false,
39573     
39574     //validateValue : function() { return true; }, // all values are ok!
39575     //onAddClick: function() { },
39576     
39577     onRender : function(ct, position) 
39578     {
39579         
39580         // create the standard hidden element
39581         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
39582         
39583         
39584         // give fake names to child combo;
39585         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
39586         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
39587         
39588         this.combo = Roo.factory(this.combo, Roo.form);
39589         this.combo.onRender(ct, position);
39590         if (typeof(this.combo.width) != 'undefined') {
39591             this.combo.onResize(this.combo.width,0);
39592         }
39593         
39594         this.combo.initEvents();
39595         
39596         // assigned so form know we need to do this..
39597         this.store          = this.combo.store;
39598         this.valueField     = this.combo.valueField;
39599         this.displayField   = this.combo.displayField ;
39600         
39601         
39602         this.combo.wrap.addClass('x-cbarray-grp');
39603         
39604         var cbwrap = this.combo.wrap.createChild(
39605             {tag: 'div', cls: 'x-cbarray-cb'},
39606             this.combo.el.dom
39607         );
39608         
39609              
39610         this.hiddenEl = this.combo.wrap.createChild({
39611             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
39612         });
39613         this.el = this.combo.wrap.createChild({
39614             tag: 'input',  type:'hidden' , name: this.name, value : ''
39615         });
39616          //   this.el.dom.removeAttribute("name");
39617         
39618         
39619         this.outerWrap = this.combo.wrap;
39620         this.wrap = cbwrap;
39621         
39622         this.outerWrap.setWidth(this.width);
39623         this.outerWrap.dom.removeChild(this.el.dom);
39624         
39625         this.wrap.dom.appendChild(this.el.dom);
39626         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
39627         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
39628         
39629         this.combo.trigger.setStyle('position','relative');
39630         this.combo.trigger.setStyle('left', '0px');
39631         this.combo.trigger.setStyle('top', '2px');
39632         
39633         this.combo.el.setStyle('vertical-align', 'text-bottom');
39634         
39635         //this.trigger.setStyle('vertical-align', 'top');
39636         
39637         // this should use the code from combo really... on('add' ....)
39638         if (this.adder) {
39639             
39640         
39641             this.adder = this.outerWrap.createChild(
39642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
39643             var _t = this;
39644             this.adder.on('click', function(e) {
39645                 _t.fireEvent('adderclick', this, e);
39646             }, _t);
39647         }
39648         //var _t = this;
39649         //this.adder.on('click', this.onAddClick, _t);
39650         
39651         
39652         this.combo.on('select', function(cb, rec, ix) {
39653             this.addItem(rec.data);
39654             
39655             cb.setValue('');
39656             cb.el.dom.value = '';
39657             //cb.lastData = rec.data;
39658             // add to list
39659             
39660         }, this);
39661         
39662         
39663     },
39664     
39665     
39666     getName: function()
39667     {
39668         // returns hidden if it's set..
39669         if (!this.rendered) {return ''};
39670         return  this.hiddenName ? this.hiddenName : this.name;
39671         
39672     },
39673     
39674     
39675     onResize: function(w, h){
39676         
39677         return;
39678         // not sure if this is needed..
39679         //this.combo.onResize(w,h);
39680         
39681         if(typeof w != 'number'){
39682             // we do not handle it!?!?
39683             return;
39684         }
39685         var tw = this.combo.trigger.getWidth();
39686         tw += this.addicon ? this.addicon.getWidth() : 0;
39687         tw += this.editicon ? this.editicon.getWidth() : 0;
39688         var x = w - tw;
39689         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
39690             
39691         this.combo.trigger.setStyle('left', '0px');
39692         
39693         if(this.list && this.listWidth === undefined){
39694             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
39695             this.list.setWidth(lw);
39696             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39697         }
39698         
39699     
39700         
39701     },
39702     
39703     addItem: function(rec)
39704     {
39705         var valueField = this.combo.valueField;
39706         var displayField = this.combo.displayField;
39707         if (this.items.indexOfKey(rec[valueField]) > -1) {
39708             //console.log("GOT " + rec.data.id);
39709             return;
39710         }
39711         
39712         var x = new Roo.form.ComboBoxArray.Item({
39713             //id : rec[this.idField],
39714             data : rec,
39715             displayField : displayField ,
39716             tipField : displayField ,
39717             cb : this
39718         });
39719         // use the 
39720         this.items.add(rec[valueField],x);
39721         // add it before the element..
39722         this.updateHiddenEl();
39723         x.render(this.outerWrap, this.wrap.dom);
39724         // add the image handler..
39725     },
39726     
39727     updateHiddenEl : function()
39728     {
39729         this.validate();
39730         if (!this.hiddenEl) {
39731             return;
39732         }
39733         var ar = [];
39734         var idField = this.combo.valueField;
39735         
39736         this.items.each(function(f) {
39737             ar.push(f.data[idField]);
39738            
39739         });
39740         this.hiddenEl.dom.value = ar.join(',');
39741         this.validate();
39742     },
39743     
39744     reset : function()
39745     {
39746         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
39747         this.items.each(function(f) {
39748            f.remove(); 
39749         });
39750         this.el.dom.value = '';
39751         if (this.hiddenEl) {
39752             this.hiddenEl.dom.value = '';
39753         }
39754         
39755     },
39756     getValue: function()
39757     {
39758         return this.hiddenEl ? this.hiddenEl.dom.value : '';
39759     },
39760     setValue: function(v) // not a valid action - must use addItems..
39761     {
39762          
39763         this.reset();
39764         
39765         
39766         
39767         if (this.store.isLocal && (typeof(v) == 'string')) {
39768             // then we can use the store to find the values..
39769             // comma seperated at present.. this needs to allow JSON based encoding..
39770             this.hiddenEl.value  = v;
39771             var v_ar = [];
39772             Roo.each(v.split(','), function(k) {
39773                 Roo.log("CHECK " + this.valueField + ',' + k);
39774                 var li = this.store.query(this.valueField, k);
39775                 if (!li.length) {
39776                     return;
39777                 }
39778                 var add = {};
39779                 add[this.valueField] = k;
39780                 add[this.displayField] = li.item(0).data[this.displayField];
39781                 
39782                 this.addItem(add);
39783             }, this) 
39784              
39785         }
39786         if (typeof(v) == 'object') {
39787             // then let's assume it's an array of objects..
39788             Roo.each(v, function(l) {
39789                 this.addItem(l);
39790             }, this);
39791              
39792         }
39793         
39794         
39795     },
39796     setFromData: function(v)
39797     {
39798         // this recieves an object, if setValues is called.
39799         this.reset();
39800         this.el.dom.value = v[this.displayField];
39801         this.hiddenEl.dom.value = v[this.valueField];
39802         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
39803             return;
39804         }
39805         var kv = v[this.valueField];
39806         var dv = v[this.displayField];
39807         kv = typeof(kv) != 'string' ? '' : kv;
39808         dv = typeof(dv) != 'string' ? '' : dv;
39809         
39810         
39811         var keys = kv.split(',');
39812         var display = dv.split(',');
39813         for (var i = 0 ; i < keys.length; i++) {
39814             
39815             add = {};
39816             add[this.valueField] = keys[i];
39817             add[this.displayField] = display[i];
39818             this.addItem(add);
39819         }
39820       
39821         
39822     },
39823     
39824     
39825     validateValue : function(value){
39826         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
39827         
39828     }
39829     
39830 });
39831
39832
39833
39834 /**
39835  * @class Roo.form.ComboBoxArray.Item
39836  * @extends Roo.BoxComponent
39837  * A selected item in the list
39838  *  Fred [x]  Brian [x]  [Pick another |v]
39839  * 
39840  * @constructor
39841  * Create a new item.
39842  * @param {Object} config Configuration options
39843  */
39844  
39845 Roo.form.ComboBoxArray.Item = function(config) {
39846     config.id = Roo.id();
39847     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
39848 }
39849
39850 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
39851     data : {},
39852     cb: false,
39853     displayField : false,
39854     tipField : false,
39855     
39856     
39857     defaultAutoCreate : {
39858         tag: 'div',
39859         cls: 'x-cbarray-item',
39860         cn : [ 
39861             { tag: 'div' },
39862             {
39863                 tag: 'img',
39864                 width:16,
39865                 height : 16,
39866                 src : Roo.BLANK_IMAGE_URL ,
39867                 align: 'center'
39868             }
39869         ]
39870         
39871     },
39872     
39873  
39874     onRender : function(ct, position)
39875     {
39876         Roo.form.Field.superclass.onRender.call(this, ct, position);
39877         
39878         if(!this.el){
39879             var cfg = this.getAutoCreate();
39880             this.el = ct.createChild(cfg, position);
39881         }
39882         
39883         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
39884         
39885         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
39886             this.cb.renderer(this.data) :
39887             String.format('{0}',this.data[this.displayField]);
39888         
39889             
39890         this.el.child('div').dom.setAttribute('qtip',
39891                         String.format('{0}',this.data[this.tipField])
39892         );
39893         
39894         this.el.child('img').on('click', this.remove, this);
39895         
39896     },
39897    
39898     remove : function()
39899     {
39900         
39901         this.cb.items.remove(this);
39902         this.el.child('img').un('click', this.remove, this);
39903         this.el.remove();
39904         this.cb.updateHiddenEl();
39905     }
39906     
39907     
39908 });/*
39909  * Based on:
39910  * Ext JS Library 1.1.1
39911  * Copyright(c) 2006-2007, Ext JS, LLC.
39912  *
39913  * Originally Released Under LGPL - original licence link has changed is not relivant.
39914  *
39915  * Fork - LGPL
39916  * <script type="text/javascript">
39917  */
39918 /**
39919  * @class Roo.form.Checkbox
39920  * @extends Roo.form.Field
39921  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
39922  * @constructor
39923  * Creates a new Checkbox
39924  * @param {Object} config Configuration options
39925  */
39926 Roo.form.Checkbox = function(config){
39927     Roo.form.Checkbox.superclass.constructor.call(this, config);
39928     this.addEvents({
39929         /**
39930          * @event check
39931          * Fires when the checkbox is checked or unchecked.
39932              * @param {Roo.form.Checkbox} this This checkbox
39933              * @param {Boolean} checked The new checked value
39934              */
39935         check : true
39936     });
39937 };
39938
39939 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
39940     /**
39941      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
39942      */
39943     focusClass : undefined,
39944     /**
39945      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
39946      */
39947     fieldClass: "x-form-field",
39948     /**
39949      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
39950      */
39951     checked: false,
39952     /**
39953      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39954      * {tag: "input", type: "checkbox", autocomplete: "off"})
39955      */
39956     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
39957     /**
39958      * @cfg {String} boxLabel The text that appears beside the checkbox
39959      */
39960     boxLabel : "",
39961     /**
39962      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
39963      */  
39964     inputValue : '1',
39965     /**
39966      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
39967      */
39968      valueOff: '0', // value when not checked..
39969
39970     actionMode : 'viewEl', 
39971     //
39972     // private
39973     itemCls : 'x-menu-check-item x-form-item',
39974     groupClass : 'x-menu-group-item',
39975     inputType : 'hidden',
39976     
39977     
39978     inSetChecked: false, // check that we are not calling self...
39979     
39980     inputElement: false, // real input element?
39981     basedOn: false, // ????
39982     
39983     isFormField: true, // not sure where this is needed!!!!
39984
39985     onResize : function(){
39986         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
39987         if(!this.boxLabel){
39988             this.el.alignTo(this.wrap, 'c-c');
39989         }
39990     },
39991
39992     initEvents : function(){
39993         Roo.form.Checkbox.superclass.initEvents.call(this);
39994         this.el.on("click", this.onClick,  this);
39995         this.el.on("change", this.onClick,  this);
39996     },
39997
39998
39999     getResizeEl : function(){
40000         return this.wrap;
40001     },
40002
40003     getPositionEl : function(){
40004         return this.wrap;
40005     },
40006
40007     // private
40008     onRender : function(ct, position){
40009         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40010         /*
40011         if(this.inputValue !== undefined){
40012             this.el.dom.value = this.inputValue;
40013         }
40014         */
40015         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40016         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40017         var viewEl = this.wrap.createChild({ 
40018             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40019         this.viewEl = viewEl;   
40020         this.wrap.on('click', this.onClick,  this); 
40021         
40022         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40023         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40024         
40025         
40026         
40027         if(this.boxLabel){
40028             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40029         //    viewEl.on('click', this.onClick,  this); 
40030         }
40031         //if(this.checked){
40032             this.setChecked(this.checked);
40033         //}else{
40034             //this.checked = this.el.dom;
40035         //}
40036
40037     },
40038
40039     // private
40040     initValue : Roo.emptyFn,
40041
40042     /**
40043      * Returns the checked state of the checkbox.
40044      * @return {Boolean} True if checked, else false
40045      */
40046     getValue : function(){
40047         if(this.el){
40048             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40049         }
40050         return this.valueOff;
40051         
40052     },
40053
40054         // private
40055     onClick : function(){ 
40056         this.setChecked(!this.checked);
40057
40058         //if(this.el.dom.checked != this.checked){
40059         //    this.setValue(this.el.dom.checked);
40060        // }
40061     },
40062
40063     /**
40064      * Sets the checked state of the checkbox.
40065      * On is always based on a string comparison between inputValue and the param.
40066      * @param {Boolean/String} value - the value to set 
40067      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40068      */
40069     setValue : function(v,suppressEvent){
40070         
40071         
40072         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40073         //if(this.el && this.el.dom){
40074         //    this.el.dom.checked = this.checked;
40075         //    this.el.dom.defaultChecked = this.checked;
40076         //}
40077         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40078         //this.fireEvent("check", this, this.checked);
40079     },
40080     // private..
40081     setChecked : function(state,suppressEvent)
40082     {
40083         if (this.inSetChecked) {
40084             this.checked = state;
40085             return;
40086         }
40087         
40088     
40089         if(this.wrap){
40090             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40091         }
40092         this.checked = state;
40093         if(suppressEvent !== true){
40094             this.fireEvent('check', this, state);
40095         }
40096         this.inSetChecked = true;
40097         this.el.dom.value = state ? this.inputValue : this.valueOff;
40098         this.inSetChecked = false;
40099         
40100     },
40101     // handle setting of hidden value by some other method!!?!?
40102     setFromHidden: function()
40103     {
40104         if(!this.el){
40105             return;
40106         }
40107         //console.log("SET FROM HIDDEN");
40108         //alert('setFrom hidden');
40109         this.setValue(this.el.dom.value);
40110     },
40111     
40112     onDestroy : function()
40113     {
40114         if(this.viewEl){
40115             Roo.get(this.viewEl).remove();
40116         }
40117          
40118         Roo.form.Checkbox.superclass.onDestroy.call(this);
40119     }
40120
40121 });/*
40122  * Based on:
40123  * Ext JS Library 1.1.1
40124  * Copyright(c) 2006-2007, Ext JS, LLC.
40125  *
40126  * Originally Released Under LGPL - original licence link has changed is not relivant.
40127  *
40128  * Fork - LGPL
40129  * <script type="text/javascript">
40130  */
40131  
40132 /**
40133  * @class Roo.form.Radio
40134  * @extends Roo.form.Checkbox
40135  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40136  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40137  * @constructor
40138  * Creates a new Radio
40139  * @param {Object} config Configuration options
40140  */
40141 Roo.form.Radio = function(){
40142     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40143 };
40144 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40145     inputType: 'radio',
40146
40147     /**
40148      * If this radio is part of a group, it will return the selected value
40149      * @return {String}
40150      */
40151     getGroupValue : function(){
40152         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40153     },
40154     
40155     
40156     onRender : function(ct, position){
40157         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40158         
40159         if(this.inputValue !== undefined){
40160             this.el.dom.value = this.inputValue;
40161         }
40162          
40163         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40164         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40165         //var viewEl = this.wrap.createChild({ 
40166         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40167         //this.viewEl = viewEl;   
40168         //this.wrap.on('click', this.onClick,  this); 
40169         
40170         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40171         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40172         
40173         
40174         
40175         if(this.boxLabel){
40176             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40177         //    viewEl.on('click', this.onClick,  this); 
40178         }
40179          if(this.checked){
40180             this.el.dom.checked =   'checked' ;
40181         }
40182          
40183     } 
40184     
40185     
40186 });//<script type="text/javascript">
40187
40188 /*
40189  * Ext JS Library 1.1.1
40190  * Copyright(c) 2006-2007, Ext JS, LLC.
40191  * licensing@extjs.com
40192  * 
40193  * http://www.extjs.com/license
40194  */
40195  
40196  /*
40197   * 
40198   * Known bugs:
40199   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40200   * - IE ? - no idea how much works there.
40201   * 
40202   * 
40203   * 
40204   */
40205  
40206
40207 /**
40208  * @class Ext.form.HtmlEditor
40209  * @extends Ext.form.Field
40210  * Provides a lightweight HTML Editor component.
40211  *
40212  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40213  * 
40214  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40215  * supported by this editor.</b><br/><br/>
40216  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40217  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40218  */
40219 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40220       /**
40221      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40222      */
40223     toolbars : false,
40224     /**
40225      * @cfg {String} createLinkText The default text for the create link prompt
40226      */
40227     createLinkText : 'Please enter the URL for the link:',
40228     /**
40229      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40230      */
40231     defaultLinkValue : 'http:/'+'/',
40232    
40233      /**
40234      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40235      *                        Roo.resizable.
40236      */
40237     resizable : false,
40238      /**
40239      * @cfg {Number} height (in pixels)
40240      */   
40241     height: 300,
40242    /**
40243      * @cfg {Number} width (in pixels)
40244      */   
40245     width: 500,
40246     
40247     /**
40248      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40249      * 
40250      */
40251     stylesheets: false,
40252     
40253     // id of frame..
40254     frameId: false,
40255     
40256     // private properties
40257     validationEvent : false,
40258     deferHeight: true,
40259     initialized : false,
40260     activated : false,
40261     sourceEditMode : false,
40262     onFocus : Roo.emptyFn,
40263     iframePad:3,
40264     hideMode:'offsets',
40265     
40266     defaultAutoCreate : { // modified by initCompnoent..
40267         tag: "textarea",
40268         style:"width:500px;height:300px;",
40269         autocomplete: "off"
40270     },
40271
40272     // private
40273     initComponent : function(){
40274         this.addEvents({
40275             /**
40276              * @event initialize
40277              * Fires when the editor is fully initialized (including the iframe)
40278              * @param {HtmlEditor} this
40279              */
40280             initialize: true,
40281             /**
40282              * @event activate
40283              * Fires when the editor is first receives the focus. Any insertion must wait
40284              * until after this event.
40285              * @param {HtmlEditor} this
40286              */
40287             activate: true,
40288              /**
40289              * @event beforesync
40290              * Fires before the textarea is updated with content from the editor iframe. Return false
40291              * to cancel the sync.
40292              * @param {HtmlEditor} this
40293              * @param {String} html
40294              */
40295             beforesync: true,
40296              /**
40297              * @event beforepush
40298              * Fires before the iframe editor is updated with content from the textarea. Return false
40299              * to cancel the push.
40300              * @param {HtmlEditor} this
40301              * @param {String} html
40302              */
40303             beforepush: true,
40304              /**
40305              * @event sync
40306              * Fires when the textarea is updated with content from the editor iframe.
40307              * @param {HtmlEditor} this
40308              * @param {String} html
40309              */
40310             sync: true,
40311              /**
40312              * @event push
40313              * Fires when the iframe editor is updated with content from the textarea.
40314              * @param {HtmlEditor} this
40315              * @param {String} html
40316              */
40317             push: true,
40318              /**
40319              * @event editmodechange
40320              * Fires when the editor switches edit modes
40321              * @param {HtmlEditor} this
40322              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40323              */
40324             editmodechange: true,
40325             /**
40326              * @event editorevent
40327              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40328              * @param {HtmlEditor} this
40329              */
40330             editorevent: true
40331         });
40332         this.defaultAutoCreate =  {
40333             tag: "textarea",
40334             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40335             autocomplete: "off"
40336         };
40337     },
40338
40339     /**
40340      * Protected method that will not generally be called directly. It
40341      * is called when the editor creates its toolbar. Override this method if you need to
40342      * add custom toolbar buttons.
40343      * @param {HtmlEditor} editor
40344      */
40345     createToolbar : function(editor){
40346         if (!editor.toolbars || !editor.toolbars.length) {
40347             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40348         }
40349         
40350         for (var i =0 ; i < editor.toolbars.length;i++) {
40351             editor.toolbars[i] = Roo.factory(
40352                     typeof(editor.toolbars[i]) == 'string' ?
40353                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40354                 Roo.form.HtmlEditor);
40355             editor.toolbars[i].init(editor);
40356         }
40357          
40358         
40359     },
40360
40361     /**
40362      * Protected method that will not generally be called directly. It
40363      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40364      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40365      */
40366     getDocMarkup : function(){
40367         // body styles..
40368         var st = '';
40369         if (this.stylesheets === false) {
40370             
40371             Roo.get(document.head).select('style').each(function(node) {
40372                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40373             });
40374             
40375             Roo.get(document.head).select('link').each(function(node) { 
40376                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40377             });
40378             
40379         } else if (!this.stylesheets.length) {
40380                 // simple..
40381                 st = '<style type="text/css">' +
40382                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40383                    '</style>';
40384         } else {
40385             Roo.each(this.stylesheets, function(s) {
40386                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40387             });
40388             
40389         }
40390         
40391         st +=  '<style type="text/css">' +
40392             'IMG { cursor: pointer } ' +
40393         '</style>';
40394
40395         
40396         return '<html><head>' + st  +
40397             //<style type="text/css">' +
40398             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40399             //'</style>' +
40400             ' </head><body class="roo-htmleditor-body"></body></html>';
40401     },
40402
40403     // private
40404     onRender : function(ct, position)
40405     {
40406         var _t = this;
40407         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40408         this.el.dom.style.border = '0 none';
40409         this.el.dom.setAttribute('tabIndex', -1);
40410         this.el.addClass('x-hidden');
40411         if(Roo.isIE){ // fix IE 1px bogus margin
40412             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40413         }
40414         this.wrap = this.el.wrap({
40415             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
40416         });
40417         
40418         if (this.resizable) {
40419             this.resizeEl = new Roo.Resizable(this.wrap, {
40420                 pinned : true,
40421                 wrap: true,
40422                 dynamic : true,
40423                 minHeight : this.height,
40424                 height: this.height,
40425                 handles : this.resizable,
40426                 width: this.width,
40427                 listeners : {
40428                     resize : function(r, w, h) {
40429                         _t.onResize(w,h); // -something
40430                     }
40431                 }
40432             });
40433             
40434         }
40435
40436         this.frameId = Roo.id();
40437         
40438         this.createToolbar(this);
40439         
40440       
40441         
40442         var iframe = this.wrap.createChild({
40443             tag: 'iframe',
40444             id: this.frameId,
40445             name: this.frameId,
40446             frameBorder : 'no',
40447             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
40448         }, this.el
40449         );
40450         
40451        // console.log(iframe);
40452         //this.wrap.dom.appendChild(iframe);
40453
40454         this.iframe = iframe.dom;
40455
40456          this.assignDocWin();
40457         
40458         this.doc.designMode = 'on';
40459        
40460         this.doc.open();
40461         this.doc.write(this.getDocMarkup());
40462         this.doc.close();
40463
40464         
40465         var task = { // must defer to wait for browser to be ready
40466             run : function(){
40467                 //console.log("run task?" + this.doc.readyState);
40468                 this.assignDocWin();
40469                 if(this.doc.body || this.doc.readyState == 'complete'){
40470                     try {
40471                         this.doc.designMode="on";
40472                     } catch (e) {
40473                         return;
40474                     }
40475                     Roo.TaskMgr.stop(task);
40476                     this.initEditor.defer(10, this);
40477                 }
40478             },
40479             interval : 10,
40480             duration:10000,
40481             scope: this
40482         };
40483         Roo.TaskMgr.start(task);
40484
40485         if(!this.width){
40486             this.setSize(this.wrap.getSize());
40487         }
40488         if (this.resizeEl) {
40489             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
40490             // should trigger onReize..
40491         }
40492     },
40493
40494     // private
40495     onResize : function(w, h)
40496     {
40497         //Roo.log('resize: ' +w + ',' + h );
40498         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
40499         if(this.el && this.iframe){
40500             if(typeof w == 'number'){
40501                 var aw = w - this.wrap.getFrameWidth('lr');
40502                 this.el.setWidth(this.adjustWidth('textarea', aw));
40503                 this.iframe.style.width = aw + 'px';
40504             }
40505             if(typeof h == 'number'){
40506                 var tbh = 0;
40507                 for (var i =0; i < this.toolbars.length;i++) {
40508                     // fixme - ask toolbars for heights?
40509                     tbh += this.toolbars[i].tb.el.getHeight();
40510                     if (this.toolbars[i].footer) {
40511                         tbh += this.toolbars[i].footer.el.getHeight();
40512                     }
40513                 }
40514                 
40515                 
40516                 
40517                 
40518                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
40519                 ah -= 5; // knock a few pixes off for look..
40520                 this.el.setHeight(this.adjustWidth('textarea', ah));
40521                 this.iframe.style.height = ah + 'px';
40522                 if(this.doc){
40523                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
40524                 }
40525             }
40526         }
40527     },
40528
40529     /**
40530      * Toggles the editor between standard and source edit mode.
40531      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
40532      */
40533     toggleSourceEdit : function(sourceEditMode){
40534         
40535         this.sourceEditMode = sourceEditMode === true;
40536         
40537         if(this.sourceEditMode){
40538 //            Roo.log('in');
40539 //            Roo.log(this.syncValue());
40540             this.syncValue();
40541             this.iframe.className = 'x-hidden';
40542             this.el.removeClass('x-hidden');
40543             this.el.dom.removeAttribute('tabIndex');
40544             this.el.focus();
40545         }else{
40546 //            Roo.log('out')
40547 //            Roo.log(this.pushValue()); 
40548             this.pushValue();
40549             this.iframe.className = '';
40550             this.el.addClass('x-hidden');
40551             this.el.dom.setAttribute('tabIndex', -1);
40552             this.deferFocus();
40553         }
40554         this.setSize(this.wrap.getSize());
40555         this.fireEvent('editmodechange', this, this.sourceEditMode);
40556     },
40557
40558     // private used internally
40559     createLink : function(){
40560         var url = prompt(this.createLinkText, this.defaultLinkValue);
40561         if(url && url != 'http:/'+'/'){
40562             this.relayCmd('createlink', url);
40563         }
40564     },
40565
40566     // private (for BoxComponent)
40567     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40568
40569     // private (for BoxComponent)
40570     getResizeEl : function(){
40571         return this.wrap;
40572     },
40573
40574     // private (for BoxComponent)
40575     getPositionEl : function(){
40576         return this.wrap;
40577     },
40578
40579     // private
40580     initEvents : function(){
40581         this.originalValue = this.getValue();
40582     },
40583
40584     /**
40585      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40586      * @method
40587      */
40588     markInvalid : Roo.emptyFn,
40589     /**
40590      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40591      * @method
40592      */
40593     clearInvalid : Roo.emptyFn,
40594
40595     setValue : function(v){
40596         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
40597         this.pushValue();
40598     },
40599
40600     /**
40601      * Protected method that will not generally be called directly. If you need/want
40602      * custom HTML cleanup, this is the method you should override.
40603      * @param {String} html The HTML to be cleaned
40604      * return {String} The cleaned HTML
40605      */
40606     cleanHtml : function(html){
40607         html = String(html);
40608         if(html.length > 5){
40609             if(Roo.isSafari){ // strip safari nonsense
40610                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
40611             }
40612         }
40613         if(html == '&nbsp;'){
40614             html = '';
40615         }
40616         return html;
40617     },
40618
40619     /**
40620      * Protected method that will not generally be called directly. Syncs the contents
40621      * of the editor iframe with the textarea.
40622      */
40623     syncValue : function(){
40624         if(this.initialized){
40625             var bd = (this.doc.body || this.doc.documentElement);
40626             //this.cleanUpPaste(); -- this is done else where and causes havoc..
40627             var html = bd.innerHTML;
40628             if(Roo.isSafari){
40629                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
40630                 var m = bs.match(/text-align:(.*?);/i);
40631                 if(m && m[1]){
40632                     html = '<div style="'+m[0]+'">' + html + '</div>';
40633                 }
40634             }
40635             html = this.cleanHtml(html);
40636             // fix up the special chars.. normaly like back quotes in word...
40637             // however we do not want to do this with chinese..
40638             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
40639                 var cc = b.charCodeAt();
40640                 if (
40641                     (cc >= 0x4E00 && cc < 0xA000 ) ||
40642                     (cc >= 0x3400 && cc < 0x4E00 ) ||
40643                     (cc >= 0xf900 && cc < 0xfb00 )
40644                 ) {
40645                         return b;
40646                 }
40647                 return "&#"+cc+";" 
40648             });
40649             if(this.fireEvent('beforesync', this, html) !== false){
40650                 this.el.dom.value = html;
40651                 this.fireEvent('sync', this, html);
40652             }
40653         }
40654     },
40655
40656     /**
40657      * Protected method that will not generally be called directly. Pushes the value of the textarea
40658      * into the iframe editor.
40659      */
40660     pushValue : function(){
40661         if(this.initialized){
40662             var v = this.el.dom.value;
40663             
40664             if(v.length < 1){
40665                 v = '&#160;';
40666             }
40667             
40668             if(this.fireEvent('beforepush', this, v) !== false){
40669                 var d = (this.doc.body || this.doc.documentElement);
40670                 d.innerHTML = v;
40671                 this.cleanUpPaste();
40672                 this.el.dom.value = d.innerHTML;
40673                 this.fireEvent('push', this, v);
40674             }
40675         }
40676     },
40677
40678     // private
40679     deferFocus : function(){
40680         this.focus.defer(10, this);
40681     },
40682
40683     // doc'ed in Field
40684     focus : function(){
40685         if(this.win && !this.sourceEditMode){
40686             this.win.focus();
40687         }else{
40688             this.el.focus();
40689         }
40690     },
40691     
40692     assignDocWin: function()
40693     {
40694         var iframe = this.iframe;
40695         
40696          if(Roo.isIE){
40697             this.doc = iframe.contentWindow.document;
40698             this.win = iframe.contentWindow;
40699         } else {
40700             if (!Roo.get(this.frameId)) {
40701                 return;
40702             }
40703             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
40704             this.win = Roo.get(this.frameId).dom.contentWindow;
40705         }
40706     },
40707     
40708     // private
40709     initEditor : function(){
40710         //console.log("INIT EDITOR");
40711         this.assignDocWin();
40712         
40713         
40714         
40715         this.doc.designMode="on";
40716         this.doc.open();
40717         this.doc.write(this.getDocMarkup());
40718         this.doc.close();
40719         
40720         var dbody = (this.doc.body || this.doc.documentElement);
40721         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
40722         // this copies styles from the containing element into thsi one..
40723         // not sure why we need all of this..
40724         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
40725         ss['background-attachment'] = 'fixed'; // w3c
40726         dbody.bgProperties = 'fixed'; // ie
40727         Roo.DomHelper.applyStyles(dbody, ss);
40728         Roo.EventManager.on(this.doc, {
40729             //'mousedown': this.onEditorEvent,
40730             'mouseup': this.onEditorEvent,
40731             'dblclick': this.onEditorEvent,
40732             'click': this.onEditorEvent,
40733             'keyup': this.onEditorEvent,
40734             buffer:100,
40735             scope: this
40736         });
40737         if(Roo.isGecko){
40738             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
40739         }
40740         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
40741             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
40742         }
40743         this.initialized = true;
40744
40745         this.fireEvent('initialize', this);
40746         this.pushValue();
40747     },
40748
40749     // private
40750     onDestroy : function(){
40751         
40752         
40753         
40754         if(this.rendered){
40755             
40756             for (var i =0; i < this.toolbars.length;i++) {
40757                 // fixme - ask toolbars for heights?
40758                 this.toolbars[i].onDestroy();
40759             }
40760             
40761             this.wrap.dom.innerHTML = '';
40762             this.wrap.remove();
40763         }
40764     },
40765
40766     // private
40767     onFirstFocus : function(){
40768         
40769         this.assignDocWin();
40770         
40771         
40772         this.activated = true;
40773         for (var i =0; i < this.toolbars.length;i++) {
40774             this.toolbars[i].onFirstFocus();
40775         }
40776        
40777         if(Roo.isGecko){ // prevent silly gecko errors
40778             this.win.focus();
40779             var s = this.win.getSelection();
40780             if(!s.focusNode || s.focusNode.nodeType != 3){
40781                 var r = s.getRangeAt(0);
40782                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
40783                 r.collapse(true);
40784                 this.deferFocus();
40785             }
40786             try{
40787                 this.execCmd('useCSS', true);
40788                 this.execCmd('styleWithCSS', false);
40789             }catch(e){}
40790         }
40791         this.fireEvent('activate', this);
40792     },
40793
40794     // private
40795     adjustFont: function(btn){
40796         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
40797         //if(Roo.isSafari){ // safari
40798         //    adjust *= 2;
40799        // }
40800         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
40801         if(Roo.isSafari){ // safari
40802             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
40803             v =  (v < 10) ? 10 : v;
40804             v =  (v > 48) ? 48 : v;
40805             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
40806             
40807         }
40808         
40809         
40810         v = Math.max(1, v+adjust);
40811         
40812         this.execCmd('FontSize', v  );
40813     },
40814
40815     onEditorEvent : function(e){
40816         this.fireEvent('editorevent', this, e);
40817       //  this.updateToolbar();
40818         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
40819     },
40820
40821     insertTag : function(tg)
40822     {
40823         // could be a bit smarter... -> wrap the current selected tRoo..
40824         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
40825             
40826             range = this.createRange(this.getSelection());
40827             var wrappingNode = this.doc.createElement(tg.toLowerCase());
40828             wrappingNode.appendChild(range.extractContents());
40829             range.insertNode(wrappingNode);
40830
40831             return;
40832             
40833             
40834             
40835         }
40836         this.execCmd("formatblock",   tg);
40837         
40838     },
40839     
40840     insertText : function(txt)
40841     {
40842         
40843         
40844         var range = this.createRange();
40845         range.deleteContents();
40846                //alert(Sender.getAttribute('label'));
40847                
40848         range.insertNode(this.doc.createTextNode(txt));
40849     } ,
40850     
40851     // private
40852     relayBtnCmd : function(btn){
40853         this.relayCmd(btn.cmd);
40854     },
40855
40856     /**
40857      * Executes a Midas editor command on the editor document and performs necessary focus and
40858      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
40859      * @param {String} cmd The Midas command
40860      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40861      */
40862     relayCmd : function(cmd, value){
40863         this.win.focus();
40864         this.execCmd(cmd, value);
40865         this.fireEvent('editorevent', this);
40866         //this.updateToolbar();
40867         this.deferFocus();
40868     },
40869
40870     /**
40871      * Executes a Midas editor command directly on the editor document.
40872      * For visual commands, you should use {@link #relayCmd} instead.
40873      * <b>This should only be called after the editor is initialized.</b>
40874      * @param {String} cmd The Midas command
40875      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40876      */
40877     execCmd : function(cmd, value){
40878         this.doc.execCommand(cmd, false, value === undefined ? null : value);
40879         this.syncValue();
40880     },
40881  
40882  
40883    
40884     /**
40885      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
40886      * to insert tRoo.
40887      * @param {String} text | dom node.. 
40888      */
40889     insertAtCursor : function(text)
40890     {
40891         
40892         
40893         
40894         if(!this.activated){
40895             return;
40896         }
40897         /*
40898         if(Roo.isIE){
40899             this.win.focus();
40900             var r = this.doc.selection.createRange();
40901             if(r){
40902                 r.collapse(true);
40903                 r.pasteHTML(text);
40904                 this.syncValue();
40905                 this.deferFocus();
40906             
40907             }
40908             return;
40909         }
40910         */
40911         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
40912             this.win.focus();
40913             
40914             
40915             // from jquery ui (MIT licenced)
40916             var range, node;
40917             var win = this.win;
40918             
40919             if (win.getSelection && win.getSelection().getRangeAt) {
40920                 range = win.getSelection().getRangeAt(0);
40921                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
40922                 range.insertNode(node);
40923             } else if (win.document.selection && win.document.selection.createRange) {
40924                 // no firefox support
40925                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40926                 win.document.selection.createRange().pasteHTML(txt);
40927             } else {
40928                 // no firefox support
40929                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40930                 this.execCmd('InsertHTML', txt);
40931             } 
40932             
40933             this.syncValue();
40934             
40935             this.deferFocus();
40936         }
40937     },
40938  // private
40939     mozKeyPress : function(e){
40940         if(e.ctrlKey){
40941             var c = e.getCharCode(), cmd;
40942           
40943             if(c > 0){
40944                 c = String.fromCharCode(c).toLowerCase();
40945                 switch(c){
40946                     case 'b':
40947                         cmd = 'bold';
40948                         break;
40949                     case 'i':
40950                         cmd = 'italic';
40951                         break;
40952                     
40953                     case 'u':
40954                         cmd = 'underline';
40955                         break;
40956                     
40957                     case 'v':
40958                         this.cleanUpPaste.defer(100, this);
40959                         return;
40960                         
40961                 }
40962                 if(cmd){
40963                     this.win.focus();
40964                     this.execCmd(cmd);
40965                     this.deferFocus();
40966                     e.preventDefault();
40967                 }
40968                 
40969             }
40970         }
40971     },
40972
40973     // private
40974     fixKeys : function(){ // load time branching for fastest keydown performance
40975         if(Roo.isIE){
40976             return function(e){
40977                 var k = e.getKey(), r;
40978                 if(k == e.TAB){
40979                     e.stopEvent();
40980                     r = this.doc.selection.createRange();
40981                     if(r){
40982                         r.collapse(true);
40983                         r.pasteHTML('&#160;&#160;&#160;&#160;');
40984                         this.deferFocus();
40985                     }
40986                     return;
40987                 }
40988                 
40989                 if(k == e.ENTER){
40990                     r = this.doc.selection.createRange();
40991                     if(r){
40992                         var target = r.parentElement();
40993                         if(!target || target.tagName.toLowerCase() != 'li'){
40994                             e.stopEvent();
40995                             r.pasteHTML('<br />');
40996                             r.collapse(false);
40997                             r.select();
40998                         }
40999                     }
41000                 }
41001                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41002                     this.cleanUpPaste.defer(100, this);
41003                     return;
41004                 }
41005                 
41006                 
41007             };
41008         }else if(Roo.isOpera){
41009             return function(e){
41010                 var k = e.getKey();
41011                 if(k == e.TAB){
41012                     e.stopEvent();
41013                     this.win.focus();
41014                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41015                     this.deferFocus();
41016                 }
41017                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41018                     this.cleanUpPaste.defer(100, this);
41019                     return;
41020                 }
41021                 
41022             };
41023         }else if(Roo.isSafari){
41024             return function(e){
41025                 var k = e.getKey();
41026                 
41027                 if(k == e.TAB){
41028                     e.stopEvent();
41029                     this.execCmd('InsertText','\t');
41030                     this.deferFocus();
41031                     return;
41032                 }
41033                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41034                     this.cleanUpPaste.defer(100, this);
41035                     return;
41036                 }
41037                 
41038              };
41039         }
41040     }(),
41041     
41042     getAllAncestors: function()
41043     {
41044         var p = this.getSelectedNode();
41045         var a = [];
41046         if (!p) {
41047             a.push(p); // push blank onto stack..
41048             p = this.getParentElement();
41049         }
41050         
41051         
41052         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41053             a.push(p);
41054             p = p.parentNode;
41055         }
41056         a.push(this.doc.body);
41057         return a;
41058     },
41059     lastSel : false,
41060     lastSelNode : false,
41061     
41062     
41063     getSelection : function() 
41064     {
41065         this.assignDocWin();
41066         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41067     },
41068     
41069     getSelectedNode: function() 
41070     {
41071         // this may only work on Gecko!!!
41072         
41073         // should we cache this!!!!
41074         
41075         
41076         
41077          
41078         var range = this.createRange(this.getSelection()).cloneRange();
41079         
41080         if (Roo.isIE) {
41081             var parent = range.parentElement();
41082             while (true) {
41083                 var testRange = range.duplicate();
41084                 testRange.moveToElementText(parent);
41085                 if (testRange.inRange(range)) {
41086                     break;
41087                 }
41088                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41089                     break;
41090                 }
41091                 parent = parent.parentElement;
41092             }
41093             return parent;
41094         }
41095         
41096         // is ancestor a text element.
41097         var ac =  range.commonAncestorContainer;
41098         if (ac.nodeType == 3) {
41099             ac = ac.parentNode;
41100         }
41101         
41102         var ar = ac.childNodes;
41103          
41104         var nodes = [];
41105         var other_nodes = [];
41106         var has_other_nodes = false;
41107         for (var i=0;i<ar.length;i++) {
41108             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41109                 continue;
41110             }
41111             // fullly contained node.
41112             
41113             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41114                 nodes.push(ar[i]);
41115                 continue;
41116             }
41117             
41118             // probably selected..
41119             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41120                 other_nodes.push(ar[i]);
41121                 continue;
41122             }
41123             // outer..
41124             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41125                 continue;
41126             }
41127             
41128             
41129             has_other_nodes = true;
41130         }
41131         if (!nodes.length && other_nodes.length) {
41132             nodes= other_nodes;
41133         }
41134         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41135             return false;
41136         }
41137         
41138         return nodes[0];
41139     },
41140     createRange: function(sel)
41141     {
41142         // this has strange effects when using with 
41143         // top toolbar - not sure if it's a great idea.
41144         //this.editor.contentWindow.focus();
41145         if (typeof sel != "undefined") {
41146             try {
41147                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41148             } catch(e) {
41149                 return this.doc.createRange();
41150             }
41151         } else {
41152             return this.doc.createRange();
41153         }
41154     },
41155     getParentElement: function()
41156     {
41157         
41158         this.assignDocWin();
41159         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41160         
41161         var range = this.createRange(sel);
41162          
41163         try {
41164             var p = range.commonAncestorContainer;
41165             while (p.nodeType == 3) { // text node
41166                 p = p.parentNode;
41167             }
41168             return p;
41169         } catch (e) {
41170             return null;
41171         }
41172     
41173     },
41174     /***
41175      *
41176      * Range intersection.. the hard stuff...
41177      *  '-1' = before
41178      *  '0' = hits..
41179      *  '1' = after.
41180      *         [ -- selected range --- ]
41181      *   [fail]                        [fail]
41182      *
41183      *    basically..
41184      *      if end is before start or  hits it. fail.
41185      *      if start is after end or hits it fail.
41186      *
41187      *   if either hits (but other is outside. - then it's not 
41188      *   
41189      *    
41190      **/
41191     
41192     
41193     // @see http://www.thismuchiknow.co.uk/?p=64.
41194     rangeIntersectsNode : function(range, node)
41195     {
41196         var nodeRange = node.ownerDocument.createRange();
41197         try {
41198             nodeRange.selectNode(node);
41199         } catch (e) {
41200             nodeRange.selectNodeContents(node);
41201         }
41202     
41203         var rangeStartRange = range.cloneRange();
41204         rangeStartRange.collapse(true);
41205     
41206         var rangeEndRange = range.cloneRange();
41207         rangeEndRange.collapse(false);
41208     
41209         var nodeStartRange = nodeRange.cloneRange();
41210         nodeStartRange.collapse(true);
41211     
41212         var nodeEndRange = nodeRange.cloneRange();
41213         nodeEndRange.collapse(false);
41214     
41215         return rangeStartRange.compareBoundaryPoints(
41216                  Range.START_TO_START, nodeEndRange) == -1 &&
41217                rangeEndRange.compareBoundaryPoints(
41218                  Range.START_TO_START, nodeStartRange) == 1;
41219         
41220          
41221     },
41222     rangeCompareNode : function(range, node)
41223     {
41224         var nodeRange = node.ownerDocument.createRange();
41225         try {
41226             nodeRange.selectNode(node);
41227         } catch (e) {
41228             nodeRange.selectNodeContents(node);
41229         }
41230         
41231         
41232         range.collapse(true);
41233     
41234         nodeRange.collapse(true);
41235      
41236         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41237         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41238          
41239         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41240         
41241         var nodeIsBefore   =  ss == 1;
41242         var nodeIsAfter    = ee == -1;
41243         
41244         if (nodeIsBefore && nodeIsAfter)
41245             return 0; // outer
41246         if (!nodeIsBefore && nodeIsAfter)
41247             return 1; //right trailed.
41248         
41249         if (nodeIsBefore && !nodeIsAfter)
41250             return 2;  // left trailed.
41251         // fully contined.
41252         return 3;
41253     },
41254
41255     // private? - in a new class?
41256     cleanUpPaste :  function()
41257     {
41258         // cleans up the whole document..
41259          Roo.log('cleanuppaste');
41260         this.cleanUpChildren(this.doc.body);
41261         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41262         if (clean != this.doc.body.innerHTML) {
41263             this.doc.body.innerHTML = clean;
41264         }
41265         
41266     },
41267     
41268     cleanWordChars : function(input) {// change the chars to hex code
41269         var he = Roo.form.HtmlEditor;
41270         
41271         var output = input;
41272         Roo.each(he.swapCodes, function(sw) { 
41273             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41274             
41275             output = output.replace(swapper, sw[1]);
41276         });
41277         
41278         return output;
41279     },
41280     
41281     
41282     cleanUpChildren : function (n)
41283     {
41284         if (!n.childNodes.length) {
41285             return;
41286         }
41287         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41288            this.cleanUpChild(n.childNodes[i]);
41289         }
41290     },
41291     
41292     
41293         
41294     
41295     cleanUpChild : function (node)
41296     {
41297         var ed = this;
41298         //console.log(node);
41299         if (node.nodeName == "#text") {
41300             // clean up silly Windows -- stuff?
41301             return; 
41302         }
41303         if (node.nodeName == "#comment") {
41304             node.parentNode.removeChild(node);
41305             // clean up silly Windows -- stuff?
41306             return; 
41307         }
41308         
41309         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41310             // remove node.
41311             node.parentNode.removeChild(node);
41312             return;
41313             
41314         }
41315         
41316         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41317         
41318         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41319         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41320         
41321         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41322         //    remove_keep_children = true;
41323         //}
41324         
41325         if (remove_keep_children) {
41326             this.cleanUpChildren(node);
41327             // inserts everything just before this node...
41328             while (node.childNodes.length) {
41329                 var cn = node.childNodes[0];
41330                 node.removeChild(cn);
41331                 node.parentNode.insertBefore(cn, node);
41332             }
41333             node.parentNode.removeChild(node);
41334             return;
41335         }
41336         
41337         if (!node.attributes || !node.attributes.length) {
41338             this.cleanUpChildren(node);
41339             return;
41340         }
41341         
41342         function cleanAttr(n,v)
41343         {
41344             
41345             if (v.match(/^\./) || v.match(/^\//)) {
41346                 return;
41347             }
41348             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41349                 return;
41350             }
41351             if (v.match(/^#/)) {
41352                 return;
41353             }
41354 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41355             node.removeAttribute(n);
41356             
41357         }
41358         
41359         function cleanStyle(n,v)
41360         {
41361             if (v.match(/expression/)) { //XSS?? should we even bother..
41362                 node.removeAttribute(n);
41363                 return;
41364             }
41365             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41366             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41367             
41368             
41369             var parts = v.split(/;/);
41370             var clean = [];
41371             
41372             Roo.each(parts, function(p) {
41373                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41374                 if (!p.length) {
41375                     return true;
41376                 }
41377                 var l = p.split(':').shift().replace(/\s+/g,'');
41378                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41379                 
41380                 
41381                 if ( cblack.indexOf(l) > -1) {
41382 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41383                     //node.removeAttribute(n);
41384                     return true;
41385                 }
41386                 //Roo.log()
41387                 // only allow 'c whitelisted system attributes'
41388                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41389 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41390                     //node.removeAttribute(n);
41391                     return true;
41392                 }
41393                 
41394                 
41395                  
41396                 
41397                 clean.push(p);
41398                 return true;
41399             });
41400             if (clean.length) { 
41401                 node.setAttribute(n, clean.join(';'));
41402             } else {
41403                 node.removeAttribute(n);
41404             }
41405             
41406         }
41407         
41408         
41409         for (var i = node.attributes.length-1; i > -1 ; i--) {
41410             var a = node.attributes[i];
41411             //console.log(a);
41412             
41413             if (a.name.toLowerCase().substr(0,2)=='on')  {
41414                 node.removeAttribute(a.name);
41415                 continue;
41416             }
41417             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
41418                 node.removeAttribute(a.name);
41419                 continue;
41420             }
41421             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
41422                 cleanAttr(a.name,a.value); // fixme..
41423                 continue;
41424             }
41425             if (a.name == 'style') {
41426                 cleanStyle(a.name,a.value);
41427                 continue;
41428             }
41429             /// clean up MS crap..
41430             // tecnically this should be a list of valid class'es..
41431             
41432             
41433             if (a.name == 'class') {
41434                 if (a.value.match(/^Mso/)) {
41435                     node.className = '';
41436                 }
41437                 
41438                 if (a.value.match(/body/)) {
41439                     node.className = '';
41440                 }
41441                 continue;
41442             }
41443             
41444             // style cleanup!?
41445             // class cleanup?
41446             
41447         }
41448         
41449         
41450         this.cleanUpChildren(node);
41451         
41452         
41453     }
41454     
41455     
41456     // hide stuff that is not compatible
41457     /**
41458      * @event blur
41459      * @hide
41460      */
41461     /**
41462      * @event change
41463      * @hide
41464      */
41465     /**
41466      * @event focus
41467      * @hide
41468      */
41469     /**
41470      * @event specialkey
41471      * @hide
41472      */
41473     /**
41474      * @cfg {String} fieldClass @hide
41475      */
41476     /**
41477      * @cfg {String} focusClass @hide
41478      */
41479     /**
41480      * @cfg {String} autoCreate @hide
41481      */
41482     /**
41483      * @cfg {String} inputType @hide
41484      */
41485     /**
41486      * @cfg {String} invalidClass @hide
41487      */
41488     /**
41489      * @cfg {String} invalidText @hide
41490      */
41491     /**
41492      * @cfg {String} msgFx @hide
41493      */
41494     /**
41495      * @cfg {String} validateOnBlur @hide
41496      */
41497 });
41498
41499 Roo.form.HtmlEditor.white = [
41500         'area', 'br', 'img', 'input', 'hr', 'wbr',
41501         
41502        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
41503        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
41504        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
41505        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
41506        'table',   'ul',         'xmp', 
41507        
41508        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
41509       'thead',   'tr', 
41510      
41511       'dir', 'menu', 'ol', 'ul', 'dl',
41512        
41513       'embed',  'object'
41514 ];
41515
41516
41517 Roo.form.HtmlEditor.black = [
41518     //    'embed',  'object', // enable - backend responsiblity to clean thiese
41519         'applet', // 
41520         'base',   'basefont', 'bgsound', 'blink',  'body', 
41521         'frame',  'frameset', 'head',    'html',   'ilayer', 
41522         'iframe', 'layer',  'link',     'meta',    'object',   
41523         'script', 'style' ,'title',  'xml' // clean later..
41524 ];
41525 Roo.form.HtmlEditor.clean = [
41526     'script', 'style', 'title', 'xml'
41527 ];
41528 Roo.form.HtmlEditor.remove = [
41529     'font'
41530 ];
41531 // attributes..
41532
41533 Roo.form.HtmlEditor.ablack = [
41534     'on'
41535 ];
41536     
41537 Roo.form.HtmlEditor.aclean = [ 
41538     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
41539 ];
41540
41541 // protocols..
41542 Roo.form.HtmlEditor.pwhite= [
41543         'http',  'https',  'mailto'
41544 ];
41545
41546 // white listed style attributes.
41547 Roo.form.HtmlEditor.cwhite= [
41548       //  'text-align', /// default is to allow most things..
41549       
41550          
41551 //        'font-size'//??
41552 ];
41553
41554 // black listed style attributes.
41555 Roo.form.HtmlEditor.cblack= [
41556       //  'font-size' -- this can be set by the project 
41557 ];
41558
41559
41560 Roo.form.HtmlEditor.swapCodes   =[ 
41561     [    8211, "--" ], 
41562     [    8212, "--" ], 
41563     [    8216,  "'" ],  
41564     [    8217, "'" ],  
41565     [    8220, '"' ],  
41566     [    8221, '"' ],  
41567     [    8226, "*" ],  
41568     [    8230, "..." ]
41569 ]; 
41570
41571     // <script type="text/javascript">
41572 /*
41573  * Based on
41574  * Ext JS Library 1.1.1
41575  * Copyright(c) 2006-2007, Ext JS, LLC.
41576  *  
41577  
41578  */
41579
41580 /**
41581  * @class Roo.form.HtmlEditorToolbar1
41582  * Basic Toolbar
41583  * 
41584  * Usage:
41585  *
41586  new Roo.form.HtmlEditor({
41587     ....
41588     toolbars : [
41589         new Roo.form.HtmlEditorToolbar1({
41590             disable : { fonts: 1 , format: 1, ..., ... , ...],
41591             btns : [ .... ]
41592         })
41593     }
41594      
41595  * 
41596  * @cfg {Object} disable List of elements to disable..
41597  * @cfg {Array} btns List of additional buttons.
41598  * 
41599  * 
41600  * NEEDS Extra CSS? 
41601  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
41602  */
41603  
41604 Roo.form.HtmlEditor.ToolbarStandard = function(config)
41605 {
41606     
41607     Roo.apply(this, config);
41608     
41609     // default disabled, based on 'good practice'..
41610     this.disable = this.disable || {};
41611     Roo.applyIf(this.disable, {
41612         fontSize : true,
41613         colors : true,
41614         specialElements : true
41615     });
41616     
41617     
41618     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41619     // dont call parent... till later.
41620 }
41621
41622 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
41623     
41624     tb: false,
41625     
41626     rendered: false,
41627     
41628     editor : false,
41629     /**
41630      * @cfg {Object} disable  List of toolbar elements to disable
41631          
41632      */
41633     disable : false,
41634       /**
41635      * @cfg {Array} fontFamilies An array of available font families
41636      */
41637     fontFamilies : [
41638         'Arial',
41639         'Courier New',
41640         'Tahoma',
41641         'Times New Roman',
41642         'Verdana'
41643     ],
41644     
41645     specialChars : [
41646            "&#169;",
41647           "&#174;",     
41648           "&#8482;",    
41649           "&#163;" ,    
41650          // "&#8212;",    
41651           "&#8230;",    
41652           "&#247;" ,    
41653         //  "&#225;" ,     ?? a acute?
41654            "&#8364;"    , //Euro
41655        //   "&#8220;"    ,
41656         //  "&#8221;"    ,
41657         //  "&#8226;"    ,
41658           "&#176;"  //   , // degrees
41659
41660          // "&#233;"     , // e ecute
41661          // "&#250;"     , // u ecute?
41662     ],
41663     
41664     specialElements : [
41665         {
41666             text: "Insert Table",
41667             xtype: 'MenuItem',
41668             xns : Roo.Menu,
41669             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
41670                 
41671         },
41672         {    
41673             text: "Insert Image",
41674             xtype: 'MenuItem',
41675             xns : Roo.Menu,
41676             ihtml : '<img src="about:blank"/>'
41677             
41678         }
41679         
41680          
41681     ],
41682     
41683     
41684     inputElements : [ 
41685             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
41686             "input:submit", "input:button", "select", "textarea", "label" ],
41687     formats : [
41688         ["p"] ,  
41689         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
41690         ["pre"],[ "code"], 
41691         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
41692         ['div'],['span']
41693     ],
41694     
41695     cleanStyles : [
41696         "font-size"
41697     ],
41698      /**
41699      * @cfg {String} defaultFont default font to use.
41700      */
41701     defaultFont: 'tahoma',
41702    
41703     fontSelect : false,
41704     
41705     
41706     formatCombo : false,
41707     
41708     init : function(editor)
41709     {
41710         this.editor = editor;
41711         
41712         
41713         var fid = editor.frameId;
41714         var etb = this;
41715         function btn(id, toggle, handler){
41716             var xid = fid + '-'+ id ;
41717             return {
41718                 id : xid,
41719                 cmd : id,
41720                 cls : 'x-btn-icon x-edit-'+id,
41721                 enableToggle:toggle !== false,
41722                 scope: editor, // was editor...
41723                 handler:handler||editor.relayBtnCmd,
41724                 clickEvent:'mousedown',
41725                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41726                 tabIndex:-1
41727             };
41728         }
41729         
41730         
41731         
41732         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
41733         this.tb = tb;
41734          // stop form submits
41735         tb.el.on('click', function(e){
41736             e.preventDefault(); // what does this do?
41737         });
41738
41739         if(!this.disable.font) { // && !Roo.isSafari){
41740             /* why no safari for fonts 
41741             editor.fontSelect = tb.el.createChild({
41742                 tag:'select',
41743                 tabIndex: -1,
41744                 cls:'x-font-select',
41745                 html: this.createFontOptions()
41746             });
41747             
41748             editor.fontSelect.on('change', function(){
41749                 var font = editor.fontSelect.dom.value;
41750                 editor.relayCmd('fontname', font);
41751                 editor.deferFocus();
41752             }, editor);
41753             
41754             tb.add(
41755                 editor.fontSelect.dom,
41756                 '-'
41757             );
41758             */
41759             
41760         };
41761         if(!this.disable.formats){
41762             this.formatCombo = new Roo.form.ComboBox({
41763                 store: new Roo.data.SimpleStore({
41764                     id : 'tag',
41765                     fields: ['tag'],
41766                     data : this.formats // from states.js
41767                 }),
41768                 blockFocus : true,
41769                 name : '',
41770                 //autoCreate : {tag: "div",  size: "20"},
41771                 displayField:'tag',
41772                 typeAhead: false,
41773                 mode: 'local',
41774                 editable : false,
41775                 triggerAction: 'all',
41776                 emptyText:'Add tag',
41777                 selectOnFocus:true,
41778                 width:135,
41779                 listeners : {
41780                     'select': function(c, r, i) {
41781                         editor.insertTag(r.get('tag'));
41782                         editor.focus();
41783                     }
41784                 }
41785
41786             });
41787             tb.addField(this.formatCombo);
41788             
41789         }
41790         
41791         if(!this.disable.format){
41792             tb.add(
41793                 btn('bold'),
41794                 btn('italic'),
41795                 btn('underline')
41796             );
41797         };
41798         if(!this.disable.fontSize){
41799             tb.add(
41800                 '-',
41801                 
41802                 
41803                 btn('increasefontsize', false, editor.adjustFont),
41804                 btn('decreasefontsize', false, editor.adjustFont)
41805             );
41806         };
41807         
41808         
41809         if(!this.disable.colors){
41810             tb.add(
41811                 '-', {
41812                     id:editor.frameId +'-forecolor',
41813                     cls:'x-btn-icon x-edit-forecolor',
41814                     clickEvent:'mousedown',
41815                     tooltip: this.buttonTips['forecolor'] || undefined,
41816                     tabIndex:-1,
41817                     menu : new Roo.menu.ColorMenu({
41818                         allowReselect: true,
41819                         focus: Roo.emptyFn,
41820                         value:'000000',
41821                         plain:true,
41822                         selectHandler: function(cp, color){
41823                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
41824                             editor.deferFocus();
41825                         },
41826                         scope: editor,
41827                         clickEvent:'mousedown'
41828                     })
41829                 }, {
41830                     id:editor.frameId +'backcolor',
41831                     cls:'x-btn-icon x-edit-backcolor',
41832                     clickEvent:'mousedown',
41833                     tooltip: this.buttonTips['backcolor'] || undefined,
41834                     tabIndex:-1,
41835                     menu : new Roo.menu.ColorMenu({
41836                         focus: Roo.emptyFn,
41837                         value:'FFFFFF',
41838                         plain:true,
41839                         allowReselect: true,
41840                         selectHandler: function(cp, color){
41841                             if(Roo.isGecko){
41842                                 editor.execCmd('useCSS', false);
41843                                 editor.execCmd('hilitecolor', color);
41844                                 editor.execCmd('useCSS', true);
41845                                 editor.deferFocus();
41846                             }else{
41847                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
41848                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
41849                                 editor.deferFocus();
41850                             }
41851                         },
41852                         scope:editor,
41853                         clickEvent:'mousedown'
41854                     })
41855                 }
41856             );
41857         };
41858         // now add all the items...
41859         
41860
41861         if(!this.disable.alignments){
41862             tb.add(
41863                 '-',
41864                 btn('justifyleft'),
41865                 btn('justifycenter'),
41866                 btn('justifyright')
41867             );
41868         };
41869
41870         //if(!Roo.isSafari){
41871             if(!this.disable.links){
41872                 tb.add(
41873                     '-',
41874                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
41875                 );
41876             };
41877
41878             if(!this.disable.lists){
41879                 tb.add(
41880                     '-',
41881                     btn('insertorderedlist'),
41882                     btn('insertunorderedlist')
41883                 );
41884             }
41885             if(!this.disable.sourceEdit){
41886                 tb.add(
41887                     '-',
41888                     btn('sourceedit', true, function(btn){
41889                         this.toggleSourceEdit(btn.pressed);
41890                     })
41891                 );
41892             }
41893         //}
41894         
41895         var smenu = { };
41896         // special menu.. - needs to be tidied up..
41897         if (!this.disable.special) {
41898             smenu = {
41899                 text: "&#169;",
41900                 cls: 'x-edit-none',
41901                 
41902                 menu : {
41903                     items : []
41904                 }
41905             };
41906             for (var i =0; i < this.specialChars.length; i++) {
41907                 smenu.menu.items.push({
41908                     
41909                     html: this.specialChars[i],
41910                     handler: function(a,b) {
41911                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
41912                         //editor.insertAtCursor(a.html);
41913                         
41914                     },
41915                     tabIndex:-1
41916                 });
41917             }
41918             
41919             
41920             tb.add(smenu);
41921             
41922             
41923         }
41924         
41925         var cmenu = { };
41926         if (!this.disable.cleanStyles) {
41927             cmenu = {
41928                 cls: 'x-btn-icon x-btn-clear',
41929                 
41930                 menu : {
41931                     items : []
41932                 }
41933             };
41934             for (var i =0; i < this.cleanStyles.length; i++) {
41935                 cmenu.menu.items.push({
41936                     
41937                     html: this.cleanStyles[i],
41938                     handler: function(a,b) {
41939                         var c = Roo.get(editor.doc.body);
41940                         c.select('[style]').each(function(s) {
41941                             s.dom.style.removeProperty(a.html);
41942                         });
41943                         
41944                     },
41945                     tabIndex:-1
41946                 });
41947             }
41948             
41949             tb.add(cmenu);
41950         }
41951          
41952         if (!this.disable.specialElements) {
41953             var semenu = {
41954                 text: "Other;",
41955                 cls: 'x-edit-none',
41956                 menu : {
41957                     items : []
41958                 }
41959             };
41960             for (var i =0; i < this.specialElements.length; i++) {
41961                 semenu.menu.items.push(
41962                     Roo.apply({ 
41963                         handler: function(a,b) {
41964                             editor.insertAtCursor(this.ihtml);
41965                         }
41966                     }, this.specialElements[i])
41967                 );
41968                     
41969             }
41970             
41971             tb.add(semenu);
41972             
41973             
41974         }
41975          
41976         
41977         if (this.btns) {
41978             for(var i =0; i< this.btns.length;i++) {
41979                 var b = Roo.factory(this.btns[i],Roo.form);
41980                 b.cls =  'x-edit-none';
41981                 b.scope = editor;
41982                 tb.add(b);
41983             }
41984         
41985         }
41986         
41987         
41988         
41989         // disable everything...
41990         
41991         this.tb.items.each(function(item){
41992            if(item.id != editor.frameId+ '-sourceedit'){
41993                 item.disable();
41994             }
41995         });
41996         this.rendered = true;
41997         
41998         // the all the btns;
41999         editor.on('editorevent', this.updateToolbar, this);
42000         // other toolbars need to implement this..
42001         //editor.on('editmodechange', this.updateToolbar, this);
42002     },
42003     
42004     
42005     
42006     /**
42007      * Protected method that will not generally be called directly. It triggers
42008      * a toolbar update by reading the markup state of the current selection in the editor.
42009      */
42010     updateToolbar: function(){
42011
42012         if(!this.editor.activated){
42013             this.editor.onFirstFocus();
42014             return;
42015         }
42016
42017         var btns = this.tb.items.map, 
42018             doc = this.editor.doc,
42019             frameId = this.editor.frameId;
42020
42021         if(!this.disable.font && !Roo.isSafari){
42022             /*
42023             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42024             if(name != this.fontSelect.dom.value){
42025                 this.fontSelect.dom.value = name;
42026             }
42027             */
42028         }
42029         if(!this.disable.format){
42030             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42031             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42032             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42033         }
42034         if(!this.disable.alignments){
42035             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42036             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42037             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42038         }
42039         if(!Roo.isSafari && !this.disable.lists){
42040             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42041             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42042         }
42043         
42044         var ans = this.editor.getAllAncestors();
42045         if (this.formatCombo) {
42046             
42047             
42048             var store = this.formatCombo.store;
42049             this.formatCombo.setValue("");
42050             for (var i =0; i < ans.length;i++) {
42051                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42052                     // select it..
42053                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42054                     break;
42055                 }
42056             }
42057         }
42058         
42059         
42060         
42061         // hides menus... - so this cant be on a menu...
42062         Roo.menu.MenuMgr.hideAll();
42063
42064         //this.editorsyncValue();
42065     },
42066    
42067     
42068     createFontOptions : function(){
42069         var buf = [], fs = this.fontFamilies, ff, lc;
42070         
42071         
42072         
42073         for(var i = 0, len = fs.length; i< len; i++){
42074             ff = fs[i];
42075             lc = ff.toLowerCase();
42076             buf.push(
42077                 '<option value="',lc,'" style="font-family:',ff,';"',
42078                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42079                     ff,
42080                 '</option>'
42081             );
42082         }
42083         return buf.join('');
42084     },
42085     
42086     toggleSourceEdit : function(sourceEditMode){
42087         if(sourceEditMode === undefined){
42088             sourceEditMode = !this.sourceEditMode;
42089         }
42090         this.sourceEditMode = sourceEditMode === true;
42091         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42092         // just toggle the button?
42093         if(btn.pressed !== this.editor.sourceEditMode){
42094             btn.toggle(this.editor.sourceEditMode);
42095             return;
42096         }
42097         
42098         if(this.sourceEditMode){
42099             this.tb.items.each(function(item){
42100                 if(item.cmd != 'sourceedit'){
42101                     item.disable();
42102                 }
42103             });
42104           
42105         }else{
42106             if(this.initialized){
42107                 this.tb.items.each(function(item){
42108                     item.enable();
42109                 });
42110             }
42111             
42112         }
42113         // tell the editor that it's been pressed..
42114         this.editor.toggleSourceEdit(sourceEditMode);
42115        
42116     },
42117      /**
42118      * Object collection of toolbar tooltips for the buttons in the editor. The key
42119      * is the command id associated with that button and the value is a valid QuickTips object.
42120      * For example:
42121 <pre><code>
42122 {
42123     bold : {
42124         title: 'Bold (Ctrl+B)',
42125         text: 'Make the selected text bold.',
42126         cls: 'x-html-editor-tip'
42127     },
42128     italic : {
42129         title: 'Italic (Ctrl+I)',
42130         text: 'Make the selected text italic.',
42131         cls: 'x-html-editor-tip'
42132     },
42133     ...
42134 </code></pre>
42135     * @type Object
42136      */
42137     buttonTips : {
42138         bold : {
42139             title: 'Bold (Ctrl+B)',
42140             text: 'Make the selected text bold.',
42141             cls: 'x-html-editor-tip'
42142         },
42143         italic : {
42144             title: 'Italic (Ctrl+I)',
42145             text: 'Make the selected text italic.',
42146             cls: 'x-html-editor-tip'
42147         },
42148         underline : {
42149             title: 'Underline (Ctrl+U)',
42150             text: 'Underline the selected text.',
42151             cls: 'x-html-editor-tip'
42152         },
42153         increasefontsize : {
42154             title: 'Grow Text',
42155             text: 'Increase the font size.',
42156             cls: 'x-html-editor-tip'
42157         },
42158         decreasefontsize : {
42159             title: 'Shrink Text',
42160             text: 'Decrease the font size.',
42161             cls: 'x-html-editor-tip'
42162         },
42163         backcolor : {
42164             title: 'Text Highlight Color',
42165             text: 'Change the background color of the selected text.',
42166             cls: 'x-html-editor-tip'
42167         },
42168         forecolor : {
42169             title: 'Font Color',
42170             text: 'Change the color of the selected text.',
42171             cls: 'x-html-editor-tip'
42172         },
42173         justifyleft : {
42174             title: 'Align Text Left',
42175             text: 'Align text to the left.',
42176             cls: 'x-html-editor-tip'
42177         },
42178         justifycenter : {
42179             title: 'Center Text',
42180             text: 'Center text in the editor.',
42181             cls: 'x-html-editor-tip'
42182         },
42183         justifyright : {
42184             title: 'Align Text Right',
42185             text: 'Align text to the right.',
42186             cls: 'x-html-editor-tip'
42187         },
42188         insertunorderedlist : {
42189             title: 'Bullet List',
42190             text: 'Start a bulleted list.',
42191             cls: 'x-html-editor-tip'
42192         },
42193         insertorderedlist : {
42194             title: 'Numbered List',
42195             text: 'Start a numbered list.',
42196             cls: 'x-html-editor-tip'
42197         },
42198         createlink : {
42199             title: 'Hyperlink',
42200             text: 'Make the selected text a hyperlink.',
42201             cls: 'x-html-editor-tip'
42202         },
42203         sourceedit : {
42204             title: 'Source Edit',
42205             text: 'Switch to source editing mode.',
42206             cls: 'x-html-editor-tip'
42207         }
42208     },
42209     // private
42210     onDestroy : function(){
42211         if(this.rendered){
42212             
42213             this.tb.items.each(function(item){
42214                 if(item.menu){
42215                     item.menu.removeAll();
42216                     if(item.menu.el){
42217                         item.menu.el.destroy();
42218                     }
42219                 }
42220                 item.destroy();
42221             });
42222              
42223         }
42224     },
42225     onFirstFocus: function() {
42226         this.tb.items.each(function(item){
42227            item.enable();
42228         });
42229     }
42230 });
42231
42232
42233
42234
42235 // <script type="text/javascript">
42236 /*
42237  * Based on
42238  * Ext JS Library 1.1.1
42239  * Copyright(c) 2006-2007, Ext JS, LLC.
42240  *  
42241  
42242  */
42243
42244  
42245 /**
42246  * @class Roo.form.HtmlEditor.ToolbarContext
42247  * Context Toolbar
42248  * 
42249  * Usage:
42250  *
42251  new Roo.form.HtmlEditor({
42252     ....
42253     toolbars : [
42254         { xtype: 'ToolbarStandard', styles : {} }
42255         { xtype: 'ToolbarContext', disable : {} }
42256     ]
42257 })
42258
42259      
42260  * 
42261  * @config : {Object} disable List of elements to disable.. (not done yet.)
42262  * @config : {Object} styles  Map of styles available.
42263  * 
42264  */
42265
42266 Roo.form.HtmlEditor.ToolbarContext = function(config)
42267 {
42268     
42269     Roo.apply(this, config);
42270     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42271     // dont call parent... till later.
42272     this.styles = this.styles || {};
42273 }
42274
42275  
42276
42277 Roo.form.HtmlEditor.ToolbarContext.types = {
42278     'IMG' : {
42279         width : {
42280             title: "Width",
42281             width: 40
42282         },
42283         height:  {
42284             title: "Height",
42285             width: 40
42286         },
42287         align: {
42288             title: "Align",
42289             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42290             width : 80
42291             
42292         },
42293         border: {
42294             title: "Border",
42295             width: 40
42296         },
42297         alt: {
42298             title: "Alt",
42299             width: 120
42300         },
42301         src : {
42302             title: "Src",
42303             width: 220
42304         }
42305         
42306     },
42307     'A' : {
42308         name : {
42309             title: "Name",
42310             width: 50
42311         },
42312         href:  {
42313             title: "Href",
42314             width: 220
42315         } // border?
42316         
42317     },
42318     'TABLE' : {
42319         rows : {
42320             title: "Rows",
42321             width: 20
42322         },
42323         cols : {
42324             title: "Cols",
42325             width: 20
42326         },
42327         width : {
42328             title: "Width",
42329             width: 40
42330         },
42331         height : {
42332             title: "Height",
42333             width: 40
42334         },
42335         border : {
42336             title: "Border",
42337             width: 20
42338         }
42339     },
42340     'TD' : {
42341         width : {
42342             title: "Width",
42343             width: 40
42344         },
42345         height : {
42346             title: "Height",
42347             width: 40
42348         },   
42349         align: {
42350             title: "Align",
42351             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42352             width: 80
42353         },
42354         valign: {
42355             title: "Valign",
42356             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42357             width: 80
42358         },
42359         colspan: {
42360             title: "Colspan",
42361             width: 20
42362             
42363         },
42364          'font-family'  : {
42365             title : "Font",
42366             style : 'fontFamily',
42367             displayField: 'display',
42368             optname : 'font-family',
42369             width: 140
42370         }
42371     },
42372     'INPUT' : {
42373         name : {
42374             title: "name",
42375             width: 120
42376         },
42377         value : {
42378             title: "Value",
42379             width: 120
42380         },
42381         width : {
42382             title: "Width",
42383             width: 40
42384         }
42385     },
42386     'LABEL' : {
42387         'for' : {
42388             title: "For",
42389             width: 120
42390         }
42391     },
42392     'TEXTAREA' : {
42393           name : {
42394             title: "name",
42395             width: 120
42396         },
42397         rows : {
42398             title: "Rows",
42399             width: 20
42400         },
42401         cols : {
42402             title: "Cols",
42403             width: 20
42404         }
42405     },
42406     'SELECT' : {
42407         name : {
42408             title: "name",
42409             width: 120
42410         },
42411         selectoptions : {
42412             title: "Options",
42413             width: 200
42414         }
42415     },
42416     
42417     // should we really allow this??
42418     // should this just be 
42419     'BODY' : {
42420         title : {
42421             title: "Title",
42422             width: 200,
42423             disabled : true
42424         }
42425     },
42426     'SPAN' : {
42427         'font-family'  : {
42428             title : "Font",
42429             style : 'fontFamily',
42430             displayField: 'display',
42431             optname : 'font-family',
42432             width: 140
42433         }
42434     },
42435     'DIV' : {
42436         'font-family'  : {
42437             title : "Font",
42438             style : 'fontFamily',
42439             displayField: 'display',
42440             optname : 'font-family',
42441             width: 140
42442         }
42443     },
42444      'P' : {
42445         'font-family'  : {
42446             title : "Font",
42447             style : 'fontFamily',
42448             displayField: 'display',
42449             optname : 'font-family',
42450             width: 140
42451         }
42452     },
42453     
42454     '*' : {
42455         // empty..
42456     }
42457
42458 };
42459
42460 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
42461 Roo.form.HtmlEditor.ToolbarContext.stores = false;
42462
42463 Roo.form.HtmlEditor.ToolbarContext.options = {
42464         'font-family'  : [ 
42465                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
42466                 [ 'Courier New', 'Courier New'],
42467                 [ 'Tahoma', 'Tahoma'],
42468                 [ 'Times New Roman,serif', 'Times'],
42469                 [ 'Verdana','Verdana' ]
42470         ]
42471 };
42472
42473 // fixme - these need to be configurable..
42474  
42475
42476 Roo.form.HtmlEditor.ToolbarContext.types
42477
42478
42479 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
42480     
42481     tb: false,
42482     
42483     rendered: false,
42484     
42485     editor : false,
42486     /**
42487      * @cfg {Object} disable  List of toolbar elements to disable
42488          
42489      */
42490     disable : false,
42491     /**
42492      * @cfg {Object} styles List of styles 
42493      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
42494      *
42495      * These must be defined in the page, so they get rendered correctly..
42496      * .headline { }
42497      * TD.underline { }
42498      * 
42499      */
42500     styles : false,
42501     
42502     options: false,
42503     
42504     toolbars : false,
42505     
42506     init : function(editor)
42507     {
42508         this.editor = editor;
42509         
42510         
42511         var fid = editor.frameId;
42512         var etb = this;
42513         function btn(id, toggle, handler){
42514             var xid = fid + '-'+ id ;
42515             return {
42516                 id : xid,
42517                 cmd : id,
42518                 cls : 'x-btn-icon x-edit-'+id,
42519                 enableToggle:toggle !== false,
42520                 scope: editor, // was editor...
42521                 handler:handler||editor.relayBtnCmd,
42522                 clickEvent:'mousedown',
42523                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42524                 tabIndex:-1
42525             };
42526         }
42527         // create a new element.
42528         var wdiv = editor.wrap.createChild({
42529                 tag: 'div'
42530             }, editor.wrap.dom.firstChild.nextSibling, true);
42531         
42532         // can we do this more than once??
42533         
42534          // stop form submits
42535       
42536  
42537         // disable everything...
42538         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42539         this.toolbars = {};
42540            
42541         for (var i in  ty) {
42542           
42543             this.toolbars[i] = this.buildToolbar(ty[i],i);
42544         }
42545         this.tb = this.toolbars.BODY;
42546         this.tb.el.show();
42547         this.buildFooter();
42548         this.footer.show();
42549         editor.on('hide', function( ) { this.footer.hide() }, this);
42550         editor.on('show', function( ) { this.footer.show() }, this);
42551         
42552          
42553         this.rendered = true;
42554         
42555         // the all the btns;
42556         editor.on('editorevent', this.updateToolbar, this);
42557         // other toolbars need to implement this..
42558         //editor.on('editmodechange', this.updateToolbar, this);
42559     },
42560     
42561     
42562     
42563     /**
42564      * Protected method that will not generally be called directly. It triggers
42565      * a toolbar update by reading the markup state of the current selection in the editor.
42566      */
42567     updateToolbar: function(editor,ev,sel){
42568
42569         //Roo.log(ev);
42570         // capture mouse up - this is handy for selecting images..
42571         // perhaps should go somewhere else...
42572         if(!this.editor.activated){
42573              this.editor.onFirstFocus();
42574             return;
42575         }
42576         
42577         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
42578         // selectNode - might want to handle IE?
42579         if (ev &&
42580             (ev.type == 'mouseup' || ev.type == 'click' ) &&
42581             ev.target && ev.target.tagName == 'IMG') {
42582             // they have click on an image...
42583             // let's see if we can change the selection...
42584             sel = ev.target;
42585          
42586               var nodeRange = sel.ownerDocument.createRange();
42587             try {
42588                 nodeRange.selectNode(sel);
42589             } catch (e) {
42590                 nodeRange.selectNodeContents(sel);
42591             }
42592             //nodeRange.collapse(true);
42593             var s = editor.win.getSelection();
42594             s.removeAllRanges();
42595             s.addRange(nodeRange);
42596         }  
42597         
42598       
42599         var updateFooter = sel ? false : true;
42600         
42601         
42602         var ans = this.editor.getAllAncestors();
42603         
42604         // pick
42605         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42606         
42607         if (!sel) { 
42608             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
42609             sel = sel ? sel : this.editor.doc.body;
42610             sel = sel.tagName.length ? sel : this.editor.doc.body;
42611             
42612         }
42613         // pick a menu that exists..
42614         var tn = sel.tagName.toUpperCase();
42615         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
42616         
42617         tn = sel.tagName.toUpperCase();
42618         
42619         var lastSel = this.tb.selectedNode
42620         
42621         this.tb.selectedNode = sel;
42622         
42623         // if current menu does not match..
42624         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
42625                 
42626             this.tb.el.hide();
42627             ///console.log("show: " + tn);
42628             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
42629             this.tb.el.show();
42630             // update name
42631             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
42632             
42633             
42634             // update attributes
42635             if (this.tb.fields) {
42636                 this.tb.fields.each(function(e) {
42637                     if (e.stylename) {
42638                         e.setValue(sel.style[e.stylename]);
42639                         return;
42640                     } 
42641                    e.setValue(sel.getAttribute(e.attrname));
42642                 });
42643             }
42644             
42645             var hasStyles = false;
42646             for(var i in this.styles) {
42647                 hasStyles = true;
42648                 break;
42649             }
42650             
42651             // update styles
42652             if (hasStyles) { 
42653                 var st = this.tb.fields.item(0);
42654                 
42655                 st.store.removeAll();
42656                
42657                 
42658                 var cn = sel.className.split(/\s+/);
42659                 
42660                 var avs = [];
42661                 if (this.styles['*']) {
42662                     
42663                     Roo.each(this.styles['*'], function(v) {
42664                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42665                     });
42666                 }
42667                 if (this.styles[tn]) { 
42668                     Roo.each(this.styles[tn], function(v) {
42669                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42670                     });
42671                 }
42672                 
42673                 st.store.loadData(avs);
42674                 st.collapse();
42675                 st.setValue(cn);
42676             }
42677             // flag our selected Node.
42678             this.tb.selectedNode = sel;
42679            
42680            
42681             Roo.menu.MenuMgr.hideAll();
42682
42683         }
42684         
42685         if (!updateFooter) {
42686             //this.footDisp.dom.innerHTML = ''; 
42687             return;
42688         }
42689         // update the footer
42690         //
42691         var html = '';
42692         
42693         this.footerEls = ans.reverse();
42694         Roo.each(this.footerEls, function(a,i) {
42695             if (!a) { return; }
42696             html += html.length ? ' &gt; '  :  '';
42697             
42698             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
42699             
42700         });
42701        
42702         // 
42703         var sz = this.footDisp.up('td').getSize();
42704         this.footDisp.dom.style.width = (sz.width -10) + 'px';
42705         this.footDisp.dom.style.marginLeft = '5px';
42706         
42707         this.footDisp.dom.style.overflow = 'hidden';
42708         
42709         this.footDisp.dom.innerHTML = html;
42710             
42711         //this.editorsyncValue();
42712     },
42713      
42714     
42715    
42716        
42717     // private
42718     onDestroy : function(){
42719         if(this.rendered){
42720             
42721             this.tb.items.each(function(item){
42722                 if(item.menu){
42723                     item.menu.removeAll();
42724                     if(item.menu.el){
42725                         item.menu.el.destroy();
42726                     }
42727                 }
42728                 item.destroy();
42729             });
42730              
42731         }
42732     },
42733     onFirstFocus: function() {
42734         // need to do this for all the toolbars..
42735         this.tb.items.each(function(item){
42736            item.enable();
42737         });
42738     },
42739     buildToolbar: function(tlist, nm)
42740     {
42741         var editor = this.editor;
42742          // create a new element.
42743         var wdiv = editor.wrap.createChild({
42744                 tag: 'div'
42745             }, editor.wrap.dom.firstChild.nextSibling, true);
42746         
42747        
42748         var tb = new Roo.Toolbar(wdiv);
42749         // add the name..
42750         
42751         tb.add(nm+ ":&nbsp;");
42752         
42753         var styles = [];
42754         for(var i in this.styles) {
42755             styles.push(i);
42756         }
42757         
42758         // styles...
42759         if (styles && styles.length) {
42760             
42761             // this needs a multi-select checkbox...
42762             tb.addField( new Roo.form.ComboBox({
42763                 store: new Roo.data.SimpleStore({
42764                     id : 'val',
42765                     fields: ['val', 'selected'],
42766                     data : [] 
42767                 }),
42768                 name : '-roo-edit-className',
42769                 attrname : 'className',
42770                 displayField: 'val',
42771                 typeAhead: false,
42772                 mode: 'local',
42773                 editable : false,
42774                 triggerAction: 'all',
42775                 emptyText:'Select Style',
42776                 selectOnFocus:true,
42777                 width: 130,
42778                 listeners : {
42779                     'select': function(c, r, i) {
42780                         // initial support only for on class per el..
42781                         tb.selectedNode.className =  r ? r.get('val') : '';
42782                         editor.syncValue();
42783                     }
42784                 }
42785     
42786             }));
42787         }
42788         
42789         var tbc = Roo.form.HtmlEditor.ToolbarContext;
42790         var tbops = tbc.options;
42791         
42792         for (var i in tlist) {
42793             
42794             var item = tlist[i];
42795             tb.add(item.title + ":&nbsp;");
42796             
42797             
42798             //optname == used so you can configure the options available..
42799             var opts = item.opts ? item.opts : false;
42800             if (item.optname) {
42801                 opts = tbops[item.optname];
42802            
42803             }
42804             
42805             if (opts) {
42806                 // opts == pulldown..
42807                 tb.addField( new Roo.form.ComboBox({
42808                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
42809                         id : 'val',
42810                         fields: ['val', 'display'],
42811                         data : opts  
42812                     }),
42813                     name : '-roo-edit-' + i,
42814                     attrname : i,
42815                     stylename : item.style ? item.style : false,
42816                     displayField: item.displayField ? item.displayField : 'val',
42817                     valueField :  'val',
42818                     typeAhead: false,
42819                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
42820                     editable : false,
42821                     triggerAction: 'all',
42822                     emptyText:'Select',
42823                     selectOnFocus:true,
42824                     width: item.width ? item.width  : 130,
42825                     listeners : {
42826                         'select': function(c, r, i) {
42827                             if (c.stylename) {
42828                                 tb.selectedNode.style[c.stylename] =  r.get('val');
42829                                 return;
42830                             }
42831                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
42832                         }
42833                     }
42834
42835                 }));
42836                 continue;
42837                     
42838                  
42839                 
42840                 tb.addField( new Roo.form.TextField({
42841                     name: i,
42842                     width: 100,
42843                     //allowBlank:false,
42844                     value: ''
42845                 }));
42846                 continue;
42847             }
42848             tb.addField( new Roo.form.TextField({
42849                 name: '-roo-edit-' + i,
42850                 attrname : i,
42851                 
42852                 width: item.width,
42853                 //allowBlank:true,
42854                 value: '',
42855                 listeners: {
42856                     'change' : function(f, nv, ov) {
42857                         tb.selectedNode.setAttribute(f.attrname, nv);
42858                     }
42859                 }
42860             }));
42861              
42862         }
42863         tb.addFill();
42864         var _this = this;
42865         tb.addButton( {
42866             text: 'Remove Tag',
42867     
42868             listeners : {
42869                 click : function ()
42870                 {
42871                     // remove
42872                     // undo does not work.
42873                      
42874                     var sn = tb.selectedNode;
42875                     
42876                     var pn = sn.parentNode;
42877                     
42878                     var stn =  sn.childNodes[0];
42879                     var en = sn.childNodes[sn.childNodes.length - 1 ];
42880                     while (sn.childNodes.length) {
42881                         var node = sn.childNodes[0];
42882                         sn.removeChild(node);
42883                         //Roo.log(node);
42884                         pn.insertBefore(node, sn);
42885                         
42886                     }
42887                     pn.removeChild(sn);
42888                     var range = editor.createRange();
42889         
42890                     range.setStart(stn,0);
42891                     range.setEnd(en,0); //????
42892                     //range.selectNode(sel);
42893                     
42894                     
42895                     var selection = editor.getSelection();
42896                     selection.removeAllRanges();
42897                     selection.addRange(range);
42898                     
42899                     
42900                     
42901                     //_this.updateToolbar(null, null, pn);
42902                     _this.updateToolbar(null, null, null);
42903                     _this.footDisp.dom.innerHTML = ''; 
42904                 }
42905             }
42906             
42907                     
42908                 
42909             
42910         });
42911         
42912         
42913         tb.el.on('click', function(e){
42914             e.preventDefault(); // what does this do?
42915         });
42916         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
42917         tb.el.hide();
42918         tb.name = nm;
42919         // dont need to disable them... as they will get hidden
42920         return tb;
42921          
42922         
42923     },
42924     buildFooter : function()
42925     {
42926         
42927         var fel = this.editor.wrap.createChild();
42928         this.footer = new Roo.Toolbar(fel);
42929         // toolbar has scrolly on left / right?
42930         var footDisp= new Roo.Toolbar.Fill();
42931         var _t = this;
42932         this.footer.add(
42933             {
42934                 text : '&lt;',
42935                 xtype: 'Button',
42936                 handler : function() {
42937                     _t.footDisp.scrollTo('left',0,true)
42938                 }
42939             }
42940         );
42941         this.footer.add( footDisp );
42942         this.footer.add( 
42943             {
42944                 text : '&gt;',
42945                 xtype: 'Button',
42946                 handler : function() {
42947                     // no animation..
42948                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
42949                 }
42950             }
42951         );
42952         var fel = Roo.get(footDisp.el);
42953         fel.addClass('x-editor-context');
42954         this.footDispWrap = fel; 
42955         this.footDispWrap.overflow  = 'hidden';
42956         
42957         this.footDisp = fel.createChild();
42958         this.footDispWrap.on('click', this.onContextClick, this)
42959         
42960         
42961     },
42962     onContextClick : function (ev,dom)
42963     {
42964         ev.preventDefault();
42965         var  cn = dom.className;
42966         //Roo.log(cn);
42967         if (!cn.match(/x-ed-loc-/)) {
42968             return;
42969         }
42970         var n = cn.split('-').pop();
42971         var ans = this.footerEls;
42972         var sel = ans[n];
42973         
42974          // pick
42975         var range = this.editor.createRange();
42976         
42977         range.selectNodeContents(sel);
42978         //range.selectNode(sel);
42979         
42980         
42981         var selection = this.editor.getSelection();
42982         selection.removeAllRanges();
42983         selection.addRange(range);
42984         
42985         
42986         
42987         this.updateToolbar(null, null, sel);
42988         
42989         
42990     }
42991     
42992     
42993     
42994     
42995     
42996 });
42997
42998
42999
43000
43001
43002 /*
43003  * Based on:
43004  * Ext JS Library 1.1.1
43005  * Copyright(c) 2006-2007, Ext JS, LLC.
43006  *
43007  * Originally Released Under LGPL - original licence link has changed is not relivant.
43008  *
43009  * Fork - LGPL
43010  * <script type="text/javascript">
43011  */
43012  
43013 /**
43014  * @class Roo.form.BasicForm
43015  * @extends Roo.util.Observable
43016  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43017  * @constructor
43018  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43019  * @param {Object} config Configuration options
43020  */
43021 Roo.form.BasicForm = function(el, config){
43022     this.allItems = [];
43023     this.childForms = [];
43024     Roo.apply(this, config);
43025     /*
43026      * The Roo.form.Field items in this form.
43027      * @type MixedCollection
43028      */
43029      
43030      
43031     this.items = new Roo.util.MixedCollection(false, function(o){
43032         return o.id || (o.id = Roo.id());
43033     });
43034     this.addEvents({
43035         /**
43036          * @event beforeaction
43037          * Fires before any action is performed. Return false to cancel the action.
43038          * @param {Form} this
43039          * @param {Action} action The action to be performed
43040          */
43041         beforeaction: true,
43042         /**
43043          * @event actionfailed
43044          * Fires when an action fails.
43045          * @param {Form} this
43046          * @param {Action} action The action that failed
43047          */
43048         actionfailed : true,
43049         /**
43050          * @event actioncomplete
43051          * Fires when an action is completed.
43052          * @param {Form} this
43053          * @param {Action} action The action that completed
43054          */
43055         actioncomplete : true
43056     });
43057     if(el){
43058         this.initEl(el);
43059     }
43060     Roo.form.BasicForm.superclass.constructor.call(this);
43061 };
43062
43063 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43064     /**
43065      * @cfg {String} method
43066      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43067      */
43068     /**
43069      * @cfg {DataReader} reader
43070      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43071      * This is optional as there is built-in support for processing JSON.
43072      */
43073     /**
43074      * @cfg {DataReader} errorReader
43075      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43076      * This is completely optional as there is built-in support for processing JSON.
43077      */
43078     /**
43079      * @cfg {String} url
43080      * The URL to use for form actions if one isn't supplied in the action options.
43081      */
43082     /**
43083      * @cfg {Boolean} fileUpload
43084      * Set to true if this form is a file upload.
43085      */
43086      
43087     /**
43088      * @cfg {Object} baseParams
43089      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43090      */
43091      /**
43092      
43093     /**
43094      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43095      */
43096     timeout: 30,
43097
43098     // private
43099     activeAction : null,
43100
43101     /**
43102      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43103      * or setValues() data instead of when the form was first created.
43104      */
43105     trackResetOnLoad : false,
43106     
43107     
43108     /**
43109      * childForms - used for multi-tab forms
43110      * @type {Array}
43111      */
43112     childForms : false,
43113     
43114     /**
43115      * allItems - full list of fields.
43116      * @type {Array}
43117      */
43118     allItems : false,
43119     
43120     /**
43121      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43122      * element by passing it or its id or mask the form itself by passing in true.
43123      * @type Mixed
43124      */
43125     waitMsgTarget : false,
43126
43127     // private
43128     initEl : function(el){
43129         this.el = Roo.get(el);
43130         this.id = this.el.id || Roo.id();
43131         this.el.on('submit', this.onSubmit, this);
43132         this.el.addClass('x-form');
43133     },
43134
43135     // private
43136     onSubmit : function(e){
43137         e.stopEvent();
43138     },
43139
43140     /**
43141      * Returns true if client-side validation on the form is successful.
43142      * @return Boolean
43143      */
43144     isValid : function(){
43145         var valid = true;
43146         this.items.each(function(f){
43147            if(!f.validate()){
43148                valid = false;
43149            }
43150         });
43151         return valid;
43152     },
43153
43154     /**
43155      * Returns true if any fields in this form have changed since their original load.
43156      * @return Boolean
43157      */
43158     isDirty : function(){
43159         var dirty = false;
43160         this.items.each(function(f){
43161            if(f.isDirty()){
43162                dirty = true;
43163                return false;
43164            }
43165         });
43166         return dirty;
43167     },
43168
43169     /**
43170      * Performs a predefined action (submit or load) or custom actions you define on this form.
43171      * @param {String} actionName The name of the action type
43172      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43173      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43174      * accept other config options):
43175      * <pre>
43176 Property          Type             Description
43177 ----------------  ---------------  ----------------------------------------------------------------------------------
43178 url               String           The url for the action (defaults to the form's url)
43179 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43180 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43181 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43182                                    validate the form on the client (defaults to false)
43183      * </pre>
43184      * @return {BasicForm} this
43185      */
43186     doAction : function(action, options){
43187         if(typeof action == 'string'){
43188             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43189         }
43190         if(this.fireEvent('beforeaction', this, action) !== false){
43191             this.beforeAction(action);
43192             action.run.defer(100, action);
43193         }
43194         return this;
43195     },
43196
43197     /**
43198      * Shortcut to do a submit action.
43199      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43200      * @return {BasicForm} this
43201      */
43202     submit : function(options){
43203         this.doAction('submit', options);
43204         return this;
43205     },
43206
43207     /**
43208      * Shortcut to do a load action.
43209      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43210      * @return {BasicForm} this
43211      */
43212     load : function(options){
43213         this.doAction('load', options);
43214         return this;
43215     },
43216
43217     /**
43218      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43219      * @param {Record} record The record to edit
43220      * @return {BasicForm} this
43221      */
43222     updateRecord : function(record){
43223         record.beginEdit();
43224         var fs = record.fields;
43225         fs.each(function(f){
43226             var field = this.findField(f.name);
43227             if(field){
43228                 record.set(f.name, field.getValue());
43229             }
43230         }, this);
43231         record.endEdit();
43232         return this;
43233     },
43234
43235     /**
43236      * Loads an Roo.data.Record into this form.
43237      * @param {Record} record The record to load
43238      * @return {BasicForm} this
43239      */
43240     loadRecord : function(record){
43241         this.setValues(record.data);
43242         return this;
43243     },
43244
43245     // private
43246     beforeAction : function(action){
43247         var o = action.options;
43248         
43249        
43250         if(this.waitMsgTarget === true){
43251             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43252         }else if(this.waitMsgTarget){
43253             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43254             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43255         }else {
43256             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43257         }
43258          
43259     },
43260
43261     // private
43262     afterAction : function(action, success){
43263         this.activeAction = null;
43264         var o = action.options;
43265         
43266         if(this.waitMsgTarget === true){
43267             this.el.unmask();
43268         }else if(this.waitMsgTarget){
43269             this.waitMsgTarget.unmask();
43270         }else{
43271             Roo.MessageBox.updateProgress(1);
43272             Roo.MessageBox.hide();
43273         }
43274          
43275         if(success){
43276             if(o.reset){
43277                 this.reset();
43278             }
43279             Roo.callback(o.success, o.scope, [this, action]);
43280             this.fireEvent('actioncomplete', this, action);
43281             
43282         }else{
43283             
43284             // failure condition..
43285             // we have a scenario where updates need confirming.
43286             // eg. if a locking scenario exists..
43287             // we look for { errors : { needs_confirm : true }} in the response.
43288             if (
43289                 (typeof(action.result) != 'undefined')  &&
43290                 (typeof(action.result.errors) != 'undefined')  &&
43291                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43292            ){
43293                 var _t = this;
43294                 Roo.MessageBox.confirm(
43295                     "Change requires confirmation",
43296                     action.result.errorMsg,
43297                     function(r) {
43298                         if (r != 'yes') {
43299                             return;
43300                         }
43301                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43302                     }
43303                     
43304                 );
43305                 
43306                 
43307                 
43308                 return;
43309             }
43310             
43311             Roo.callback(o.failure, o.scope, [this, action]);
43312             // show an error message if no failed handler is set..
43313             if (!this.hasListener('actionfailed')) {
43314                 Roo.MessageBox.alert("Error",
43315                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43316                         action.result.errorMsg :
43317                         "Saving Failed, please check your entries or try again"
43318                 );
43319             }
43320             
43321             this.fireEvent('actionfailed', this, action);
43322         }
43323         
43324     },
43325
43326     /**
43327      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43328      * @param {String} id The value to search for
43329      * @return Field
43330      */
43331     findField : function(id){
43332         var field = this.items.get(id);
43333         if(!field){
43334             this.items.each(function(f){
43335                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43336                     field = f;
43337                     return false;
43338                 }
43339             });
43340         }
43341         return field || null;
43342     },
43343
43344     /**
43345      * Add a secondary form to this one, 
43346      * Used to provide tabbed forms. One form is primary, with hidden values 
43347      * which mirror the elements from the other forms.
43348      * 
43349      * @param {Roo.form.Form} form to add.
43350      * 
43351      */
43352     addForm : function(form)
43353     {
43354        
43355         if (this.childForms.indexOf(form) > -1) {
43356             // already added..
43357             return;
43358         }
43359         this.childForms.push(form);
43360         var n = '';
43361         Roo.each(form.allItems, function (fe) {
43362             
43363             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43364             if (this.findField(n)) { // already added..
43365                 return;
43366             }
43367             var add = new Roo.form.Hidden({
43368                 name : n
43369             });
43370             add.render(this.el);
43371             
43372             this.add( add );
43373         }, this);
43374         
43375     },
43376     /**
43377      * Mark fields in this form invalid in bulk.
43378      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43379      * @return {BasicForm} this
43380      */
43381     markInvalid : function(errors){
43382         if(errors instanceof Array){
43383             for(var i = 0, len = errors.length; i < len; i++){
43384                 var fieldError = errors[i];
43385                 var f = this.findField(fieldError.id);
43386                 if(f){
43387                     f.markInvalid(fieldError.msg);
43388                 }
43389             }
43390         }else{
43391             var field, id;
43392             for(id in errors){
43393                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43394                     field.markInvalid(errors[id]);
43395                 }
43396             }
43397         }
43398         Roo.each(this.childForms || [], function (f) {
43399             f.markInvalid(errors);
43400         });
43401         
43402         return this;
43403     },
43404
43405     /**
43406      * Set values for fields in this form in bulk.
43407      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43408      * @return {BasicForm} this
43409      */
43410     setValues : function(values){
43411         if(values instanceof Array){ // array of objects
43412             for(var i = 0, len = values.length; i < len; i++){
43413                 var v = values[i];
43414                 var f = this.findField(v.id);
43415                 if(f){
43416                     f.setValue(v.value);
43417                     if(this.trackResetOnLoad){
43418                         f.originalValue = f.getValue();
43419                     }
43420                 }
43421             }
43422         }else{ // object hash
43423             var field, id;
43424             for(id in values){
43425                 if(typeof values[id] != 'function' && (field = this.findField(id))){
43426                     
43427                     if (field.setFromData && 
43428                         field.valueField && 
43429                         field.displayField &&
43430                         // combos' with local stores can 
43431                         // be queried via setValue()
43432                         // to set their value..
43433                         (field.store && !field.store.isLocal)
43434                         ) {
43435                         // it's a combo
43436                         var sd = { };
43437                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
43438                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
43439                         field.setFromData(sd);
43440                         
43441                     } else {
43442                         field.setValue(values[id]);
43443                     }
43444                     
43445                     
43446                     if(this.trackResetOnLoad){
43447                         field.originalValue = field.getValue();
43448                     }
43449                 }
43450             }
43451         }
43452          
43453         Roo.each(this.childForms || [], function (f) {
43454             f.setValues(values);
43455         });
43456                 
43457         return this;
43458     },
43459
43460     /**
43461      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
43462      * they are returned as an array.
43463      * @param {Boolean} asString
43464      * @return {Object}
43465      */
43466     getValues : function(asString){
43467         if (this.childForms) {
43468             // copy values from the child forms
43469             Roo.each(this.childForms, function (f) {
43470                 this.setValues(f.getValues());
43471             }, this);
43472         }
43473         
43474         
43475         
43476         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
43477         if(asString === true){
43478             return fs;
43479         }
43480         return Roo.urlDecode(fs);
43481     },
43482     
43483     /**
43484      * Returns the fields in this form as an object with key/value pairs. 
43485      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
43486      * @return {Object}
43487      */
43488     getFieldValues : function(with_hidden)
43489     {
43490         if (this.childForms) {
43491             // copy values from the child forms
43492             // should this call getFieldValues - probably not as we do not currently copy
43493             // hidden fields when we generate..
43494             Roo.each(this.childForms, function (f) {
43495                 this.setValues(f.getValues());
43496             }, this);
43497         }
43498         
43499         var ret = {};
43500         this.items.each(function(f){
43501             if (!f.getName()) {
43502                 return;
43503             }
43504             var v = f.getValue();
43505             if (f.inputType =='radio') {
43506                 if (typeof(ret[f.getName()]) == 'undefined') {
43507                     ret[f.getName()] = ''; // empty..
43508                 }
43509                 
43510                 if (!f.el.dom.checked) {
43511                     return;
43512                     
43513                 }
43514                 v = f.el.dom.value;
43515                 
43516             }
43517             
43518             // not sure if this supported any more..
43519             if ((typeof(v) == 'object') && f.getRawValue) {
43520                 v = f.getRawValue() ; // dates..
43521             }
43522             // combo boxes where name != hiddenName...
43523             if (f.name != f.getName()) {
43524                 ret[f.name] = f.getRawValue();
43525             }
43526             ret[f.getName()] = v;
43527         });
43528         
43529         return ret;
43530     },
43531
43532     /**
43533      * Clears all invalid messages in this form.
43534      * @return {BasicForm} this
43535      */
43536     clearInvalid : function(){
43537         this.items.each(function(f){
43538            f.clearInvalid();
43539         });
43540         
43541         Roo.each(this.childForms || [], function (f) {
43542             f.clearInvalid();
43543         });
43544         
43545         
43546         return this;
43547     },
43548
43549     /**
43550      * Resets this form.
43551      * @return {BasicForm} this
43552      */
43553     reset : function(){
43554         this.items.each(function(f){
43555             f.reset();
43556         });
43557         
43558         Roo.each(this.childForms || [], function (f) {
43559             f.reset();
43560         });
43561        
43562         
43563         return this;
43564     },
43565
43566     /**
43567      * Add Roo.form components to this form.
43568      * @param {Field} field1
43569      * @param {Field} field2 (optional)
43570      * @param {Field} etc (optional)
43571      * @return {BasicForm} this
43572      */
43573     add : function(){
43574         this.items.addAll(Array.prototype.slice.call(arguments, 0));
43575         return this;
43576     },
43577
43578
43579     /**
43580      * Removes a field from the items collection (does NOT remove its markup).
43581      * @param {Field} field
43582      * @return {BasicForm} this
43583      */
43584     remove : function(field){
43585         this.items.remove(field);
43586         return this;
43587     },
43588
43589     /**
43590      * Looks at the fields in this form, checks them for an id attribute,
43591      * and calls applyTo on the existing dom element with that id.
43592      * @return {BasicForm} this
43593      */
43594     render : function(){
43595         this.items.each(function(f){
43596             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
43597                 f.applyTo(f.id);
43598             }
43599         });
43600         return this;
43601     },
43602
43603     /**
43604      * Calls {@link Ext#apply} for all fields in this form with the passed object.
43605      * @param {Object} values
43606      * @return {BasicForm} this
43607      */
43608     applyToFields : function(o){
43609         this.items.each(function(f){
43610            Roo.apply(f, o);
43611         });
43612         return this;
43613     },
43614
43615     /**
43616      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
43617      * @param {Object} values
43618      * @return {BasicForm} this
43619      */
43620     applyIfToFields : function(o){
43621         this.items.each(function(f){
43622            Roo.applyIf(f, o);
43623         });
43624         return this;
43625     }
43626 });
43627
43628 // back compat
43629 Roo.BasicForm = Roo.form.BasicForm;/*
43630  * Based on:
43631  * Ext JS Library 1.1.1
43632  * Copyright(c) 2006-2007, Ext JS, LLC.
43633  *
43634  * Originally Released Under LGPL - original licence link has changed is not relivant.
43635  *
43636  * Fork - LGPL
43637  * <script type="text/javascript">
43638  */
43639
43640 /**
43641  * @class Roo.form.Form
43642  * @extends Roo.form.BasicForm
43643  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
43644  * @constructor
43645  * @param {Object} config Configuration options
43646  */
43647 Roo.form.Form = function(config){
43648     var xitems =  [];
43649     if (config.items) {
43650         xitems = config.items;
43651         delete config.items;
43652     }
43653    
43654     
43655     Roo.form.Form.superclass.constructor.call(this, null, config);
43656     this.url = this.url || this.action;
43657     if(!this.root){
43658         this.root = new Roo.form.Layout(Roo.applyIf({
43659             id: Roo.id()
43660         }, config));
43661     }
43662     this.active = this.root;
43663     /**
43664      * Array of all the buttons that have been added to this form via {@link addButton}
43665      * @type Array
43666      */
43667     this.buttons = [];
43668     this.allItems = [];
43669     this.addEvents({
43670         /**
43671          * @event clientvalidation
43672          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
43673          * @param {Form} this
43674          * @param {Boolean} valid true if the form has passed client-side validation
43675          */
43676         clientvalidation: true,
43677         /**
43678          * @event rendered
43679          * Fires when the form is rendered
43680          * @param {Roo.form.Form} form
43681          */
43682         rendered : true
43683     });
43684     
43685     if (this.progressUrl) {
43686             // push a hidden field onto the list of fields..
43687             this.addxtype( {
43688                     xns: Roo.form, 
43689                     xtype : 'Hidden', 
43690                     name : 'UPLOAD_IDENTIFIER' 
43691             });
43692         }
43693         
43694     
43695     Roo.each(xitems, this.addxtype, this);
43696     
43697     
43698     
43699 };
43700
43701 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
43702     /**
43703      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
43704      */
43705     /**
43706      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
43707      */
43708     /**
43709      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
43710      */
43711     buttonAlign:'center',
43712
43713     /**
43714      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
43715      */
43716     minButtonWidth:75,
43717
43718     /**
43719      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
43720      * This property cascades to child containers if not set.
43721      */
43722     labelAlign:'left',
43723
43724     /**
43725      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
43726      * fires a looping event with that state. This is required to bind buttons to the valid
43727      * state using the config value formBind:true on the button.
43728      */
43729     monitorValid : false,
43730
43731     /**
43732      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
43733      */
43734     monitorPoll : 200,
43735     
43736     /**
43737      * @cfg {String} progressUrl - Url to return progress data 
43738      */
43739     
43740     progressUrl : false,
43741   
43742     /**
43743      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
43744      * fields are added and the column is closed. If no fields are passed the column remains open
43745      * until end() is called.
43746      * @param {Object} config The config to pass to the column
43747      * @param {Field} field1 (optional)
43748      * @param {Field} field2 (optional)
43749      * @param {Field} etc (optional)
43750      * @return Column The column container object
43751      */
43752     column : function(c){
43753         var col = new Roo.form.Column(c);
43754         this.start(col);
43755         if(arguments.length > 1){ // duplicate code required because of Opera
43756             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43757             this.end();
43758         }
43759         return col;
43760     },
43761
43762     /**
43763      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
43764      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
43765      * until end() is called.
43766      * @param {Object} config The config to pass to the fieldset
43767      * @param {Field} field1 (optional)
43768      * @param {Field} field2 (optional)
43769      * @param {Field} etc (optional)
43770      * @return FieldSet The fieldset container object
43771      */
43772     fieldset : function(c){
43773         var fs = new Roo.form.FieldSet(c);
43774         this.start(fs);
43775         if(arguments.length > 1){ // duplicate code required because of Opera
43776             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43777             this.end();
43778         }
43779         return fs;
43780     },
43781
43782     /**
43783      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
43784      * fields are added and the container is closed. If no fields are passed the container remains open
43785      * until end() is called.
43786      * @param {Object} config The config to pass to the Layout
43787      * @param {Field} field1 (optional)
43788      * @param {Field} field2 (optional)
43789      * @param {Field} etc (optional)
43790      * @return Layout The container object
43791      */
43792     container : function(c){
43793         var l = new Roo.form.Layout(c);
43794         this.start(l);
43795         if(arguments.length > 1){ // duplicate code required because of Opera
43796             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43797             this.end();
43798         }
43799         return l;
43800     },
43801
43802     /**
43803      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
43804      * @param {Object} container A Roo.form.Layout or subclass of Layout
43805      * @return {Form} this
43806      */
43807     start : function(c){
43808         // cascade label info
43809         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
43810         this.active.stack.push(c);
43811         c.ownerCt = this.active;
43812         this.active = c;
43813         return this;
43814     },
43815
43816     /**
43817      * Closes the current open container
43818      * @return {Form} this
43819      */
43820     end : function(){
43821         if(this.active == this.root){
43822             return this;
43823         }
43824         this.active = this.active.ownerCt;
43825         return this;
43826     },
43827
43828     /**
43829      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
43830      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
43831      * as the label of the field.
43832      * @param {Field} field1
43833      * @param {Field} field2 (optional)
43834      * @param {Field} etc. (optional)
43835      * @return {Form} this
43836      */
43837     add : function(){
43838         this.active.stack.push.apply(this.active.stack, arguments);
43839         this.allItems.push.apply(this.allItems,arguments);
43840         var r = [];
43841         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
43842             if(a[i].isFormField){
43843                 r.push(a[i]);
43844             }
43845         }
43846         if(r.length > 0){
43847             Roo.form.Form.superclass.add.apply(this, r);
43848         }
43849         return this;
43850     },
43851     
43852
43853     
43854     
43855     
43856      /**
43857      * Find any element that has been added to a form, using it's ID or name
43858      * This can include framesets, columns etc. along with regular fields..
43859      * @param {String} id - id or name to find.
43860      
43861      * @return {Element} e - or false if nothing found.
43862      */
43863     findbyId : function(id)
43864     {
43865         var ret = false;
43866         if (!id) {
43867             return ret;
43868         }
43869         Roo.each(this.allItems, function(f){
43870             if (f.id == id || f.name == id ){
43871                 ret = f;
43872                 return false;
43873             }
43874         });
43875         return ret;
43876     },
43877
43878     
43879     
43880     /**
43881      * Render this form into the passed container. This should only be called once!
43882      * @param {String/HTMLElement/Element} container The element this component should be rendered into
43883      * @return {Form} this
43884      */
43885     render : function(ct)
43886     {
43887         
43888         
43889         
43890         ct = Roo.get(ct);
43891         var o = this.autoCreate || {
43892             tag: 'form',
43893             method : this.method || 'POST',
43894             id : this.id || Roo.id()
43895         };
43896         this.initEl(ct.createChild(o));
43897
43898         this.root.render(this.el);
43899         
43900        
43901              
43902         this.items.each(function(f){
43903             f.render('x-form-el-'+f.id);
43904         });
43905
43906         if(this.buttons.length > 0){
43907             // tables are required to maintain order and for correct IE layout
43908             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
43909                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
43910                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
43911             }}, null, true);
43912             var tr = tb.getElementsByTagName('tr')[0];
43913             for(var i = 0, len = this.buttons.length; i < len; i++) {
43914                 var b = this.buttons[i];
43915                 var td = document.createElement('td');
43916                 td.className = 'x-form-btn-td';
43917                 b.render(tr.appendChild(td));
43918             }
43919         }
43920         if(this.monitorValid){ // initialize after render
43921             this.startMonitoring();
43922         }
43923         this.fireEvent('rendered', this);
43924         return this;
43925     },
43926
43927     /**
43928      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
43929      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
43930      * object or a valid Roo.DomHelper element config
43931      * @param {Function} handler The function called when the button is clicked
43932      * @param {Object} scope (optional) The scope of the handler function
43933      * @return {Roo.Button}
43934      */
43935     addButton : function(config, handler, scope){
43936         var bc = {
43937             handler: handler,
43938             scope: scope,
43939             minWidth: this.minButtonWidth,
43940             hideParent:true
43941         };
43942         if(typeof config == "string"){
43943             bc.text = config;
43944         }else{
43945             Roo.apply(bc, config);
43946         }
43947         var btn = new Roo.Button(null, bc);
43948         this.buttons.push(btn);
43949         return btn;
43950     },
43951
43952      /**
43953      * Adds a series of form elements (using the xtype property as the factory method.
43954      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
43955      * @param {Object} config 
43956      */
43957     
43958     addxtype : function()
43959     {
43960         var ar = Array.prototype.slice.call(arguments, 0);
43961         var ret = false;
43962         for(var i = 0; i < ar.length; i++) {
43963             if (!ar[i]) {
43964                 continue; // skip -- if this happends something invalid got sent, we 
43965                 // should ignore it, as basically that interface element will not show up
43966                 // and that should be pretty obvious!!
43967             }
43968             
43969             if (Roo.form[ar[i].xtype]) {
43970                 ar[i].form = this;
43971                 var fe = Roo.factory(ar[i], Roo.form);
43972                 if (!ret) {
43973                     ret = fe;
43974                 }
43975                 fe.form = this;
43976                 if (fe.store) {
43977                     fe.store.form = this;
43978                 }
43979                 if (fe.isLayout) {  
43980                          
43981                     this.start(fe);
43982                     this.allItems.push(fe);
43983                     if (fe.items && fe.addxtype) {
43984                         fe.addxtype.apply(fe, fe.items);
43985                         delete fe.items;
43986                     }
43987                      this.end();
43988                     continue;
43989                 }
43990                 
43991                 
43992                  
43993                 this.add(fe);
43994               //  console.log('adding ' + ar[i].xtype);
43995             }
43996             if (ar[i].xtype == 'Button') {  
43997                 //console.log('adding button');
43998                 //console.log(ar[i]);
43999                 this.addButton(ar[i]);
44000                 this.allItems.push(fe);
44001                 continue;
44002             }
44003             
44004             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44005                 alert('end is not supported on xtype any more, use items');
44006             //    this.end();
44007             //    //console.log('adding end');
44008             }
44009             
44010         }
44011         return ret;
44012     },
44013     
44014     /**
44015      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44016      * option "monitorValid"
44017      */
44018     startMonitoring : function(){
44019         if(!this.bound){
44020             this.bound = true;
44021             Roo.TaskMgr.start({
44022                 run : this.bindHandler,
44023                 interval : this.monitorPoll || 200,
44024                 scope: this
44025             });
44026         }
44027     },
44028
44029     /**
44030      * Stops monitoring of the valid state of this form
44031      */
44032     stopMonitoring : function(){
44033         this.bound = false;
44034     },
44035
44036     // private
44037     bindHandler : function(){
44038         if(!this.bound){
44039             return false; // stops binding
44040         }
44041         var valid = true;
44042         this.items.each(function(f){
44043             if(!f.isValid(true)){
44044                 valid = false;
44045                 return false;
44046             }
44047         });
44048         for(var i = 0, len = this.buttons.length; i < len; i++){
44049             var btn = this.buttons[i];
44050             if(btn.formBind === true && btn.disabled === valid){
44051                 btn.setDisabled(!valid);
44052             }
44053         }
44054         this.fireEvent('clientvalidation', this, valid);
44055     }
44056     
44057     
44058     
44059     
44060     
44061     
44062     
44063     
44064 });
44065
44066
44067 // back compat
44068 Roo.Form = Roo.form.Form;
44069 /*
44070  * Based on:
44071  * Ext JS Library 1.1.1
44072  * Copyright(c) 2006-2007, Ext JS, LLC.
44073  *
44074  * Originally Released Under LGPL - original licence link has changed is not relivant.
44075  *
44076  * Fork - LGPL
44077  * <script type="text/javascript">
44078  */
44079  
44080  /**
44081  * @class Roo.form.Action
44082  * Internal Class used to handle form actions
44083  * @constructor
44084  * @param {Roo.form.BasicForm} el The form element or its id
44085  * @param {Object} config Configuration options
44086  */
44087  
44088  
44089 // define the action interface
44090 Roo.form.Action = function(form, options){
44091     this.form = form;
44092     this.options = options || {};
44093 };
44094 /**
44095  * Client Validation Failed
44096  * @const 
44097  */
44098 Roo.form.Action.CLIENT_INVALID = 'client';
44099 /**
44100  * Server Validation Failed
44101  * @const 
44102  */
44103  Roo.form.Action.SERVER_INVALID = 'server';
44104  /**
44105  * Connect to Server Failed
44106  * @const 
44107  */
44108 Roo.form.Action.CONNECT_FAILURE = 'connect';
44109 /**
44110  * Reading Data from Server Failed
44111  * @const 
44112  */
44113 Roo.form.Action.LOAD_FAILURE = 'load';
44114
44115 Roo.form.Action.prototype = {
44116     type : 'default',
44117     failureType : undefined,
44118     response : undefined,
44119     result : undefined,
44120
44121     // interface method
44122     run : function(options){
44123
44124     },
44125
44126     // interface method
44127     success : function(response){
44128
44129     },
44130
44131     // interface method
44132     handleResponse : function(response){
44133
44134     },
44135
44136     // default connection failure
44137     failure : function(response){
44138         
44139         this.response = response;
44140         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44141         this.form.afterAction(this, false);
44142     },
44143
44144     processResponse : function(response){
44145         this.response = response;
44146         if(!response.responseText){
44147             return true;
44148         }
44149         this.result = this.handleResponse(response);
44150         return this.result;
44151     },
44152
44153     // utility functions used internally
44154     getUrl : function(appendParams){
44155         var url = this.options.url || this.form.url || this.form.el.dom.action;
44156         if(appendParams){
44157             var p = this.getParams();
44158             if(p){
44159                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44160             }
44161         }
44162         return url;
44163     },
44164
44165     getMethod : function(){
44166         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44167     },
44168
44169     getParams : function(){
44170         var bp = this.form.baseParams;
44171         var p = this.options.params;
44172         if(p){
44173             if(typeof p == "object"){
44174                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44175             }else if(typeof p == 'string' && bp){
44176                 p += '&' + Roo.urlEncode(bp);
44177             }
44178         }else if(bp){
44179             p = Roo.urlEncode(bp);
44180         }
44181         return p;
44182     },
44183
44184     createCallback : function(){
44185         return {
44186             success: this.success,
44187             failure: this.failure,
44188             scope: this,
44189             timeout: (this.form.timeout*1000),
44190             upload: this.form.fileUpload ? this.success : undefined
44191         };
44192     }
44193 };
44194
44195 Roo.form.Action.Submit = function(form, options){
44196     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44197 };
44198
44199 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44200     type : 'submit',
44201
44202     haveProgress : false,
44203     uploadComplete : false,
44204     
44205     // uploadProgress indicator.
44206     uploadProgress : function()
44207     {
44208         if (!this.form.progressUrl) {
44209             return;
44210         }
44211         
44212         if (!this.haveProgress) {
44213             Roo.MessageBox.progress("Uploading", "Uploading");
44214         }
44215         if (this.uploadComplete) {
44216            Roo.MessageBox.hide();
44217            return;
44218         }
44219         
44220         this.haveProgress = true;
44221    
44222         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44223         
44224         var c = new Roo.data.Connection();
44225         c.request({
44226             url : this.form.progressUrl,
44227             params: {
44228                 id : uid
44229             },
44230             method: 'GET',
44231             success : function(req){
44232                //console.log(data);
44233                 var rdata = false;
44234                 var edata;
44235                 try  {
44236                    rdata = Roo.decode(req.responseText)
44237                 } catch (e) {
44238                     Roo.log("Invalid data from server..");
44239                     Roo.log(edata);
44240                     return;
44241                 }
44242                 if (!rdata || !rdata.success) {
44243                     Roo.log(rdata);
44244                     Roo.MessageBox.alert(Roo.encode(rdata));
44245                     return;
44246                 }
44247                 var data = rdata.data;
44248                 
44249                 if (this.uploadComplete) {
44250                    Roo.MessageBox.hide();
44251                    return;
44252                 }
44253                    
44254                 if (data){
44255                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44256                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44257                     );
44258                 }
44259                 this.uploadProgress.defer(2000,this);
44260             },
44261        
44262             failure: function(data) {
44263                 Roo.log('progress url failed ');
44264                 Roo.log(data);
44265             },
44266             scope : this
44267         });
44268            
44269     },
44270     
44271     
44272     run : function()
44273     {
44274         // run get Values on the form, so it syncs any secondary forms.
44275         this.form.getValues();
44276         
44277         var o = this.options;
44278         var method = this.getMethod();
44279         var isPost = method == 'POST';
44280         if(o.clientValidation === false || this.form.isValid()){
44281             
44282             if (this.form.progressUrl) {
44283                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44284                     (new Date() * 1) + '' + Math.random());
44285                     
44286             } 
44287             
44288             
44289             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44290                 form:this.form.el.dom,
44291                 url:this.getUrl(!isPost),
44292                 method: method,
44293                 params:isPost ? this.getParams() : null,
44294                 isUpload: this.form.fileUpload
44295             }));
44296             
44297             this.uploadProgress();
44298
44299         }else if (o.clientValidation !== false){ // client validation failed
44300             this.failureType = Roo.form.Action.CLIENT_INVALID;
44301             this.form.afterAction(this, false);
44302         }
44303     },
44304
44305     success : function(response)
44306     {
44307         this.uploadComplete= true;
44308         if (this.haveProgress) {
44309             Roo.MessageBox.hide();
44310         }
44311         
44312         
44313         var result = this.processResponse(response);
44314         if(result === true || result.success){
44315             this.form.afterAction(this, true);
44316             return;
44317         }
44318         if(result.errors){
44319             this.form.markInvalid(result.errors);
44320             this.failureType = Roo.form.Action.SERVER_INVALID;
44321         }
44322         this.form.afterAction(this, false);
44323     },
44324     failure : function(response)
44325     {
44326         this.uploadComplete= true;
44327         if (this.haveProgress) {
44328             Roo.MessageBox.hide();
44329         }
44330         
44331         this.response = response;
44332         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44333         this.form.afterAction(this, false);
44334     },
44335     
44336     handleResponse : function(response){
44337         if(this.form.errorReader){
44338             var rs = this.form.errorReader.read(response);
44339             var errors = [];
44340             if(rs.records){
44341                 for(var i = 0, len = rs.records.length; i < len; i++) {
44342                     var r = rs.records[i];
44343                     errors[i] = r.data;
44344                 }
44345             }
44346             if(errors.length < 1){
44347                 errors = null;
44348             }
44349             return {
44350                 success : rs.success,
44351                 errors : errors
44352             };
44353         }
44354         var ret = false;
44355         try {
44356             ret = Roo.decode(response.responseText);
44357         } catch (e) {
44358             ret = {
44359                 success: false,
44360                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44361                 errors : []
44362             };
44363         }
44364         return ret;
44365         
44366     }
44367 });
44368
44369
44370 Roo.form.Action.Load = function(form, options){
44371     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44372     this.reader = this.form.reader;
44373 };
44374
44375 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44376     type : 'load',
44377
44378     run : function(){
44379         
44380         Roo.Ajax.request(Roo.apply(
44381                 this.createCallback(), {
44382                     method:this.getMethod(),
44383                     url:this.getUrl(false),
44384                     params:this.getParams()
44385         }));
44386     },
44387
44388     success : function(response){
44389         
44390         var result = this.processResponse(response);
44391         if(result === true || !result.success || !result.data){
44392             this.failureType = Roo.form.Action.LOAD_FAILURE;
44393             this.form.afterAction(this, false);
44394             return;
44395         }
44396         this.form.clearInvalid();
44397         this.form.setValues(result.data);
44398         this.form.afterAction(this, true);
44399     },
44400
44401     handleResponse : function(response){
44402         if(this.form.reader){
44403             var rs = this.form.reader.read(response);
44404             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44405             return {
44406                 success : rs.success,
44407                 data : data
44408             };
44409         }
44410         return Roo.decode(response.responseText);
44411     }
44412 });
44413
44414 Roo.form.Action.ACTION_TYPES = {
44415     'load' : Roo.form.Action.Load,
44416     'submit' : Roo.form.Action.Submit
44417 };/*
44418  * Based on:
44419  * Ext JS Library 1.1.1
44420  * Copyright(c) 2006-2007, Ext JS, LLC.
44421  *
44422  * Originally Released Under LGPL - original licence link has changed is not relivant.
44423  *
44424  * Fork - LGPL
44425  * <script type="text/javascript">
44426  */
44427  
44428 /**
44429  * @class Roo.form.Layout
44430  * @extends Roo.Component
44431  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
44432  * @constructor
44433  * @param {Object} config Configuration options
44434  */
44435 Roo.form.Layout = function(config){
44436     var xitems = [];
44437     if (config.items) {
44438         xitems = config.items;
44439         delete config.items;
44440     }
44441     Roo.form.Layout.superclass.constructor.call(this, config);
44442     this.stack = [];
44443     Roo.each(xitems, this.addxtype, this);
44444      
44445 };
44446
44447 Roo.extend(Roo.form.Layout, Roo.Component, {
44448     /**
44449      * @cfg {String/Object} autoCreate
44450      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
44451      */
44452     /**
44453      * @cfg {String/Object/Function} style
44454      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
44455      * a function which returns such a specification.
44456      */
44457     /**
44458      * @cfg {String} labelAlign
44459      * Valid values are "left," "top" and "right" (defaults to "left")
44460      */
44461     /**
44462      * @cfg {Number} labelWidth
44463      * Fixed width in pixels of all field labels (defaults to undefined)
44464      */
44465     /**
44466      * @cfg {Boolean} clear
44467      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
44468      */
44469     clear : true,
44470     /**
44471      * @cfg {String} labelSeparator
44472      * The separator to use after field labels (defaults to ':')
44473      */
44474     labelSeparator : ':',
44475     /**
44476      * @cfg {Boolean} hideLabels
44477      * True to suppress the display of field labels in this layout (defaults to false)
44478      */
44479     hideLabels : false,
44480
44481     // private
44482     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
44483     
44484     isLayout : true,
44485     
44486     // private
44487     onRender : function(ct, position){
44488         if(this.el){ // from markup
44489             this.el = Roo.get(this.el);
44490         }else {  // generate
44491             var cfg = this.getAutoCreate();
44492             this.el = ct.createChild(cfg, position);
44493         }
44494         if(this.style){
44495             this.el.applyStyles(this.style);
44496         }
44497         if(this.labelAlign){
44498             this.el.addClass('x-form-label-'+this.labelAlign);
44499         }
44500         if(this.hideLabels){
44501             this.labelStyle = "display:none";
44502             this.elementStyle = "padding-left:0;";
44503         }else{
44504             if(typeof this.labelWidth == 'number'){
44505                 this.labelStyle = "width:"+this.labelWidth+"px;";
44506                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
44507             }
44508             if(this.labelAlign == 'top'){
44509                 this.labelStyle = "width:auto;";
44510                 this.elementStyle = "padding-left:0;";
44511             }
44512         }
44513         var stack = this.stack;
44514         var slen = stack.length;
44515         if(slen > 0){
44516             if(!this.fieldTpl){
44517                 var t = new Roo.Template(
44518                     '<div class="x-form-item {5}">',
44519                         '<label for="{0}" style="{2}">{1}{4}</label>',
44520                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44521                         '</div>',
44522                     '</div><div class="x-form-clear-left"></div>'
44523                 );
44524                 t.disableFormats = true;
44525                 t.compile();
44526                 Roo.form.Layout.prototype.fieldTpl = t;
44527             }
44528             for(var i = 0; i < slen; i++) {
44529                 if(stack[i].isFormField){
44530                     this.renderField(stack[i]);
44531                 }else{
44532                     this.renderComponent(stack[i]);
44533                 }
44534             }
44535         }
44536         if(this.clear){
44537             this.el.createChild({cls:'x-form-clear'});
44538         }
44539     },
44540
44541     // private
44542     renderField : function(f){
44543         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
44544                f.id, //0
44545                f.fieldLabel, //1
44546                f.labelStyle||this.labelStyle||'', //2
44547                this.elementStyle||'', //3
44548                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
44549                f.itemCls||this.itemCls||''  //5
44550        ], true).getPrevSibling());
44551     },
44552
44553     // private
44554     renderComponent : function(c){
44555         c.render(c.isLayout ? this.el : this.el.createChild());    
44556     },
44557     /**
44558      * Adds a object form elements (using the xtype property as the factory method.)
44559      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
44560      * @param {Object} config 
44561      */
44562     addxtype : function(o)
44563     {
44564         // create the lement.
44565         o.form = this.form;
44566         var fe = Roo.factory(o, Roo.form);
44567         this.form.allItems.push(fe);
44568         this.stack.push(fe);
44569         
44570         if (fe.isFormField) {
44571             this.form.items.add(fe);
44572         }
44573          
44574         return fe;
44575     }
44576 });
44577
44578 /**
44579  * @class Roo.form.Column
44580  * @extends Roo.form.Layout
44581  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
44582  * @constructor
44583  * @param {Object} config Configuration options
44584  */
44585 Roo.form.Column = function(config){
44586     Roo.form.Column.superclass.constructor.call(this, config);
44587 };
44588
44589 Roo.extend(Roo.form.Column, Roo.form.Layout, {
44590     /**
44591      * @cfg {Number/String} width
44592      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44593      */
44594     /**
44595      * @cfg {String/Object} autoCreate
44596      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
44597      */
44598
44599     // private
44600     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
44601
44602     // private
44603     onRender : function(ct, position){
44604         Roo.form.Column.superclass.onRender.call(this, ct, position);
44605         if(this.width){
44606             this.el.setWidth(this.width);
44607         }
44608     }
44609 });
44610
44611
44612 /**
44613  * @class Roo.form.Row
44614  * @extends Roo.form.Layout
44615  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
44616  * @constructor
44617  * @param {Object} config Configuration options
44618  */
44619
44620  
44621 Roo.form.Row = function(config){
44622     Roo.form.Row.superclass.constructor.call(this, config);
44623 };
44624  
44625 Roo.extend(Roo.form.Row, Roo.form.Layout, {
44626       /**
44627      * @cfg {Number/String} width
44628      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44629      */
44630     /**
44631      * @cfg {Number/String} height
44632      * The fixed height of the column in pixels or CSS value (defaults to "auto")
44633      */
44634     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
44635     
44636     padWidth : 20,
44637     // private
44638     onRender : function(ct, position){
44639         //console.log('row render');
44640         if(!this.rowTpl){
44641             var t = new Roo.Template(
44642                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
44643                     '<label for="{0}" style="{2}">{1}{4}</label>',
44644                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44645                     '</div>',
44646                 '</div>'
44647             );
44648             t.disableFormats = true;
44649             t.compile();
44650             Roo.form.Layout.prototype.rowTpl = t;
44651         }
44652         this.fieldTpl = this.rowTpl;
44653         
44654         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
44655         var labelWidth = 100;
44656         
44657         if ((this.labelAlign != 'top')) {
44658             if (typeof this.labelWidth == 'number') {
44659                 labelWidth = this.labelWidth
44660             }
44661             this.padWidth =  20 + labelWidth;
44662             
44663         }
44664         
44665         Roo.form.Column.superclass.onRender.call(this, ct, position);
44666         if(this.width){
44667             this.el.setWidth(this.width);
44668         }
44669         if(this.height){
44670             this.el.setHeight(this.height);
44671         }
44672     },
44673     
44674     // private
44675     renderField : function(f){
44676         f.fieldEl = this.fieldTpl.append(this.el, [
44677                f.id, f.fieldLabel,
44678                f.labelStyle||this.labelStyle||'',
44679                this.elementStyle||'',
44680                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
44681                f.itemCls||this.itemCls||'',
44682                f.width ? f.width + this.padWidth : 160 + this.padWidth
44683        ],true);
44684     }
44685 });
44686  
44687
44688 /**
44689  * @class Roo.form.FieldSet
44690  * @extends Roo.form.Layout
44691  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
44692  * @constructor
44693  * @param {Object} config Configuration options
44694  */
44695 Roo.form.FieldSet = function(config){
44696     Roo.form.FieldSet.superclass.constructor.call(this, config);
44697 };
44698
44699 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
44700     /**
44701      * @cfg {String} legend
44702      * The text to display as the legend for the FieldSet (defaults to '')
44703      */
44704     /**
44705      * @cfg {String/Object} autoCreate
44706      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
44707      */
44708
44709     // private
44710     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
44711
44712     // private
44713     onRender : function(ct, position){
44714         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
44715         if(this.legend){
44716             this.setLegend(this.legend);
44717         }
44718     },
44719
44720     // private
44721     setLegend : function(text){
44722         if(this.rendered){
44723             this.el.child('legend').update(text);
44724         }
44725     }
44726 });/*
44727  * Based on:
44728  * Ext JS Library 1.1.1
44729  * Copyright(c) 2006-2007, Ext JS, LLC.
44730  *
44731  * Originally Released Under LGPL - original licence link has changed is not relivant.
44732  *
44733  * Fork - LGPL
44734  * <script type="text/javascript">
44735  */
44736 /**
44737  * @class Roo.form.VTypes
44738  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
44739  * @singleton
44740  */
44741 Roo.form.VTypes = function(){
44742     // closure these in so they are only created once.
44743     var alpha = /^[a-zA-Z_]+$/;
44744     var alphanum = /^[a-zA-Z0-9_]+$/;
44745     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
44746     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
44747
44748     // All these messages and functions are configurable
44749     return {
44750         /**
44751          * The function used to validate email addresses
44752          * @param {String} value The email address
44753          */
44754         'email' : function(v){
44755             return email.test(v);
44756         },
44757         /**
44758          * The error text to display when the email validation function returns false
44759          * @type String
44760          */
44761         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
44762         /**
44763          * The keystroke filter mask to be applied on email input
44764          * @type RegExp
44765          */
44766         'emailMask' : /[a-z0-9_\.\-@]/i,
44767
44768         /**
44769          * The function used to validate URLs
44770          * @param {String} value The URL
44771          */
44772         'url' : function(v){
44773             return url.test(v);
44774         },
44775         /**
44776          * The error text to display when the url validation function returns false
44777          * @type String
44778          */
44779         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
44780         
44781         /**
44782          * The function used to validate alpha values
44783          * @param {String} value The value
44784          */
44785         'alpha' : function(v){
44786             return alpha.test(v);
44787         },
44788         /**
44789          * The error text to display when the alpha validation function returns false
44790          * @type String
44791          */
44792         'alphaText' : 'This field should only contain letters and _',
44793         /**
44794          * The keystroke filter mask to be applied on alpha input
44795          * @type RegExp
44796          */
44797         'alphaMask' : /[a-z_]/i,
44798
44799         /**
44800          * The function used to validate alphanumeric values
44801          * @param {String} value The value
44802          */
44803         'alphanum' : function(v){
44804             return alphanum.test(v);
44805         },
44806         /**
44807          * The error text to display when the alphanumeric validation function returns false
44808          * @type String
44809          */
44810         'alphanumText' : 'This field should only contain letters, numbers and _',
44811         /**
44812          * The keystroke filter mask to be applied on alphanumeric input
44813          * @type RegExp
44814          */
44815         'alphanumMask' : /[a-z0-9_]/i
44816     };
44817 }();//<script type="text/javascript">
44818
44819 /**
44820  * @class Roo.form.FCKeditor
44821  * @extends Roo.form.TextArea
44822  * Wrapper around the FCKEditor http://www.fckeditor.net
44823  * @constructor
44824  * Creates a new FCKeditor
44825  * @param {Object} config Configuration options
44826  */
44827 Roo.form.FCKeditor = function(config){
44828     Roo.form.FCKeditor.superclass.constructor.call(this, config);
44829     this.addEvents({
44830          /**
44831          * @event editorinit
44832          * Fired when the editor is initialized - you can add extra handlers here..
44833          * @param {FCKeditor} this
44834          * @param {Object} the FCK object.
44835          */
44836         editorinit : true
44837     });
44838     
44839     
44840 };
44841 Roo.form.FCKeditor.editors = { };
44842 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
44843 {
44844     //defaultAutoCreate : {
44845     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
44846     //},
44847     // private
44848     /**
44849      * @cfg {Object} fck options - see fck manual for details.
44850      */
44851     fckconfig : false,
44852     
44853     /**
44854      * @cfg {Object} fck toolbar set (Basic or Default)
44855      */
44856     toolbarSet : 'Basic',
44857     /**
44858      * @cfg {Object} fck BasePath
44859      */ 
44860     basePath : '/fckeditor/',
44861     
44862     
44863     frame : false,
44864     
44865     value : '',
44866     
44867    
44868     onRender : function(ct, position)
44869     {
44870         if(!this.el){
44871             this.defaultAutoCreate = {
44872                 tag: "textarea",
44873                 style:"width:300px;height:60px;",
44874                 autocomplete: "off"
44875             };
44876         }
44877         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
44878         /*
44879         if(this.grow){
44880             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
44881             if(this.preventScrollbars){
44882                 this.el.setStyle("overflow", "hidden");
44883             }
44884             this.el.setHeight(this.growMin);
44885         }
44886         */
44887         //console.log('onrender' + this.getId() );
44888         Roo.form.FCKeditor.editors[this.getId()] = this;
44889          
44890
44891         this.replaceTextarea() ;
44892         
44893     },
44894     
44895     getEditor : function() {
44896         return this.fckEditor;
44897     },
44898     /**
44899      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
44900      * @param {Mixed} value The value to set
44901      */
44902     
44903     
44904     setValue : function(value)
44905     {
44906         //console.log('setValue: ' + value);
44907         
44908         if(typeof(value) == 'undefined') { // not sure why this is happending...
44909             return;
44910         }
44911         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44912         
44913         //if(!this.el || !this.getEditor()) {
44914         //    this.value = value;
44915             //this.setValue.defer(100,this,[value]);    
44916         //    return;
44917         //} 
44918         
44919         if(!this.getEditor()) {
44920             return;
44921         }
44922         
44923         this.getEditor().SetData(value);
44924         
44925         //
44926
44927     },
44928
44929     /**
44930      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
44931      * @return {Mixed} value The field value
44932      */
44933     getValue : function()
44934     {
44935         
44936         if (this.frame && this.frame.dom.style.display == 'none') {
44937             return Roo.form.FCKeditor.superclass.getValue.call(this);
44938         }
44939         
44940         if(!this.el || !this.getEditor()) {
44941            
44942            // this.getValue.defer(100,this); 
44943             return this.value;
44944         }
44945        
44946         
44947         var value=this.getEditor().GetData();
44948         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44949         return Roo.form.FCKeditor.superclass.getValue.call(this);
44950         
44951
44952     },
44953
44954     /**
44955      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
44956      * @return {Mixed} value The field value
44957      */
44958     getRawValue : function()
44959     {
44960         if (this.frame && this.frame.dom.style.display == 'none') {
44961             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44962         }
44963         
44964         if(!this.el || !this.getEditor()) {
44965             //this.getRawValue.defer(100,this); 
44966             return this.value;
44967             return;
44968         }
44969         
44970         
44971         
44972         var value=this.getEditor().GetData();
44973         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
44974         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44975          
44976     },
44977     
44978     setSize : function(w,h) {
44979         
44980         
44981         
44982         //if (this.frame && this.frame.dom.style.display == 'none') {
44983         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44984         //    return;
44985         //}
44986         //if(!this.el || !this.getEditor()) {
44987         //    this.setSize.defer(100,this, [w,h]); 
44988         //    return;
44989         //}
44990         
44991         
44992         
44993         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44994         
44995         this.frame.dom.setAttribute('width', w);
44996         this.frame.dom.setAttribute('height', h);
44997         this.frame.setSize(w,h);
44998         
44999     },
45000     
45001     toggleSourceEdit : function(value) {
45002         
45003       
45004          
45005         this.el.dom.style.display = value ? '' : 'none';
45006         this.frame.dom.style.display = value ?  'none' : '';
45007         
45008     },
45009     
45010     
45011     focus: function(tag)
45012     {
45013         if (this.frame.dom.style.display == 'none') {
45014             return Roo.form.FCKeditor.superclass.focus.call(this);
45015         }
45016         if(!this.el || !this.getEditor()) {
45017             this.focus.defer(100,this, [tag]); 
45018             return;
45019         }
45020         
45021         
45022         
45023         
45024         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45025         this.getEditor().Focus();
45026         if (tgs.length) {
45027             if (!this.getEditor().Selection.GetSelection()) {
45028                 this.focus.defer(100,this, [tag]); 
45029                 return;
45030             }
45031             
45032             
45033             var r = this.getEditor().EditorDocument.createRange();
45034             r.setStart(tgs[0],0);
45035             r.setEnd(tgs[0],0);
45036             this.getEditor().Selection.GetSelection().removeAllRanges();
45037             this.getEditor().Selection.GetSelection().addRange(r);
45038             this.getEditor().Focus();
45039         }
45040         
45041     },
45042     
45043     
45044     
45045     replaceTextarea : function()
45046     {
45047         if ( document.getElementById( this.getId() + '___Frame' ) )
45048             return ;
45049         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45050         //{
45051             // We must check the elements firstly using the Id and then the name.
45052         var oTextarea = document.getElementById( this.getId() );
45053         
45054         var colElementsByName = document.getElementsByName( this.getId() ) ;
45055          
45056         oTextarea.style.display = 'none' ;
45057
45058         if ( oTextarea.tabIndex ) {            
45059             this.TabIndex = oTextarea.tabIndex ;
45060         }
45061         
45062         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45063         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45064         this.frame = Roo.get(this.getId() + '___Frame')
45065     },
45066     
45067     _getConfigHtml : function()
45068     {
45069         var sConfig = '' ;
45070
45071         for ( var o in this.fckconfig ) {
45072             sConfig += sConfig.length > 0  ? '&amp;' : '';
45073             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45074         }
45075
45076         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45077     },
45078     
45079     
45080     _getIFrameHtml : function()
45081     {
45082         var sFile = 'fckeditor.html' ;
45083         /* no idea what this is about..
45084         try
45085         {
45086             if ( (/fcksource=true/i).test( window.top.location.search ) )
45087                 sFile = 'fckeditor.original.html' ;
45088         }
45089         catch (e) { 
45090         */
45091
45092         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45093         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45094         
45095         
45096         var html = '<iframe id="' + this.getId() +
45097             '___Frame" src="' + sLink +
45098             '" width="' + this.width +
45099             '" height="' + this.height + '"' +
45100             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45101             ' frameborder="0" scrolling="no"></iframe>' ;
45102
45103         return html ;
45104     },
45105     
45106     _insertHtmlBefore : function( html, element )
45107     {
45108         if ( element.insertAdjacentHTML )       {
45109             // IE
45110             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45111         } else { // Gecko
45112             var oRange = document.createRange() ;
45113             oRange.setStartBefore( element ) ;
45114             var oFragment = oRange.createContextualFragment( html );
45115             element.parentNode.insertBefore( oFragment, element ) ;
45116         }
45117     }
45118     
45119     
45120   
45121     
45122     
45123     
45124     
45125
45126 });
45127
45128 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45129
45130 function FCKeditor_OnComplete(editorInstance){
45131     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45132     f.fckEditor = editorInstance;
45133     //console.log("loaded");
45134     f.fireEvent('editorinit', f, editorInstance);
45135
45136   
45137
45138  
45139
45140
45141
45142
45143
45144
45145
45146
45147
45148
45149
45150
45151
45152
45153
45154 //<script type="text/javascript">
45155 /**
45156  * @class Roo.form.GridField
45157  * @extends Roo.form.Field
45158  * Embed a grid (or editable grid into a form)
45159  * STATUS ALPHA
45160  * 
45161  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45162  * it needs 
45163  * xgrid.store = Roo.data.Store
45164  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45165  * xgrid.store.reader = Roo.data.JsonReader 
45166  * 
45167  * 
45168  * @constructor
45169  * Creates a new GridField
45170  * @param {Object} config Configuration options
45171  */
45172 Roo.form.GridField = function(config){
45173     Roo.form.GridField.superclass.constructor.call(this, config);
45174      
45175 };
45176
45177 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45178     /**
45179      * @cfg {Number} width  - used to restrict width of grid..
45180      */
45181     width : 100,
45182     /**
45183      * @cfg {Number} height - used to restrict height of grid..
45184      */
45185     height : 50,
45186      /**
45187      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45188          * 
45189          *}
45190      */
45191     xgrid : false, 
45192     /**
45193      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45194      * {tag: "input", type: "checkbox", autocomplete: "off"})
45195      */
45196    // defaultAutoCreate : { tag: 'div' },
45197     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45198     /**
45199      * @cfg {String} addTitle Text to include for adding a title.
45200      */
45201     addTitle : false,
45202     //
45203     onResize : function(){
45204         Roo.form.Field.superclass.onResize.apply(this, arguments);
45205     },
45206
45207     initEvents : function(){
45208         // Roo.form.Checkbox.superclass.initEvents.call(this);
45209         // has no events...
45210        
45211     },
45212
45213
45214     getResizeEl : function(){
45215         return this.wrap;
45216     },
45217
45218     getPositionEl : function(){
45219         return this.wrap;
45220     },
45221
45222     // private
45223     onRender : function(ct, position){
45224         
45225         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45226         var style = this.style;
45227         delete this.style;
45228         
45229         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45230         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45231         this.viewEl = this.wrap.createChild({ tag: 'div' });
45232         if (style) {
45233             this.viewEl.applyStyles(style);
45234         }
45235         if (this.width) {
45236             this.viewEl.setWidth(this.width);
45237         }
45238         if (this.height) {
45239             this.viewEl.setHeight(this.height);
45240         }
45241         //if(this.inputValue !== undefined){
45242         //this.setValue(this.value);
45243         
45244         
45245         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45246         
45247         
45248         this.grid.render();
45249         this.grid.getDataSource().on('remove', this.refreshValue, this);
45250         this.grid.getDataSource().on('update', this.refreshValue, this);
45251         this.grid.on('afteredit', this.refreshValue, this);
45252  
45253     },
45254      
45255     
45256     /**
45257      * Sets the value of the item. 
45258      * @param {String} either an object  or a string..
45259      */
45260     setValue : function(v){
45261         //this.value = v;
45262         v = v || []; // empty set..
45263         // this does not seem smart - it really only affects memoryproxy grids..
45264         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45265             var ds = this.grid.getDataSource();
45266             // assumes a json reader..
45267             var data = {}
45268             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45269             ds.loadData( data);
45270         }
45271         // clear selection so it does not get stale.
45272         if (this.grid.sm) { 
45273             this.grid.sm.clearSelections();
45274         }
45275         
45276         Roo.form.GridField.superclass.setValue.call(this, v);
45277         this.refreshValue();
45278         // should load data in the grid really....
45279     },
45280     
45281     // private
45282     refreshValue: function() {
45283          var val = [];
45284         this.grid.getDataSource().each(function(r) {
45285             val.push(r.data);
45286         });
45287         this.el.dom.value = Roo.encode(val);
45288     }
45289     
45290      
45291     
45292     
45293 });/*
45294  * Based on:
45295  * Ext JS Library 1.1.1
45296  * Copyright(c) 2006-2007, Ext JS, LLC.
45297  *
45298  * Originally Released Under LGPL - original licence link has changed is not relivant.
45299  *
45300  * Fork - LGPL
45301  * <script type="text/javascript">
45302  */
45303 /**
45304  * @class Roo.form.DisplayField
45305  * @extends Roo.form.Field
45306  * A generic Field to display non-editable data.
45307  * @constructor
45308  * Creates a new Display Field item.
45309  * @param {Object} config Configuration options
45310  */
45311 Roo.form.DisplayField = function(config){
45312     Roo.form.DisplayField.superclass.constructor.call(this, config);
45313     
45314 };
45315
45316 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45317     inputType:      'hidden',
45318     allowBlank:     true,
45319     readOnly:         true,
45320     
45321  
45322     /**
45323      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45324      */
45325     focusClass : undefined,
45326     /**
45327      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45328      */
45329     fieldClass: 'x-form-field',
45330     
45331      /**
45332      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45333      */
45334     valueRenderer: undefined,
45335     
45336     width: 100,
45337     /**
45338      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45339      * {tag: "input", type: "checkbox", autocomplete: "off"})
45340      */
45341      
45342  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45343
45344     onResize : function(){
45345         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45346         
45347     },
45348
45349     initEvents : function(){
45350         // Roo.form.Checkbox.superclass.initEvents.call(this);
45351         // has no events...
45352        
45353     },
45354
45355
45356     getResizeEl : function(){
45357         return this.wrap;
45358     },
45359
45360     getPositionEl : function(){
45361         return this.wrap;
45362     },
45363
45364     // private
45365     onRender : function(ct, position){
45366         
45367         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45368         //if(this.inputValue !== undefined){
45369         this.wrap = this.el.wrap();
45370         
45371         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45372         
45373         if (this.bodyStyle) {
45374             this.viewEl.applyStyles(this.bodyStyle);
45375         }
45376         //this.viewEl.setStyle('padding', '2px');
45377         
45378         this.setValue(this.value);
45379         
45380     },
45381 /*
45382     // private
45383     initValue : Roo.emptyFn,
45384
45385   */
45386
45387         // private
45388     onClick : function(){
45389         
45390     },
45391
45392     /**
45393      * Sets the checked state of the checkbox.
45394      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45395      */
45396     setValue : function(v){
45397         this.value = v;
45398         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45399         // this might be called before we have a dom element..
45400         if (!this.viewEl) {
45401             return;
45402         }
45403         this.viewEl.dom.innerHTML = html;
45404         Roo.form.DisplayField.superclass.setValue.call(this, v);
45405
45406     }
45407 });/*
45408  * 
45409  * Licence- LGPL
45410  * 
45411  */
45412
45413 /**
45414  * @class Roo.form.DayPicker
45415  * @extends Roo.form.Field
45416  * A Day picker show [M] [T] [W] ....
45417  * @constructor
45418  * Creates a new Day Picker
45419  * @param {Object} config Configuration options
45420  */
45421 Roo.form.DayPicker= function(config){
45422     Roo.form.DayPicker.superclass.constructor.call(this, config);
45423      
45424 };
45425
45426 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
45427     /**
45428      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45429      */
45430     focusClass : undefined,
45431     /**
45432      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45433      */
45434     fieldClass: "x-form-field",
45435    
45436     /**
45437      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45438      * {tag: "input", type: "checkbox", autocomplete: "off"})
45439      */
45440     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
45441     
45442    
45443     actionMode : 'viewEl', 
45444     //
45445     // private
45446  
45447     inputType : 'hidden',
45448     
45449      
45450     inputElement: false, // real input element?
45451     basedOn: false, // ????
45452     
45453     isFormField: true, // not sure where this is needed!!!!
45454
45455     onResize : function(){
45456         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
45457         if(!this.boxLabel){
45458             this.el.alignTo(this.wrap, 'c-c');
45459         }
45460     },
45461
45462     initEvents : function(){
45463         Roo.form.Checkbox.superclass.initEvents.call(this);
45464         this.el.on("click", this.onClick,  this);
45465         this.el.on("change", this.onClick,  this);
45466     },
45467
45468
45469     getResizeEl : function(){
45470         return this.wrap;
45471     },
45472
45473     getPositionEl : function(){
45474         return this.wrap;
45475     },
45476
45477     
45478     // private
45479     onRender : function(ct, position){
45480         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45481        
45482         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
45483         
45484         var r1 = '<table><tr>';
45485         var r2 = '<tr class="x-form-daypick-icons">';
45486         for (var i=0; i < 7; i++) {
45487             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
45488             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
45489         }
45490         
45491         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
45492         viewEl.select('img').on('click', this.onClick, this);
45493         this.viewEl = viewEl;   
45494         
45495         
45496         // this will not work on Chrome!!!
45497         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45498         this.el.on('propertychange', this.setFromHidden,  this);  //ie
45499         
45500         
45501           
45502
45503     },
45504
45505     // private
45506     initValue : Roo.emptyFn,
45507
45508     /**
45509      * Returns the checked state of the checkbox.
45510      * @return {Boolean} True if checked, else false
45511      */
45512     getValue : function(){
45513         return this.el.dom.value;
45514         
45515     },
45516
45517         // private
45518     onClick : function(e){ 
45519         //this.setChecked(!this.checked);
45520         Roo.get(e.target).toggleClass('x-menu-item-checked');
45521         this.refreshValue();
45522         //if(this.el.dom.checked != this.checked){
45523         //    this.setValue(this.el.dom.checked);
45524        // }
45525     },
45526     
45527     // private
45528     refreshValue : function()
45529     {
45530         var val = '';
45531         this.viewEl.select('img',true).each(function(e,i,n)  {
45532             val += e.is(".x-menu-item-checked") ? String(n) : '';
45533         });
45534         this.setValue(val, true);
45535     },
45536
45537     /**
45538      * Sets the checked state of the checkbox.
45539      * On is always based on a string comparison between inputValue and the param.
45540      * @param {Boolean/String} value - the value to set 
45541      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45542      */
45543     setValue : function(v,suppressEvent){
45544         if (!this.el.dom) {
45545             return;
45546         }
45547         var old = this.el.dom.value ;
45548         this.el.dom.value = v;
45549         if (suppressEvent) {
45550             return ;
45551         }
45552          
45553         // update display..
45554         this.viewEl.select('img',true).each(function(e,i,n)  {
45555             
45556             var on = e.is(".x-menu-item-checked");
45557             var newv = v.indexOf(String(n)) > -1;
45558             if (on != newv) {
45559                 e.toggleClass('x-menu-item-checked');
45560             }
45561             
45562         });
45563         
45564         
45565         this.fireEvent('change', this, v, old);
45566         
45567         
45568     },
45569    
45570     // handle setting of hidden value by some other method!!?!?
45571     setFromHidden: function()
45572     {
45573         if(!this.el){
45574             return;
45575         }
45576         //console.log("SET FROM HIDDEN");
45577         //alert('setFrom hidden');
45578         this.setValue(this.el.dom.value);
45579     },
45580     
45581     onDestroy : function()
45582     {
45583         if(this.viewEl){
45584             Roo.get(this.viewEl).remove();
45585         }
45586          
45587         Roo.form.DayPicker.superclass.onDestroy.call(this);
45588     }
45589
45590 });/*
45591  * RooJS Library 1.1.1
45592  * Copyright(c) 2008-2011  Alan Knowles
45593  *
45594  * License - LGPL
45595  */
45596  
45597
45598 /**
45599  * @class Roo.form.ComboCheck
45600  * @extends Roo.form.ComboBox
45601  * A combobox for multiple select items.
45602  *
45603  * FIXME - could do with a reset button..
45604  * 
45605  * @constructor
45606  * Create a new ComboCheck
45607  * @param {Object} config Configuration options
45608  */
45609 Roo.form.ComboCheck = function(config){
45610     Roo.form.ComboCheck.superclass.constructor.call(this, config);
45611     // should verify some data...
45612     // like
45613     // hiddenName = required..
45614     // displayField = required
45615     // valudField == required
45616     var req= [ 'hiddenName', 'displayField', 'valueField' ];
45617     var _t = this;
45618     Roo.each(req, function(e) {
45619         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
45620             throw "Roo.form.ComboCheck : missing value for: " + e;
45621         }
45622     });
45623     
45624     
45625 };
45626
45627 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
45628      
45629      
45630     editable : false,
45631      
45632     selectedClass: 'x-menu-item-checked', 
45633     
45634     // private
45635     onRender : function(ct, position){
45636         var _t = this;
45637         
45638         
45639         
45640         if(!this.tpl){
45641             var cls = 'x-combo-list';
45642
45643             
45644             this.tpl =  new Roo.Template({
45645                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
45646                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
45647                    '<span>{' + this.displayField + '}</span>' +
45648                     '</div>' 
45649                 
45650             });
45651         }
45652  
45653         
45654         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
45655         this.view.singleSelect = false;
45656         this.view.multiSelect = true;
45657         this.view.toggleSelect = true;
45658         this.pageTb.add(new Roo.Toolbar.Fill(), {
45659             
45660             text: 'Done',
45661             handler: function()
45662             {
45663                 _t.collapse();
45664             }
45665         });
45666     },
45667     
45668     onViewOver : function(e, t){
45669         // do nothing...
45670         return;
45671         
45672     },
45673     
45674     onViewClick : function(doFocus,index){
45675         return;
45676         
45677     },
45678     select: function () {
45679         //Roo.log("SELECT CALLED");
45680     },
45681      
45682     selectByValue : function(xv, scrollIntoView){
45683         var ar = this.getValueArray();
45684         var sels = [];
45685         
45686         Roo.each(ar, function(v) {
45687             if(v === undefined || v === null){
45688                 return;
45689             }
45690             var r = this.findRecord(this.valueField, v);
45691             if(r){
45692                 sels.push(this.store.indexOf(r))
45693                 
45694             }
45695         },this);
45696         this.view.select(sels);
45697         return false;
45698     },
45699     
45700     
45701     
45702     onSelect : function(record, index){
45703        // Roo.log("onselect Called");
45704        // this is only called by the clear button now..
45705         this.view.clearSelections();
45706         this.setValue('[]');
45707         if (this.value != this.valueBefore) {
45708             this.fireEvent('change', this, this.value, this.valueBefore);
45709             this.valueBefore = this.value;
45710         }
45711     },
45712     getValueArray : function()
45713     {
45714         var ar = [] ;
45715         
45716         try {
45717             //Roo.log(this.value);
45718             if (typeof(this.value) == 'undefined') {
45719                 return [];
45720             }
45721             var ar = Roo.decode(this.value);
45722             return  ar instanceof Array ? ar : []; //?? valid?
45723             
45724         } catch(e) {
45725             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
45726             return [];
45727         }
45728          
45729     },
45730     expand : function ()
45731     {
45732         
45733         Roo.form.ComboCheck.superclass.expand.call(this);
45734         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
45735         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
45736         
45737
45738     },
45739     
45740     collapse : function(){
45741         Roo.form.ComboCheck.superclass.collapse.call(this);
45742         var sl = this.view.getSelectedIndexes();
45743         var st = this.store;
45744         var nv = [];
45745         var tv = [];
45746         var r;
45747         Roo.each(sl, function(i) {
45748             r = st.getAt(i);
45749             nv.push(r.get(this.valueField));
45750         },this);
45751         this.setValue(Roo.encode(nv));
45752         if (this.value != this.valueBefore) {
45753
45754             this.fireEvent('change', this, this.value, this.valueBefore);
45755             this.valueBefore = this.value;
45756         }
45757         
45758     },
45759     
45760     setValue : function(v){
45761         // Roo.log(v);
45762         this.value = v;
45763         
45764         var vals = this.getValueArray();
45765         var tv = [];
45766         Roo.each(vals, function(k) {
45767             var r = this.findRecord(this.valueField, k);
45768             if(r){
45769                 tv.push(r.data[this.displayField]);
45770             }else if(this.valueNotFoundText !== undefined){
45771                 tv.push( this.valueNotFoundText );
45772             }
45773         },this);
45774        // Roo.log(tv);
45775         
45776         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
45777         this.hiddenField.value = v;
45778         this.value = v;
45779     }
45780     
45781 });/*
45782  * Based on:
45783  * Ext JS Library 1.1.1
45784  * Copyright(c) 2006-2007, Ext JS, LLC.
45785  *
45786  * Originally Released Under LGPL - original licence link has changed is not relivant.
45787  *
45788  * Fork - LGPL
45789  * <script type="text/javascript">
45790  */
45791  
45792 /**
45793  * @class Roo.form.Signature
45794  * @extends Roo.form.Field
45795  * Signature field.  
45796  * @constructor
45797  * 
45798  * @param {Object} config Configuration options
45799  */
45800
45801 Roo.form.Signature = function(config){
45802     Roo.form.Signature.superclass.constructor.call(this, config);
45803     
45804     this.addEvents({// not in used??
45805          /**
45806          * @event confirm
45807          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
45808              * @param {Roo.form.Signature} combo This combo box
45809              */
45810         'confirm' : true,
45811         /**
45812          * @event reset
45813          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
45814              * @param {Roo.form.ComboBox} combo This combo box
45815              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
45816              */
45817         'reset' : true
45818     });
45819 };
45820
45821 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
45822     /**
45823      * @cfg {Object} labels Label to use when rendering a form.
45824      * defaults to 
45825      * labels : { 
45826      *      clear : "Clear",
45827      *      confirm : "Confirm"
45828      *  }
45829      */
45830     labels : { 
45831         clear : "Clear",
45832         confirm : "Confirm"
45833     },
45834     /**
45835      * @cfg {Number} width The signature panel width (defaults to 300)
45836      */
45837     width: 300,
45838     /**
45839      * @cfg {Number} height The signature panel height (defaults to 100)
45840      */
45841     height : 100,
45842     /**
45843      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
45844      */
45845     allowBlank : false,
45846     
45847     //private
45848     // {Object} signPanel The signature SVG panel element (defaults to {})
45849     signPanel : {},
45850     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
45851     isMouseDown : false,
45852     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
45853     isConfirmed : false,
45854     // {String} signatureTmp SVG mapping string (defaults to empty string)
45855     signatureTmp : '',
45856     
45857     
45858     defaultAutoCreate : { // modified by initCompnoent..
45859         tag: "input",
45860         type:"hidden"
45861     },
45862
45863     // private
45864     onRender : function(ct, position){
45865         
45866         Roo.form.Signature.superclass.onRender.call(this, ct, position);
45867         
45868         this.wrap = this.el.wrap({
45869             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
45870         });
45871         
45872         this.createToolbar(this);
45873         this.signPanel = this.wrap.createChild({
45874                 tag: 'div',
45875                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
45876             }, this.el
45877         );
45878             
45879         this.svgID = Roo.id();
45880         this.svgEl = this.signPanel.createChild({
45881               xmlns : 'http://www.w3.org/2000/svg',
45882               tag : 'svg',
45883               id : this.svgID + "-svg",
45884               width: this.width,
45885               height: this.height,
45886               viewBox: '0 0 '+this.width+' '+this.height,
45887               cn : [
45888                 {
45889                     tag: "rect",
45890                     id: this.svgID + "-svg-r",
45891                     width: this.width,
45892                     height: this.height,
45893                     fill: "#ffa"
45894                 },
45895                 {
45896                     tag: "line",
45897                     id: this.svgID + "-svg-l",
45898                     x1: "0", // start
45899                     y1: (this.height*0.8), // start set the line in 80% of height
45900                     x2: this.width, // end
45901                     y2: (this.height*0.8), // end set the line in 80% of height
45902                     'stroke': "#666",
45903                     'stroke-width': "1",
45904                     'stroke-dasharray': "3",
45905                     'shape-rendering': "crispEdges",
45906                     'pointer-events': "none"
45907                 },
45908                 {
45909                     tag: "path",
45910                     id: this.svgID + "-svg-p",
45911                     'stroke': "navy",
45912                     'stroke-width': "3",
45913                     'fill': "none",
45914                     'pointer-events': 'none'
45915                 }
45916               ]
45917         });
45918         this.createSVG();
45919         this.svgBox = this.svgEl.dom.getScreenCTM();
45920     },
45921     createSVG : function(){ 
45922         var svg = this.signPanel;
45923         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
45924         var t = this;
45925
45926         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
45927         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
45928         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
45929         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
45930         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
45931         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
45932         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
45933         
45934     },
45935     isTouchEvent : function(e){
45936         return e.type.match(/^touch/);
45937     },
45938     getCoords : function (e) {
45939         var pt    = this.svgEl.dom.createSVGPoint();
45940         pt.x = e.clientX; 
45941         pt.y = e.clientY;
45942         if (this.isTouchEvent(e)) {
45943             pt.x =  e.targetTouches[0].clientX 
45944             pt.y = e.targetTouches[0].clientY;
45945         }
45946         var a = this.svgEl.dom.getScreenCTM();
45947         var b = a.inverse();
45948         var mx = pt.matrixTransform(b);
45949         return mx.x + ',' + mx.y;
45950     },
45951     //mouse event headler 
45952     down : function (e) {
45953         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
45954         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
45955         
45956         this.isMouseDown = true;
45957         
45958         e.preventDefault();
45959     },
45960     move : function (e) {
45961         if (this.isMouseDown) {
45962             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
45963             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
45964         }
45965         
45966         e.preventDefault();
45967     },
45968     up : function (e) {
45969         this.isMouseDown = false;
45970         var sp = this.signatureTmp.split(' ');
45971         
45972         if(sp.length > 1){
45973             if(!sp[sp.length-2].match(/^L/)){
45974                 sp.pop();
45975                 sp.pop();
45976                 sp.push("");
45977                 this.signatureTmp = sp.join(" ");
45978             }
45979         }
45980         if(this.getValue() != this.signatureTmp){
45981             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
45982             this.isConfirmed = false;
45983         }
45984         e.preventDefault();
45985     },
45986     
45987     /**
45988      * Protected method that will not generally be called directly. It
45989      * is called when the editor creates its toolbar. Override this method if you need to
45990      * add custom toolbar buttons.
45991      * @param {HtmlEditor} editor
45992      */
45993     createToolbar : function(editor){
45994          function btn(id, toggle, handler){
45995             var xid = fid + '-'+ id ;
45996             return {
45997                 id : xid,
45998                 cmd : id,
45999                 cls : 'x-btn-icon x-edit-'+id,
46000                 enableToggle:toggle !== false,
46001                 scope: editor, // was editor...
46002                 handler:handler||editor.relayBtnCmd,
46003                 clickEvent:'mousedown',
46004                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46005                 tabIndex:-1
46006             };
46007         }
46008         
46009         
46010         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46011         this.tb = tb;
46012         this.tb.add(
46013            {
46014                 cls : ' x-signature-btn x-signature-'+id,
46015                 scope: editor, // was editor...
46016                 handler: this.reset,
46017                 clickEvent:'mousedown',
46018                 text: this.labels.clear
46019             },
46020             {
46021                  xtype : 'Fill',
46022                  xns: Roo.Toolbar
46023             }, 
46024             {
46025                 cls : '  x-signature-btn x-signature-'+id,
46026                 scope: editor, // was editor...
46027                 handler: this.confirmHandler,
46028                 clickEvent:'mousedown',
46029                 text: this.labels.confirm
46030             }
46031         );
46032     
46033     },
46034     //public
46035     /**
46036      * when user is clicked confirm then show this image.....
46037      * 
46038      * @return {String} Image Data URI
46039      */
46040     getImageDataURI : function(){
46041         var svg = this.svgEl.dom.parentNode.innerHTML;
46042         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46043         return src; 
46044     },
46045     /**
46046      * 
46047      * @return {Boolean} this.isConfirmed
46048      */
46049     getConfirmed : function(){
46050         return this.isConfirmed;
46051     },
46052     /**
46053      * 
46054      * @return {Number} this.width
46055      */
46056     getWidth : function(){
46057         return this.width;
46058     },
46059     /**
46060      * 
46061      * @return {Number} this.height
46062      */
46063     getHeight : function(){
46064         return this.height;
46065     },
46066     // private
46067     getSignature : function(){
46068         return this.signatureTmp;
46069     },
46070     // private
46071     reset : function(){
46072         this.signatureTmp = '';
46073         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46074         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46075         this.isConfirmed = false;
46076         Roo.form.Signature.superclass.reset.call(this);
46077     },
46078     setSignature : function(s){
46079         this.signatureTmp = s;
46080         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46081         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46082         this.setValue(s);
46083         this.isConfirmed = false;
46084         Roo.form.Signature.superclass.reset.call(this);
46085     }, 
46086     test : function(){
46087 //        Roo.log(this.signPanel.dom.contentWindow.up())
46088     },
46089     //private
46090     setConfirmed : function(){
46091         
46092         
46093         
46094 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46095     },
46096     // private
46097     confirmHandler : function(){
46098         if(!this.getSignature()){
46099             return;
46100         }
46101         
46102         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46103         this.setValue(this.getSignature());
46104         this.isConfirmed = true;
46105         
46106         this.fireEvent('confirm', this);
46107     },
46108     // private
46109     // Subclasses should provide the validation implementation by overriding this
46110     validateValue : function(value){
46111         if(this.allowBlank){
46112             return true;
46113         }
46114         
46115         if(this.isConfirmed){
46116             return true;
46117         }
46118         return false;
46119     }
46120 });/*
46121  * Based on:
46122  * Ext JS Library 1.1.1
46123  * Copyright(c) 2006-2007, Ext JS, LLC.
46124  *
46125  * Originally Released Under LGPL - original licence link has changed is not relivant.
46126  *
46127  * Fork - LGPL
46128  * <script type="text/javascript">
46129  */
46130  
46131
46132 /**
46133  * @class Roo.form.ComboBox
46134  * @extends Roo.form.TriggerField
46135  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46136  * @constructor
46137  * Create a new ComboBox.
46138  * @param {Object} config Configuration options
46139  */
46140 Roo.form.Select = function(config){
46141     Roo.form.Select.superclass.constructor.call(this, config);
46142      
46143 };
46144
46145 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46146     /**
46147      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46148      */
46149     /**
46150      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46151      * rendering into an Roo.Editor, defaults to false)
46152      */
46153     /**
46154      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46155      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46156      */
46157     /**
46158      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46159      */
46160     /**
46161      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46162      * the dropdown list (defaults to undefined, with no header element)
46163      */
46164
46165      /**
46166      * @cfg {String/Roo.Template} tpl The template to use to render the output
46167      */
46168      
46169     // private
46170     defaultAutoCreate : {tag: "select"  },
46171     /**
46172      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46173      */
46174     listWidth: undefined,
46175     /**
46176      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46177      * mode = 'remote' or 'text' if mode = 'local')
46178      */
46179     displayField: undefined,
46180     /**
46181      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46182      * mode = 'remote' or 'value' if mode = 'local'). 
46183      * Note: use of a valueField requires the user make a selection
46184      * in order for a value to be mapped.
46185      */
46186     valueField: undefined,
46187     
46188     
46189     /**
46190      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46191      * field's data value (defaults to the underlying DOM element's name)
46192      */
46193     hiddenName: undefined,
46194     /**
46195      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46196      */
46197     listClass: '',
46198     /**
46199      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46200      */
46201     selectedClass: 'x-combo-selected',
46202     /**
46203      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46204      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46205      * which displays a downward arrow icon).
46206      */
46207     triggerClass : 'x-form-arrow-trigger',
46208     /**
46209      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46210      */
46211     shadow:'sides',
46212     /**
46213      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46214      * anchor positions (defaults to 'tl-bl')
46215      */
46216     listAlign: 'tl-bl?',
46217     /**
46218      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46219      */
46220     maxHeight: 300,
46221     /**
46222      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46223      * query specified by the allQuery config option (defaults to 'query')
46224      */
46225     triggerAction: 'query',
46226     /**
46227      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46228      * (defaults to 4, does not apply if editable = false)
46229      */
46230     minChars : 4,
46231     /**
46232      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46233      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46234      */
46235     typeAhead: false,
46236     /**
46237      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46238      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46239      */
46240     queryDelay: 500,
46241     /**
46242      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46243      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46244      */
46245     pageSize: 0,
46246     /**
46247      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46248      * when editable = true (defaults to false)
46249      */
46250     selectOnFocus:false,
46251     /**
46252      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46253      */
46254     queryParam: 'query',
46255     /**
46256      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46257      * when mode = 'remote' (defaults to 'Loading...')
46258      */
46259     loadingText: 'Loading...',
46260     /**
46261      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46262      */
46263     resizable: false,
46264     /**
46265      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46266      */
46267     handleHeight : 8,
46268     /**
46269      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46270      * traditional select (defaults to true)
46271      */
46272     editable: true,
46273     /**
46274      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46275      */
46276     allQuery: '',
46277     /**
46278      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46279      */
46280     mode: 'remote',
46281     /**
46282      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46283      * listWidth has a higher value)
46284      */
46285     minListWidth : 70,
46286     /**
46287      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46288      * allow the user to set arbitrary text into the field (defaults to false)
46289      */
46290     forceSelection:false,
46291     /**
46292      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46293      * if typeAhead = true (defaults to 250)
46294      */
46295     typeAheadDelay : 250,
46296     /**
46297      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46298      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46299      */
46300     valueNotFoundText : undefined,
46301     
46302     /**
46303      * @cfg {String} defaultValue The value displayed after loading the store.
46304      */
46305     defaultValue: '',
46306     
46307     /**
46308      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46309      */
46310     blockFocus : false,
46311     
46312     /**
46313      * @cfg {Boolean} disableClear Disable showing of clear button.
46314      */
46315     disableClear : false,
46316     /**
46317      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46318      */
46319     alwaysQuery : false,
46320     
46321     //private
46322     addicon : false,
46323     editicon: false,
46324     
46325     // element that contains real text value.. (when hidden is used..)
46326      
46327     // private
46328     onRender : function(ct, position){
46329         Roo.form.Field.prototype.onRender.call(this, ct, position);
46330         
46331         if(this.store){
46332             this.store.on('beforeload', this.onBeforeLoad, this);
46333             this.store.on('load', this.onLoad, this);
46334             this.store.on('loadexception', this.onLoadException, this);
46335             this.store.load({});
46336         }
46337         
46338         
46339         
46340     },
46341
46342     // private
46343     initEvents : function(){
46344         //Roo.form.ComboBox.superclass.initEvents.call(this);
46345  
46346     },
46347
46348     onDestroy : function(){
46349        
46350         if(this.store){
46351             this.store.un('beforeload', this.onBeforeLoad, this);
46352             this.store.un('load', this.onLoad, this);
46353             this.store.un('loadexception', this.onLoadException, this);
46354         }
46355         //Roo.form.ComboBox.superclass.onDestroy.call(this);
46356     },
46357
46358     // private
46359     fireKey : function(e){
46360         if(e.isNavKeyPress() && !this.list.isVisible()){
46361             this.fireEvent("specialkey", this, e);
46362         }
46363     },
46364
46365     // private
46366     onResize: function(w, h){
46367         
46368         return; 
46369     
46370         
46371     },
46372
46373     /**
46374      * Allow or prevent the user from directly editing the field text.  If false is passed,
46375      * the user will only be able to select from the items defined in the dropdown list.  This method
46376      * is the runtime equivalent of setting the 'editable' config option at config time.
46377      * @param {Boolean} value True to allow the user to directly edit the field text
46378      */
46379     setEditable : function(value){
46380          
46381     },
46382
46383     // private
46384     onBeforeLoad : function(){
46385         
46386         Roo.log("Select before load");
46387         return;
46388     
46389         this.innerList.update(this.loadingText ?
46390                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
46391         //this.restrictHeight();
46392         this.selectedIndex = -1;
46393     },
46394
46395     // private
46396     onLoad : function(){
46397
46398     
46399         var dom = this.el.dom;
46400         dom.innerHTML = '';
46401          var od = dom.ownerDocument;
46402          
46403         if (this.emptyText) {
46404             var op = od.createElement('option');
46405             op.setAttribute('value', '');
46406             op.innerHTML = String.format('{0}', this.emptyText);
46407             dom.appendChild(op);
46408         }
46409         if(this.store.getCount() > 0){
46410            
46411             var vf = this.valueField;
46412             var df = this.displayField;
46413             this.store.data.each(function(r) {
46414                 // which colmsn to use... testing - cdoe / title..
46415                 var op = od.createElement('option');
46416                 op.setAttribute('value', r.data[vf]);
46417                 op.innerHTML = String.format('{0}', r.data[df]);
46418                 dom.appendChild(op);
46419             });
46420             if (typeof(this.defaultValue != 'undefined')) {
46421                 this.setValue(this.defaultValue);
46422             }
46423             
46424              
46425         }else{
46426             //this.onEmptyResults();
46427         }
46428         //this.el.focus();
46429     },
46430     // private
46431     onLoadException : function()
46432     {
46433         dom.innerHTML = '';
46434             
46435         Roo.log("Select on load exception");
46436         return;
46437     
46438         this.collapse();
46439         Roo.log(this.store.reader.jsonData);
46440         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
46441             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
46442         }
46443         
46444         
46445     },
46446     // private
46447     onTypeAhead : function(){
46448          
46449     },
46450
46451     // private
46452     onSelect : function(record, index){
46453         Roo.log('on select?');
46454         return;
46455         if(this.fireEvent('beforeselect', this, record, index) !== false){
46456             this.setFromData(index > -1 ? record.data : false);
46457             this.collapse();
46458             this.fireEvent('select', this, record, index);
46459         }
46460     },
46461
46462     /**
46463      * Returns the currently selected field value or empty string if no value is set.
46464      * @return {String} value The selected value
46465      */
46466     getValue : function(){
46467         var dom = this.el.dom;
46468         this.value = dom.options[dom.selectedIndex].value;
46469         return this.value;
46470         
46471     },
46472
46473     /**
46474      * Clears any text/value currently set in the field
46475      */
46476     clearValue : function(){
46477         this.value = '';
46478         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
46479         
46480     },
46481
46482     /**
46483      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
46484      * will be displayed in the field.  If the value does not match the data value of an existing item,
46485      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
46486      * Otherwise the field will be blank (although the value will still be set).
46487      * @param {String} value The value to match
46488      */
46489     setValue : function(v){
46490         var d = this.el.dom;
46491         for (var i =0; i < d.options.length;i++) {
46492             if (v == d.options[i].value) {
46493                 d.selectedIndex = i;
46494                 this.value = v;
46495                 return;
46496             }
46497         }
46498         this.clearValue();
46499     },
46500     /**
46501      * @property {Object} the last set data for the element
46502      */
46503     
46504     lastData : false,
46505     /**
46506      * Sets the value of the field based on a object which is related to the record format for the store.
46507      * @param {Object} value the value to set as. or false on reset?
46508      */
46509     setFromData : function(o){
46510         Roo.log('setfrom data?');
46511          
46512         
46513         
46514     },
46515     // private
46516     reset : function(){
46517         this.clearValue();
46518     },
46519     // private
46520     findRecord : function(prop, value){
46521         
46522         return false;
46523     
46524         var record;
46525         if(this.store.getCount() > 0){
46526             this.store.each(function(r){
46527                 if(r.data[prop] == value){
46528                     record = r;
46529                     return false;
46530                 }
46531                 return true;
46532             });
46533         }
46534         return record;
46535     },
46536     
46537     getName: function()
46538     {
46539         // returns hidden if it's set..
46540         if (!this.rendered) {return ''};
46541         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
46542         
46543     },
46544      
46545
46546     
46547
46548     // private
46549     onEmptyResults : function(){
46550         Roo.log('empty results');
46551         //this.collapse();
46552     },
46553
46554     /**
46555      * Returns true if the dropdown list is expanded, else false.
46556      */
46557     isExpanded : function(){
46558         return false;
46559     },
46560
46561     /**
46562      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
46563      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
46564      * @param {String} value The data value of the item to select
46565      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
46566      * selected item if it is not currently in view (defaults to true)
46567      * @return {Boolean} True if the value matched an item in the list, else false
46568      */
46569     selectByValue : function(v, scrollIntoView){
46570         Roo.log('select By Value');
46571         return false;
46572     
46573         if(v !== undefined && v !== null){
46574             var r = this.findRecord(this.valueField || this.displayField, v);
46575             if(r){
46576                 this.select(this.store.indexOf(r), scrollIntoView);
46577                 return true;
46578             }
46579         }
46580         return false;
46581     },
46582
46583     /**
46584      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
46585      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
46586      * @param {Number} index The zero-based index of the list item to select
46587      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
46588      * selected item if it is not currently in view (defaults to true)
46589      */
46590     select : function(index, scrollIntoView){
46591         Roo.log('select ');
46592         return  ;
46593         
46594         this.selectedIndex = index;
46595         this.view.select(index);
46596         if(scrollIntoView !== false){
46597             var el = this.view.getNode(index);
46598             if(el){
46599                 this.innerList.scrollChildIntoView(el, false);
46600             }
46601         }
46602     },
46603
46604       
46605
46606     // private
46607     validateBlur : function(){
46608         
46609         return;
46610         
46611     },
46612
46613     // private
46614     initQuery : function(){
46615         this.doQuery(this.getRawValue());
46616     },
46617
46618     // private
46619     doForce : function(){
46620         if(this.el.dom.value.length > 0){
46621             this.el.dom.value =
46622                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
46623              
46624         }
46625     },
46626
46627     /**
46628      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
46629      * query allowing the query action to be canceled if needed.
46630      * @param {String} query The SQL query to execute
46631      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
46632      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
46633      * saved in the current store (defaults to false)
46634      */
46635     doQuery : function(q, forceAll){
46636         
46637         Roo.log('doQuery?');
46638         if(q === undefined || q === null){
46639             q = '';
46640         }
46641         var qe = {
46642             query: q,
46643             forceAll: forceAll,
46644             combo: this,
46645             cancel:false
46646         };
46647         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
46648             return false;
46649         }
46650         q = qe.query;
46651         forceAll = qe.forceAll;
46652         if(forceAll === true || (q.length >= this.minChars)){
46653             if(this.lastQuery != q || this.alwaysQuery){
46654                 this.lastQuery = q;
46655                 if(this.mode == 'local'){
46656                     this.selectedIndex = -1;
46657                     if(forceAll){
46658                         this.store.clearFilter();
46659                     }else{
46660                         this.store.filter(this.displayField, q);
46661                     }
46662                     this.onLoad();
46663                 }else{
46664                     this.store.baseParams[this.queryParam] = q;
46665                     this.store.load({
46666                         params: this.getParams(q)
46667                     });
46668                     this.expand();
46669                 }
46670             }else{
46671                 this.selectedIndex = -1;
46672                 this.onLoad();   
46673             }
46674         }
46675     },
46676
46677     // private
46678     getParams : function(q){
46679         var p = {};
46680         //p[this.queryParam] = q;
46681         if(this.pageSize){
46682             p.start = 0;
46683             p.limit = this.pageSize;
46684         }
46685         return p;
46686     },
46687
46688     /**
46689      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
46690      */
46691     collapse : function(){
46692         
46693     },
46694
46695     // private
46696     collapseIf : function(e){
46697         
46698     },
46699
46700     /**
46701      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
46702      */
46703     expand : function(){
46704         
46705     } ,
46706
46707     // private
46708      
46709
46710     /** 
46711     * @cfg {Boolean} grow 
46712     * @hide 
46713     */
46714     /** 
46715     * @cfg {Number} growMin 
46716     * @hide 
46717     */
46718     /** 
46719     * @cfg {Number} growMax 
46720     * @hide 
46721     */
46722     /**
46723      * @hide
46724      * @method autoSize
46725      */
46726     
46727     setWidth : function()
46728     {
46729         
46730     },
46731     getResizeEl : function(){
46732         return this.el;
46733     }
46734 });//<script type="text/javasscript">
46735  
46736
46737 /**
46738  * @class Roo.DDView
46739  * A DnD enabled version of Roo.View.
46740  * @param {Element/String} container The Element in which to create the View.
46741  * @param {String} tpl The template string used to create the markup for each element of the View
46742  * @param {Object} config The configuration properties. These include all the config options of
46743  * {@link Roo.View} plus some specific to this class.<br>
46744  * <p>
46745  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
46746  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
46747  * <p>
46748  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
46749 .x-view-drag-insert-above {
46750         border-top:1px dotted #3366cc;
46751 }
46752 .x-view-drag-insert-below {
46753         border-bottom:1px dotted #3366cc;
46754 }
46755 </code></pre>
46756  * 
46757  */
46758  
46759 Roo.DDView = function(container, tpl, config) {
46760     Roo.DDView.superclass.constructor.apply(this, arguments);
46761     this.getEl().setStyle("outline", "0px none");
46762     this.getEl().unselectable();
46763     if (this.dragGroup) {
46764                 this.setDraggable(this.dragGroup.split(","));
46765     }
46766     if (this.dropGroup) {
46767                 this.setDroppable(this.dropGroup.split(","));
46768     }
46769     if (this.deletable) {
46770         this.setDeletable();
46771     }
46772     this.isDirtyFlag = false;
46773         this.addEvents({
46774                 "drop" : true
46775         });
46776 };
46777
46778 Roo.extend(Roo.DDView, Roo.View, {
46779 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
46780 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
46781 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
46782 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
46783
46784         isFormField: true,
46785
46786         reset: Roo.emptyFn,
46787         
46788         clearInvalid: Roo.form.Field.prototype.clearInvalid,
46789
46790         validate: function() {
46791                 return true;
46792         },
46793         
46794         destroy: function() {
46795                 this.purgeListeners();
46796                 this.getEl.removeAllListeners();
46797                 this.getEl().remove();
46798                 if (this.dragZone) {
46799                         if (this.dragZone.destroy) {
46800                                 this.dragZone.destroy();
46801                         }
46802                 }
46803                 if (this.dropZone) {
46804                         if (this.dropZone.destroy) {
46805                                 this.dropZone.destroy();
46806                         }
46807                 }
46808         },
46809
46810 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
46811         getName: function() {
46812                 return this.name;
46813         },
46814
46815 /**     Loads the View from a JSON string representing the Records to put into the Store. */
46816         setValue: function(v) {
46817                 if (!this.store) {
46818                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
46819                 }
46820                 var data = {};
46821                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
46822                 this.store.proxy = new Roo.data.MemoryProxy(data);
46823                 this.store.load();
46824         },
46825
46826 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
46827         getValue: function() {
46828                 var result = '(';
46829                 this.store.each(function(rec) {
46830                         result += rec.id + ',';
46831                 });
46832                 return result.substr(0, result.length - 1) + ')';
46833         },
46834         
46835         getIds: function() {
46836                 var i = 0, result = new Array(this.store.getCount());
46837                 this.store.each(function(rec) {
46838                         result[i++] = rec.id;
46839                 });
46840                 return result;
46841         },
46842         
46843         isDirty: function() {
46844                 return this.isDirtyFlag;
46845         },
46846
46847 /**
46848  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
46849  *      whole Element becomes the target, and this causes the drop gesture to append.
46850  */
46851     getTargetFromEvent : function(e) {
46852                 var target = e.getTarget();
46853                 while ((target !== null) && (target.parentNode != this.el.dom)) {
46854                 target = target.parentNode;
46855                 }
46856                 if (!target) {
46857                         target = this.el.dom.lastChild || this.el.dom;
46858                 }
46859                 return target;
46860     },
46861
46862 /**
46863  *      Create the drag data which consists of an object which has the property "ddel" as
46864  *      the drag proxy element. 
46865  */
46866     getDragData : function(e) {
46867         var target = this.findItemFromChild(e.getTarget());
46868                 if(target) {
46869                         this.handleSelection(e);
46870                         var selNodes = this.getSelectedNodes();
46871             var dragData = {
46872                 source: this,
46873                 copy: this.copy || (this.allowCopy && e.ctrlKey),
46874                 nodes: selNodes,
46875                 records: []
46876                         };
46877                         var selectedIndices = this.getSelectedIndexes();
46878                         for (var i = 0; i < selectedIndices.length; i++) {
46879                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
46880                         }
46881                         if (selNodes.length == 1) {
46882                                 dragData.ddel = target.cloneNode(true); // the div element
46883                         } else {
46884                                 var div = document.createElement('div'); // create the multi element drag "ghost"
46885                                 div.className = 'multi-proxy';
46886                                 for (var i = 0, len = selNodes.length; i < len; i++) {
46887                                         div.appendChild(selNodes[i].cloneNode(true));
46888                                 }
46889                                 dragData.ddel = div;
46890                         }
46891             //console.log(dragData)
46892             //console.log(dragData.ddel.innerHTML)
46893                         return dragData;
46894                 }
46895         //console.log('nodragData')
46896                 return false;
46897     },
46898     
46899 /**     Specify to which ddGroup items in this DDView may be dragged. */
46900     setDraggable: function(ddGroup) {
46901         if (ddGroup instanceof Array) {
46902                 Roo.each(ddGroup, this.setDraggable, this);
46903                 return;
46904         }
46905         if (this.dragZone) {
46906                 this.dragZone.addToGroup(ddGroup);
46907         } else {
46908                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
46909                                 containerScroll: true,
46910                                 ddGroup: ddGroup 
46911
46912                         });
46913 //                      Draggability implies selection. DragZone's mousedown selects the element.
46914                         if (!this.multiSelect) { this.singleSelect = true; }
46915
46916 //                      Wire the DragZone's handlers up to methods in *this*
46917                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
46918                 }
46919     },
46920
46921 /**     Specify from which ddGroup this DDView accepts drops. */
46922     setDroppable: function(ddGroup) {
46923         if (ddGroup instanceof Array) {
46924                 Roo.each(ddGroup, this.setDroppable, this);
46925                 return;
46926         }
46927         if (this.dropZone) {
46928                 this.dropZone.addToGroup(ddGroup);
46929         } else {
46930                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
46931                                 containerScroll: true,
46932                                 ddGroup: ddGroup
46933                         });
46934
46935 //                      Wire the DropZone's handlers up to methods in *this*
46936                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
46937                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
46938                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
46939                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
46940                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
46941                 }
46942     },
46943
46944 /**     Decide whether to drop above or below a View node. */
46945     getDropPoint : function(e, n, dd){
46946         if (n == this.el.dom) { return "above"; }
46947                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
46948                 var c = t + (b - t) / 2;
46949                 var y = Roo.lib.Event.getPageY(e);
46950                 if(y <= c) {
46951                         return "above";
46952                 }else{
46953                         return "below";
46954                 }
46955     },
46956
46957     onNodeEnter : function(n, dd, e, data){
46958                 return false;
46959     },
46960     
46961     onNodeOver : function(n, dd, e, data){
46962                 var pt = this.getDropPoint(e, n, dd);
46963                 // set the insert point style on the target node
46964                 var dragElClass = this.dropNotAllowed;
46965                 if (pt) {
46966                         var targetElClass;
46967                         if (pt == "above"){
46968                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
46969                                 targetElClass = "x-view-drag-insert-above";
46970                         } else {
46971                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
46972                                 targetElClass = "x-view-drag-insert-below";
46973                         }
46974                         if (this.lastInsertClass != targetElClass){
46975                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
46976                                 this.lastInsertClass = targetElClass;
46977                         }
46978                 }
46979                 return dragElClass;
46980         },
46981
46982     onNodeOut : function(n, dd, e, data){
46983                 this.removeDropIndicators(n);
46984     },
46985
46986     onNodeDrop : function(n, dd, e, data){
46987         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
46988                 return false;
46989         }
46990         var pt = this.getDropPoint(e, n, dd);
46991                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
46992                 if (pt == "below") { insertAt++; }
46993                 for (var i = 0; i < data.records.length; i++) {
46994                         var r = data.records[i];
46995                         var dup = this.store.getById(r.id);
46996                         if (dup && (dd != this.dragZone)) {
46997                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
46998                         } else {
46999                                 if (data.copy) {
47000                                         this.store.insert(insertAt++, r.copy());
47001                                 } else {
47002                                         data.source.isDirtyFlag = true;
47003                                         r.store.remove(r);
47004                                         this.store.insert(insertAt++, r);
47005                                 }
47006                                 this.isDirtyFlag = true;
47007                         }
47008                 }
47009                 this.dragZone.cachedTarget = null;
47010                 return true;
47011     },
47012
47013     removeDropIndicators : function(n){
47014                 if(n){
47015                         Roo.fly(n).removeClass([
47016                                 "x-view-drag-insert-above",
47017                                 "x-view-drag-insert-below"]);
47018                         this.lastInsertClass = "_noclass";
47019                 }
47020     },
47021
47022 /**
47023  *      Utility method. Add a delete option to the DDView's context menu.
47024  *      @param {String} imageUrl The URL of the "delete" icon image.
47025  */
47026         setDeletable: function(imageUrl) {
47027                 if (!this.singleSelect && !this.multiSelect) {
47028                         this.singleSelect = true;
47029                 }
47030                 var c = this.getContextMenu();
47031                 this.contextMenu.on("itemclick", function(item) {
47032                         switch (item.id) {
47033                                 case "delete":
47034                                         this.remove(this.getSelectedIndexes());
47035                                         break;
47036                         }
47037                 }, this);
47038                 this.contextMenu.add({
47039                         icon: imageUrl,
47040                         id: "delete",
47041                         text: 'Delete'
47042                 });
47043         },
47044         
47045 /**     Return the context menu for this DDView. */
47046         getContextMenu: function() {
47047                 if (!this.contextMenu) {
47048 //                      Create the View's context menu
47049                         this.contextMenu = new Roo.menu.Menu({
47050                                 id: this.id + "-contextmenu"
47051                         });
47052                         this.el.on("contextmenu", this.showContextMenu, this);
47053                 }
47054                 return this.contextMenu;
47055         },
47056         
47057         disableContextMenu: function() {
47058                 if (this.contextMenu) {
47059                         this.el.un("contextmenu", this.showContextMenu, this);
47060                 }
47061         },
47062
47063         showContextMenu: function(e, item) {
47064         item = this.findItemFromChild(e.getTarget());
47065                 if (item) {
47066                         e.stopEvent();
47067                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47068                         this.contextMenu.showAt(e.getXY());
47069             }
47070     },
47071
47072 /**
47073  *      Remove {@link Roo.data.Record}s at the specified indices.
47074  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47075  */
47076     remove: function(selectedIndices) {
47077                 selectedIndices = [].concat(selectedIndices);
47078                 for (var i = 0; i < selectedIndices.length; i++) {
47079                         var rec = this.store.getAt(selectedIndices[i]);
47080                         this.store.remove(rec);
47081                 }
47082     },
47083
47084 /**
47085  *      Double click fires the event, but also, if this is draggable, and there is only one other
47086  *      related DropZone, it transfers the selected node.
47087  */
47088     onDblClick : function(e){
47089         var item = this.findItemFromChild(e.getTarget());
47090         if(item){
47091             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47092                 return false;
47093             }
47094             if (this.dragGroup) {
47095                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47096                     while (targets.indexOf(this.dropZone) > -1) {
47097                             targets.remove(this.dropZone);
47098                                 }
47099                     if (targets.length == 1) {
47100                                         this.dragZone.cachedTarget = null;
47101                         var el = Roo.get(targets[0].getEl());
47102                         var box = el.getBox(true);
47103                         targets[0].onNodeDrop(el.dom, {
47104                                 target: el.dom,
47105                                 xy: [box.x, box.y + box.height - 1]
47106                         }, null, this.getDragData(e));
47107                     }
47108                 }
47109         }
47110     },
47111     
47112     handleSelection: function(e) {
47113                 this.dragZone.cachedTarget = null;
47114         var item = this.findItemFromChild(e.getTarget());
47115         if (!item) {
47116                 this.clearSelections(true);
47117                 return;
47118         }
47119                 if (item && (this.multiSelect || this.singleSelect)){
47120                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47121                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47122                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47123                                 this.unselect(item);
47124                         } else {
47125                                 this.select(item, this.multiSelect && e.ctrlKey);
47126                                 this.lastSelection = item;
47127                         }
47128                 }
47129     },
47130
47131     onItemClick : function(item, index, e){
47132                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47133                         return false;
47134                 }
47135                 return true;
47136     },
47137
47138     unselect : function(nodeInfo, suppressEvent){
47139                 var node = this.getNode(nodeInfo);
47140                 if(node && this.isSelected(node)){
47141                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47142                                 Roo.fly(node).removeClass(this.selectedClass);
47143                                 this.selections.remove(node);
47144                                 if(!suppressEvent){
47145                                         this.fireEvent("selectionchange", this, this.selections);
47146                                 }
47147                         }
47148                 }
47149     }
47150 });
47151 /*
47152  * Based on:
47153  * Ext JS Library 1.1.1
47154  * Copyright(c) 2006-2007, Ext JS, LLC.
47155  *
47156  * Originally Released Under LGPL - original licence link has changed is not relivant.
47157  *
47158  * Fork - LGPL
47159  * <script type="text/javascript">
47160  */
47161  
47162 /**
47163  * @class Roo.LayoutManager
47164  * @extends Roo.util.Observable
47165  * Base class for layout managers.
47166  */
47167 Roo.LayoutManager = function(container, config){
47168     Roo.LayoutManager.superclass.constructor.call(this);
47169     this.el = Roo.get(container);
47170     // ie scrollbar fix
47171     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47172         document.body.scroll = "no";
47173     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47174         this.el.position('relative');
47175     }
47176     this.id = this.el.id;
47177     this.el.addClass("x-layout-container");
47178     /** false to disable window resize monitoring @type Boolean */
47179     this.monitorWindowResize = true;
47180     this.regions = {};
47181     this.addEvents({
47182         /**
47183          * @event layout
47184          * Fires when a layout is performed. 
47185          * @param {Roo.LayoutManager} this
47186          */
47187         "layout" : true,
47188         /**
47189          * @event regionresized
47190          * Fires when the user resizes a region. 
47191          * @param {Roo.LayoutRegion} region The resized region
47192          * @param {Number} newSize The new size (width for east/west, height for north/south)
47193          */
47194         "regionresized" : true,
47195         /**
47196          * @event regioncollapsed
47197          * Fires when a region is collapsed. 
47198          * @param {Roo.LayoutRegion} region The collapsed region
47199          */
47200         "regioncollapsed" : true,
47201         /**
47202          * @event regionexpanded
47203          * Fires when a region is expanded.  
47204          * @param {Roo.LayoutRegion} region The expanded region
47205          */
47206         "regionexpanded" : true
47207     });
47208     this.updating = false;
47209     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47210 };
47211
47212 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47213     /**
47214      * Returns true if this layout is currently being updated
47215      * @return {Boolean}
47216      */
47217     isUpdating : function(){
47218         return this.updating; 
47219     },
47220     
47221     /**
47222      * Suspend the LayoutManager from doing auto-layouts while
47223      * making multiple add or remove calls
47224      */
47225     beginUpdate : function(){
47226         this.updating = true;    
47227     },
47228     
47229     /**
47230      * Restore auto-layouts and optionally disable the manager from performing a layout
47231      * @param {Boolean} noLayout true to disable a layout update 
47232      */
47233     endUpdate : function(noLayout){
47234         this.updating = false;
47235         if(!noLayout){
47236             this.layout();
47237         }    
47238     },
47239     
47240     layout: function(){
47241         
47242     },
47243     
47244     onRegionResized : function(region, newSize){
47245         this.fireEvent("regionresized", region, newSize);
47246         this.layout();
47247     },
47248     
47249     onRegionCollapsed : function(region){
47250         this.fireEvent("regioncollapsed", region);
47251     },
47252     
47253     onRegionExpanded : function(region){
47254         this.fireEvent("regionexpanded", region);
47255     },
47256         
47257     /**
47258      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47259      * performs box-model adjustments.
47260      * @return {Object} The size as an object {width: (the width), height: (the height)}
47261      */
47262     getViewSize : function(){
47263         var size;
47264         if(this.el.dom != document.body){
47265             size = this.el.getSize();
47266         }else{
47267             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47268         }
47269         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47270         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47271         return size;
47272     },
47273     
47274     /**
47275      * Returns the Element this layout is bound to.
47276      * @return {Roo.Element}
47277      */
47278     getEl : function(){
47279         return this.el;
47280     },
47281     
47282     /**
47283      * Returns the specified region.
47284      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47285      * @return {Roo.LayoutRegion}
47286      */
47287     getRegion : function(target){
47288         return this.regions[target.toLowerCase()];
47289     },
47290     
47291     onWindowResize : function(){
47292         if(this.monitorWindowResize){
47293             this.layout();
47294         }
47295     }
47296 });/*
47297  * Based on:
47298  * Ext JS Library 1.1.1
47299  * Copyright(c) 2006-2007, Ext JS, LLC.
47300  *
47301  * Originally Released Under LGPL - original licence link has changed is not relivant.
47302  *
47303  * Fork - LGPL
47304  * <script type="text/javascript">
47305  */
47306 /**
47307  * @class Roo.BorderLayout
47308  * @extends Roo.LayoutManager
47309  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47310  * please see: <br><br>
47311  * <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>
47312  * <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>
47313  * Example:
47314  <pre><code>
47315  var layout = new Roo.BorderLayout(document.body, {
47316     north: {
47317         initialSize: 25,
47318         titlebar: false
47319     },
47320     west: {
47321         split:true,
47322         initialSize: 200,
47323         minSize: 175,
47324         maxSize: 400,
47325         titlebar: true,
47326         collapsible: true
47327     },
47328     east: {
47329         split:true,
47330         initialSize: 202,
47331         minSize: 175,
47332         maxSize: 400,
47333         titlebar: true,
47334         collapsible: true
47335     },
47336     south: {
47337         split:true,
47338         initialSize: 100,
47339         minSize: 100,
47340         maxSize: 200,
47341         titlebar: true,
47342         collapsible: true
47343     },
47344     center: {
47345         titlebar: true,
47346         autoScroll:true,
47347         resizeTabs: true,
47348         minTabWidth: 50,
47349         preferredTabWidth: 150
47350     }
47351 });
47352
47353 // shorthand
47354 var CP = Roo.ContentPanel;
47355
47356 layout.beginUpdate();
47357 layout.add("north", new CP("north", "North"));
47358 layout.add("south", new CP("south", {title: "South", closable: true}));
47359 layout.add("west", new CP("west", {title: "West"}));
47360 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
47361 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
47362 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
47363 layout.getRegion("center").showPanel("center1");
47364 layout.endUpdate();
47365 </code></pre>
47366
47367 <b>The container the layout is rendered into can be either the body element or any other element.
47368 If it is not the body element, the container needs to either be an absolute positioned element,
47369 or you will need to add "position:relative" to the css of the container.  You will also need to specify
47370 the container size if it is not the body element.</b>
47371
47372 * @constructor
47373 * Create a new BorderLayout
47374 * @param {String/HTMLElement/Element} container The container this layout is bound to
47375 * @param {Object} config Configuration options
47376  */
47377 Roo.BorderLayout = function(container, config){
47378     config = config || {};
47379     Roo.BorderLayout.superclass.constructor.call(this, container, config);
47380     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
47381     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
47382         var target = this.factory.validRegions[i];
47383         if(config[target]){
47384             this.addRegion(target, config[target]);
47385         }
47386     }
47387 };
47388
47389 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
47390     /**
47391      * Creates and adds a new region if it doesn't already exist.
47392      * @param {String} target The target region key (north, south, east, west or center).
47393      * @param {Object} config The regions config object
47394      * @return {BorderLayoutRegion} The new region
47395      */
47396     addRegion : function(target, config){
47397         if(!this.regions[target]){
47398             var r = this.factory.create(target, this, config);
47399             this.bindRegion(target, r);
47400         }
47401         return this.regions[target];
47402     },
47403
47404     // private (kinda)
47405     bindRegion : function(name, r){
47406         this.regions[name] = r;
47407         r.on("visibilitychange", this.layout, this);
47408         r.on("paneladded", this.layout, this);
47409         r.on("panelremoved", this.layout, this);
47410         r.on("invalidated", this.layout, this);
47411         r.on("resized", this.onRegionResized, this);
47412         r.on("collapsed", this.onRegionCollapsed, this);
47413         r.on("expanded", this.onRegionExpanded, this);
47414     },
47415
47416     /**
47417      * Performs a layout update.
47418      */
47419     layout : function(){
47420         if(this.updating) return;
47421         var size = this.getViewSize();
47422         var w = size.width;
47423         var h = size.height;
47424         var centerW = w;
47425         var centerH = h;
47426         var centerY = 0;
47427         var centerX = 0;
47428         //var x = 0, y = 0;
47429
47430         var rs = this.regions;
47431         var north = rs["north"];
47432         var south = rs["south"]; 
47433         var west = rs["west"];
47434         var east = rs["east"];
47435         var center = rs["center"];
47436         //if(this.hideOnLayout){ // not supported anymore
47437             //c.el.setStyle("display", "none");
47438         //}
47439         if(north && north.isVisible()){
47440             var b = north.getBox();
47441             var m = north.getMargins();
47442             b.width = w - (m.left+m.right);
47443             b.x = m.left;
47444             b.y = m.top;
47445             centerY = b.height + b.y + m.bottom;
47446             centerH -= centerY;
47447             north.updateBox(this.safeBox(b));
47448         }
47449         if(south && south.isVisible()){
47450             var b = south.getBox();
47451             var m = south.getMargins();
47452             b.width = w - (m.left+m.right);
47453             b.x = m.left;
47454             var totalHeight = (b.height + m.top + m.bottom);
47455             b.y = h - totalHeight + m.top;
47456             centerH -= totalHeight;
47457             south.updateBox(this.safeBox(b));
47458         }
47459         if(west && west.isVisible()){
47460             var b = west.getBox();
47461             var m = west.getMargins();
47462             b.height = centerH - (m.top+m.bottom);
47463             b.x = m.left;
47464             b.y = centerY + m.top;
47465             var totalWidth = (b.width + m.left + m.right);
47466             centerX += totalWidth;
47467             centerW -= totalWidth;
47468             west.updateBox(this.safeBox(b));
47469         }
47470         if(east && east.isVisible()){
47471             var b = east.getBox();
47472             var m = east.getMargins();
47473             b.height = centerH - (m.top+m.bottom);
47474             var totalWidth = (b.width + m.left + m.right);
47475             b.x = w - totalWidth + m.left;
47476             b.y = centerY + m.top;
47477             centerW -= totalWidth;
47478             east.updateBox(this.safeBox(b));
47479         }
47480         if(center){
47481             var m = center.getMargins();
47482             var centerBox = {
47483                 x: centerX + m.left,
47484                 y: centerY + m.top,
47485                 width: centerW - (m.left+m.right),
47486                 height: centerH - (m.top+m.bottom)
47487             };
47488             //if(this.hideOnLayout){
47489                 //center.el.setStyle("display", "block");
47490             //}
47491             center.updateBox(this.safeBox(centerBox));
47492         }
47493         this.el.repaint();
47494         this.fireEvent("layout", this);
47495     },
47496
47497     // private
47498     safeBox : function(box){
47499         box.width = Math.max(0, box.width);
47500         box.height = Math.max(0, box.height);
47501         return box;
47502     },
47503
47504     /**
47505      * Adds a ContentPanel (or subclass) to this layout.
47506      * @param {String} target The target region key (north, south, east, west or center).
47507      * @param {Roo.ContentPanel} panel The panel to add
47508      * @return {Roo.ContentPanel} The added panel
47509      */
47510     add : function(target, panel){
47511          
47512         target = target.toLowerCase();
47513         return this.regions[target].add(panel);
47514     },
47515
47516     /**
47517      * Remove a ContentPanel (or subclass) to this layout.
47518      * @param {String} target The target region key (north, south, east, west or center).
47519      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
47520      * @return {Roo.ContentPanel} The removed panel
47521      */
47522     remove : function(target, panel){
47523         target = target.toLowerCase();
47524         return this.regions[target].remove(panel);
47525     },
47526
47527     /**
47528      * Searches all regions for a panel with the specified id
47529      * @param {String} panelId
47530      * @return {Roo.ContentPanel} The panel or null if it wasn't found
47531      */
47532     findPanel : function(panelId){
47533         var rs = this.regions;
47534         for(var target in rs){
47535             if(typeof rs[target] != "function"){
47536                 var p = rs[target].getPanel(panelId);
47537                 if(p){
47538                     return p;
47539                 }
47540             }
47541         }
47542         return null;
47543     },
47544
47545     /**
47546      * Searches all regions for a panel with the specified id and activates (shows) it.
47547      * @param {String/ContentPanel} panelId The panels id or the panel itself
47548      * @return {Roo.ContentPanel} The shown panel or null
47549      */
47550     showPanel : function(panelId) {
47551       var rs = this.regions;
47552       for(var target in rs){
47553          var r = rs[target];
47554          if(typeof r != "function"){
47555             if(r.hasPanel(panelId)){
47556                return r.showPanel(panelId);
47557             }
47558          }
47559       }
47560       return null;
47561    },
47562
47563    /**
47564      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
47565      * @param {Roo.state.Provider} provider (optional) An alternate state provider
47566      */
47567     restoreState : function(provider){
47568         if(!provider){
47569             provider = Roo.state.Manager;
47570         }
47571         var sm = new Roo.LayoutStateManager();
47572         sm.init(this, provider);
47573     },
47574
47575     /**
47576      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
47577      * object should contain properties for each region to add ContentPanels to, and each property's value should be
47578      * a valid ContentPanel config object.  Example:
47579      * <pre><code>
47580 // Create the main layout
47581 var layout = new Roo.BorderLayout('main-ct', {
47582     west: {
47583         split:true,
47584         minSize: 175,
47585         titlebar: true
47586     },
47587     center: {
47588         title:'Components'
47589     }
47590 }, 'main-ct');
47591
47592 // Create and add multiple ContentPanels at once via configs
47593 layout.batchAdd({
47594    west: {
47595        id: 'source-files',
47596        autoCreate:true,
47597        title:'Ext Source Files',
47598        autoScroll:true,
47599        fitToFrame:true
47600    },
47601    center : {
47602        el: cview,
47603        autoScroll:true,
47604        fitToFrame:true,
47605        toolbar: tb,
47606        resizeEl:'cbody'
47607    }
47608 });
47609 </code></pre>
47610      * @param {Object} regions An object containing ContentPanel configs by region name
47611      */
47612     batchAdd : function(regions){
47613         this.beginUpdate();
47614         for(var rname in regions){
47615             var lr = this.regions[rname];
47616             if(lr){
47617                 this.addTypedPanels(lr, regions[rname]);
47618             }
47619         }
47620         this.endUpdate();
47621     },
47622
47623     // private
47624     addTypedPanels : function(lr, ps){
47625         if(typeof ps == 'string'){
47626             lr.add(new Roo.ContentPanel(ps));
47627         }
47628         else if(ps instanceof Array){
47629             for(var i =0, len = ps.length; i < len; i++){
47630                 this.addTypedPanels(lr, ps[i]);
47631             }
47632         }
47633         else if(!ps.events){ // raw config?
47634             var el = ps.el;
47635             delete ps.el; // prevent conflict
47636             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
47637         }
47638         else {  // panel object assumed!
47639             lr.add(ps);
47640         }
47641     },
47642     /**
47643      * Adds a xtype elements to the layout.
47644      * <pre><code>
47645
47646 layout.addxtype({
47647        xtype : 'ContentPanel',
47648        region: 'west',
47649        items: [ .... ]
47650    }
47651 );
47652
47653 layout.addxtype({
47654         xtype : 'NestedLayoutPanel',
47655         region: 'west',
47656         layout: {
47657            center: { },
47658            west: { }   
47659         },
47660         items : [ ... list of content panels or nested layout panels.. ]
47661    }
47662 );
47663 </code></pre>
47664      * @param {Object} cfg Xtype definition of item to add.
47665      */
47666     addxtype : function(cfg)
47667     {
47668         // basically accepts a pannel...
47669         // can accept a layout region..!?!?
47670         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
47671         
47672         if (!cfg.xtype.match(/Panel$/)) {
47673             return false;
47674         }
47675         var ret = false;
47676         
47677         if (typeof(cfg.region) == 'undefined') {
47678             Roo.log("Failed to add Panel, region was not set");
47679             Roo.log(cfg);
47680             return false;
47681         }
47682         var region = cfg.region;
47683         delete cfg.region;
47684         
47685           
47686         var xitems = [];
47687         if (cfg.items) {
47688             xitems = cfg.items;
47689             delete cfg.items;
47690         }
47691         var nb = false;
47692         
47693         switch(cfg.xtype) 
47694         {
47695             case 'ContentPanel':  // ContentPanel (el, cfg)
47696             case 'ScrollPanel':  // ContentPanel (el, cfg)
47697             case 'ViewPanel': 
47698                 if(cfg.autoCreate) {
47699                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
47700                 } else {
47701                     var el = this.el.createChild();
47702                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
47703                 }
47704                 
47705                 this.add(region, ret);
47706                 break;
47707             
47708             
47709             case 'TreePanel': // our new panel!
47710                 cfg.el = this.el.createChild();
47711                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
47712                 this.add(region, ret);
47713                 break;
47714             
47715             case 'NestedLayoutPanel': 
47716                 // create a new Layout (which is  a Border Layout...
47717                 var el = this.el.createChild();
47718                 var clayout = cfg.layout;
47719                 delete cfg.layout;
47720                 clayout.items   = clayout.items  || [];
47721                 // replace this exitems with the clayout ones..
47722                 xitems = clayout.items;
47723                  
47724                 
47725                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
47726                     cfg.background = false;
47727                 }
47728                 var layout = new Roo.BorderLayout(el, clayout);
47729                 
47730                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
47731                 //console.log('adding nested layout panel '  + cfg.toSource());
47732                 this.add(region, ret);
47733                 nb = {}; /// find first...
47734                 break;
47735                 
47736             case 'GridPanel': 
47737             
47738                 // needs grid and region
47739                 
47740                 //var el = this.getRegion(region).el.createChild();
47741                 var el = this.el.createChild();
47742                 // create the grid first...
47743                 
47744                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
47745                 delete cfg.grid;
47746                 if (region == 'center' && this.active ) {
47747                     cfg.background = false;
47748                 }
47749                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
47750                 
47751                 this.add(region, ret);
47752                 if (cfg.background) {
47753                     ret.on('activate', function(gp) {
47754                         if (!gp.grid.rendered) {
47755                             gp.grid.render();
47756                         }
47757                     });
47758                 } else {
47759                     grid.render();
47760                 }
47761                 break;
47762            
47763            
47764            
47765                 
47766                 
47767                 
47768             default: 
47769                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
47770                 return null;
47771              // GridPanel (grid, cfg)
47772             
47773         }
47774         this.beginUpdate();
47775         // add children..
47776         var region = '';
47777         var abn = {};
47778         Roo.each(xitems, function(i)  {
47779             region = nb && i.region ? i.region : false;
47780             
47781             var add = ret.addxtype(i);
47782            
47783             if (region) {
47784                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
47785                 if (!i.background) {
47786                     abn[region] = nb[region] ;
47787                 }
47788             }
47789             
47790         });
47791         this.endUpdate();
47792
47793         // make the last non-background panel active..
47794         //if (nb) { Roo.log(abn); }
47795         if (nb) {
47796             
47797             for(var r in abn) {
47798                 region = this.getRegion(r);
47799                 if (region) {
47800                     // tried using nb[r], but it does not work..
47801                      
47802                     region.showPanel(abn[r]);
47803                    
47804                 }
47805             }
47806         }
47807         return ret;
47808         
47809     }
47810 });
47811
47812 /**
47813  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
47814  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
47815  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
47816  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
47817  * <pre><code>
47818 // shorthand
47819 var CP = Roo.ContentPanel;
47820
47821 var layout = Roo.BorderLayout.create({
47822     north: {
47823         initialSize: 25,
47824         titlebar: false,
47825         panels: [new CP("north", "North")]
47826     },
47827     west: {
47828         split:true,
47829         initialSize: 200,
47830         minSize: 175,
47831         maxSize: 400,
47832         titlebar: true,
47833         collapsible: true,
47834         panels: [new CP("west", {title: "West"})]
47835     },
47836     east: {
47837         split:true,
47838         initialSize: 202,
47839         minSize: 175,
47840         maxSize: 400,
47841         titlebar: true,
47842         collapsible: true,
47843         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
47844     },
47845     south: {
47846         split:true,
47847         initialSize: 100,
47848         minSize: 100,
47849         maxSize: 200,
47850         titlebar: true,
47851         collapsible: true,
47852         panels: [new CP("south", {title: "South", closable: true})]
47853     },
47854     center: {
47855         titlebar: true,
47856         autoScroll:true,
47857         resizeTabs: true,
47858         minTabWidth: 50,
47859         preferredTabWidth: 150,
47860         panels: [
47861             new CP("center1", {title: "Close Me", closable: true}),
47862             new CP("center2", {title: "Center Panel", closable: false})
47863         ]
47864     }
47865 }, document.body);
47866
47867 layout.getRegion("center").showPanel("center1");
47868 </code></pre>
47869  * @param config
47870  * @param targetEl
47871  */
47872 Roo.BorderLayout.create = function(config, targetEl){
47873     var layout = new Roo.BorderLayout(targetEl || document.body, config);
47874     layout.beginUpdate();
47875     var regions = Roo.BorderLayout.RegionFactory.validRegions;
47876     for(var j = 0, jlen = regions.length; j < jlen; j++){
47877         var lr = regions[j];
47878         if(layout.regions[lr] && config[lr].panels){
47879             var r = layout.regions[lr];
47880             var ps = config[lr].panels;
47881             layout.addTypedPanels(r, ps);
47882         }
47883     }
47884     layout.endUpdate();
47885     return layout;
47886 };
47887
47888 // private
47889 Roo.BorderLayout.RegionFactory = {
47890     // private
47891     validRegions : ["north","south","east","west","center"],
47892
47893     // private
47894     create : function(target, mgr, config){
47895         target = target.toLowerCase();
47896         if(config.lightweight || config.basic){
47897             return new Roo.BasicLayoutRegion(mgr, config, target);
47898         }
47899         switch(target){
47900             case "north":
47901                 return new Roo.NorthLayoutRegion(mgr, config);
47902             case "south":
47903                 return new Roo.SouthLayoutRegion(mgr, config);
47904             case "east":
47905                 return new Roo.EastLayoutRegion(mgr, config);
47906             case "west":
47907                 return new Roo.WestLayoutRegion(mgr, config);
47908             case "center":
47909                 return new Roo.CenterLayoutRegion(mgr, config);
47910         }
47911         throw 'Layout region "'+target+'" not supported.';
47912     }
47913 };/*
47914  * Based on:
47915  * Ext JS Library 1.1.1
47916  * Copyright(c) 2006-2007, Ext JS, LLC.
47917  *
47918  * Originally Released Under LGPL - original licence link has changed is not relivant.
47919  *
47920  * Fork - LGPL
47921  * <script type="text/javascript">
47922  */
47923  
47924 /**
47925  * @class Roo.BasicLayoutRegion
47926  * @extends Roo.util.Observable
47927  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
47928  * and does not have a titlebar, tabs or any other features. All it does is size and position 
47929  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
47930  */
47931 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
47932     this.mgr = mgr;
47933     this.position  = pos;
47934     this.events = {
47935         /**
47936          * @scope Roo.BasicLayoutRegion
47937          */
47938         
47939         /**
47940          * @event beforeremove
47941          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
47942          * @param {Roo.LayoutRegion} this
47943          * @param {Roo.ContentPanel} panel The panel
47944          * @param {Object} e The cancel event object
47945          */
47946         "beforeremove" : true,
47947         /**
47948          * @event invalidated
47949          * Fires when the layout for this region is changed.
47950          * @param {Roo.LayoutRegion} this
47951          */
47952         "invalidated" : true,
47953         /**
47954          * @event visibilitychange
47955          * Fires when this region is shown or hidden 
47956          * @param {Roo.LayoutRegion} this
47957          * @param {Boolean} visibility true or false
47958          */
47959         "visibilitychange" : true,
47960         /**
47961          * @event paneladded
47962          * Fires when a panel is added. 
47963          * @param {Roo.LayoutRegion} this
47964          * @param {Roo.ContentPanel} panel The panel
47965          */
47966         "paneladded" : true,
47967         /**
47968          * @event panelremoved
47969          * Fires when a panel is removed. 
47970          * @param {Roo.LayoutRegion} this
47971          * @param {Roo.ContentPanel} panel The panel
47972          */
47973         "panelremoved" : true,
47974         /**
47975          * @event collapsed
47976          * Fires when this region is collapsed.
47977          * @param {Roo.LayoutRegion} this
47978          */
47979         "collapsed" : true,
47980         /**
47981          * @event expanded
47982          * Fires when this region is expanded.
47983          * @param {Roo.LayoutRegion} this
47984          */
47985         "expanded" : true,
47986         /**
47987          * @event slideshow
47988          * Fires when this region is slid into view.
47989          * @param {Roo.LayoutRegion} this
47990          */
47991         "slideshow" : true,
47992         /**
47993          * @event slidehide
47994          * Fires when this region slides out of view. 
47995          * @param {Roo.LayoutRegion} this
47996          */
47997         "slidehide" : true,
47998         /**
47999          * @event panelactivated
48000          * Fires when a panel is activated. 
48001          * @param {Roo.LayoutRegion} this
48002          * @param {Roo.ContentPanel} panel The activated panel
48003          */
48004         "panelactivated" : true,
48005         /**
48006          * @event resized
48007          * Fires when the user resizes this region. 
48008          * @param {Roo.LayoutRegion} this
48009          * @param {Number} newSize The new size (width for east/west, height for north/south)
48010          */
48011         "resized" : true
48012     };
48013     /** A collection of panels in this region. @type Roo.util.MixedCollection */
48014     this.panels = new Roo.util.MixedCollection();
48015     this.panels.getKey = this.getPanelId.createDelegate(this);
48016     this.box = null;
48017     this.activePanel = null;
48018     // ensure listeners are added...
48019     
48020     if (config.listeners || config.events) {
48021         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
48022             listeners : config.listeners || {},
48023             events : config.events || {}
48024         });
48025     }
48026     
48027     if(skipConfig !== true){
48028         this.applyConfig(config);
48029     }
48030 };
48031
48032 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
48033     getPanelId : function(p){
48034         return p.getId();
48035     },
48036     
48037     applyConfig : function(config){
48038         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48039         this.config = config;
48040         
48041     },
48042     
48043     /**
48044      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
48045      * the width, for horizontal (north, south) the height.
48046      * @param {Number} newSize The new width or height
48047      */
48048     resizeTo : function(newSize){
48049         var el = this.el ? this.el :
48050                  (this.activePanel ? this.activePanel.getEl() : null);
48051         if(el){
48052             switch(this.position){
48053                 case "east":
48054                 case "west":
48055                     el.setWidth(newSize);
48056                     this.fireEvent("resized", this, newSize);
48057                 break;
48058                 case "north":
48059                 case "south":
48060                     el.setHeight(newSize);
48061                     this.fireEvent("resized", this, newSize);
48062                 break;                
48063             }
48064         }
48065     },
48066     
48067     getBox : function(){
48068         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48069     },
48070     
48071     getMargins : function(){
48072         return this.margins;
48073     },
48074     
48075     updateBox : function(box){
48076         this.box = box;
48077         var el = this.activePanel.getEl();
48078         el.dom.style.left = box.x + "px";
48079         el.dom.style.top = box.y + "px";
48080         this.activePanel.setSize(box.width, box.height);
48081     },
48082     
48083     /**
48084      * Returns the container element for this region.
48085      * @return {Roo.Element}
48086      */
48087     getEl : function(){
48088         return this.activePanel;
48089     },
48090     
48091     /**
48092      * Returns true if this region is currently visible.
48093      * @return {Boolean}
48094      */
48095     isVisible : function(){
48096         return this.activePanel ? true : false;
48097     },
48098     
48099     setActivePanel : function(panel){
48100         panel = this.getPanel(panel);
48101         if(this.activePanel && this.activePanel != panel){
48102             this.activePanel.setActiveState(false);
48103             this.activePanel.getEl().setLeftTop(-10000,-10000);
48104         }
48105         this.activePanel = panel;
48106         panel.setActiveState(true);
48107         if(this.box){
48108             panel.setSize(this.box.width, this.box.height);
48109         }
48110         this.fireEvent("panelactivated", this, panel);
48111         this.fireEvent("invalidated");
48112     },
48113     
48114     /**
48115      * Show the specified panel.
48116      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48117      * @return {Roo.ContentPanel} The shown panel or null
48118      */
48119     showPanel : function(panel){
48120         if(panel = this.getPanel(panel)){
48121             this.setActivePanel(panel);
48122         }
48123         return panel;
48124     },
48125     
48126     /**
48127      * Get the active panel for this region.
48128      * @return {Roo.ContentPanel} The active panel or null
48129      */
48130     getActivePanel : function(){
48131         return this.activePanel;
48132     },
48133     
48134     /**
48135      * Add the passed ContentPanel(s)
48136      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48137      * @return {Roo.ContentPanel} The panel added (if only one was added)
48138      */
48139     add : function(panel){
48140         if(arguments.length > 1){
48141             for(var i = 0, len = arguments.length; i < len; i++) {
48142                 this.add(arguments[i]);
48143             }
48144             return null;
48145         }
48146         if(this.hasPanel(panel)){
48147             this.showPanel(panel);
48148             return panel;
48149         }
48150         var el = panel.getEl();
48151         if(el.dom.parentNode != this.mgr.el.dom){
48152             this.mgr.el.dom.appendChild(el.dom);
48153         }
48154         if(panel.setRegion){
48155             panel.setRegion(this);
48156         }
48157         this.panels.add(panel);
48158         el.setStyle("position", "absolute");
48159         if(!panel.background){
48160             this.setActivePanel(panel);
48161             if(this.config.initialSize && this.panels.getCount()==1){
48162                 this.resizeTo(this.config.initialSize);
48163             }
48164         }
48165         this.fireEvent("paneladded", this, panel);
48166         return panel;
48167     },
48168     
48169     /**
48170      * Returns true if the panel is in this region.
48171      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48172      * @return {Boolean}
48173      */
48174     hasPanel : function(panel){
48175         if(typeof panel == "object"){ // must be panel obj
48176             panel = panel.getId();
48177         }
48178         return this.getPanel(panel) ? true : false;
48179     },
48180     
48181     /**
48182      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48183      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48184      * @param {Boolean} preservePanel Overrides the config preservePanel option
48185      * @return {Roo.ContentPanel} The panel that was removed
48186      */
48187     remove : function(panel, preservePanel){
48188         panel = this.getPanel(panel);
48189         if(!panel){
48190             return null;
48191         }
48192         var e = {};
48193         this.fireEvent("beforeremove", this, panel, e);
48194         if(e.cancel === true){
48195             return null;
48196         }
48197         var panelId = panel.getId();
48198         this.panels.removeKey(panelId);
48199         return panel;
48200     },
48201     
48202     /**
48203      * Returns the panel specified or null if it's not in this region.
48204      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48205      * @return {Roo.ContentPanel}
48206      */
48207     getPanel : function(id){
48208         if(typeof id == "object"){ // must be panel obj
48209             return id;
48210         }
48211         return this.panels.get(id);
48212     },
48213     
48214     /**
48215      * Returns this regions position (north/south/east/west/center).
48216      * @return {String} 
48217      */
48218     getPosition: function(){
48219         return this.position;    
48220     }
48221 });/*
48222  * Based on:
48223  * Ext JS Library 1.1.1
48224  * Copyright(c) 2006-2007, Ext JS, LLC.
48225  *
48226  * Originally Released Under LGPL - original licence link has changed is not relivant.
48227  *
48228  * Fork - LGPL
48229  * <script type="text/javascript">
48230  */
48231  
48232 /**
48233  * @class Roo.LayoutRegion
48234  * @extends Roo.BasicLayoutRegion
48235  * This class represents a region in a layout manager.
48236  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48237  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48238  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48239  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48240  * @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})
48241  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48242  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48243  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48244  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48245  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48246  * @cfg {String}    title           The title for the region (overrides panel titles)
48247  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48248  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48249  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48250  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48251  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48252  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48253  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48254  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48255  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48256  * @cfg {Boolean}   showPin         True to show a pin button
48257  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48258  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48259  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48260  * @cfg {Number}    width           For East/West panels
48261  * @cfg {Number}    height          For North/South panels
48262  * @cfg {Boolean}   split           To show the splitter
48263  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48264  */
48265 Roo.LayoutRegion = function(mgr, config, pos){
48266     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48267     var dh = Roo.DomHelper;
48268     /** This region's container element 
48269     * @type Roo.Element */
48270     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48271     /** This region's title element 
48272     * @type Roo.Element */
48273
48274     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48275         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48276         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48277     ]}, true);
48278     this.titleEl.enableDisplayMode();
48279     /** This region's title text element 
48280     * @type HTMLElement */
48281     this.titleTextEl = this.titleEl.dom.firstChild;
48282     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48283     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48284     this.closeBtn.enableDisplayMode();
48285     this.closeBtn.on("click", this.closeClicked, this);
48286     this.closeBtn.hide();
48287
48288     this.createBody(config);
48289     this.visible = true;
48290     this.collapsed = false;
48291
48292     if(config.hideWhenEmpty){
48293         this.hide();
48294         this.on("paneladded", this.validateVisibility, this);
48295         this.on("panelremoved", this.validateVisibility, this);
48296     }
48297     this.applyConfig(config);
48298 };
48299
48300 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48301
48302     createBody : function(){
48303         /** This region's body element 
48304         * @type Roo.Element */
48305         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48306     },
48307
48308     applyConfig : function(c){
48309         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48310             var dh = Roo.DomHelper;
48311             if(c.titlebar !== false){
48312                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48313                 this.collapseBtn.on("click", this.collapse, this);
48314                 this.collapseBtn.enableDisplayMode();
48315
48316                 if(c.showPin === true || this.showPin){
48317                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48318                     this.stickBtn.enableDisplayMode();
48319                     this.stickBtn.on("click", this.expand, this);
48320                     this.stickBtn.hide();
48321                 }
48322             }
48323             /** This region's collapsed element
48324             * @type Roo.Element */
48325             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48326                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48327             ]}, true);
48328             if(c.floatable !== false){
48329                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48330                this.collapsedEl.on("click", this.collapseClick, this);
48331             }
48332
48333             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48334                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48335                    id: "message", unselectable: "on", style:{"float":"left"}});
48336                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
48337              }
48338             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
48339             this.expandBtn.on("click", this.expand, this);
48340         }
48341         if(this.collapseBtn){
48342             this.collapseBtn.setVisible(c.collapsible == true);
48343         }
48344         this.cmargins = c.cmargins || this.cmargins ||
48345                          (this.position == "west" || this.position == "east" ?
48346                              {top: 0, left: 2, right:2, bottom: 0} :
48347                              {top: 2, left: 0, right:0, bottom: 2});
48348         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48349         this.bottomTabs = c.tabPosition != "top";
48350         this.autoScroll = c.autoScroll || false;
48351         if(this.autoScroll){
48352             this.bodyEl.setStyle("overflow", "auto");
48353         }else{
48354             this.bodyEl.setStyle("overflow", "hidden");
48355         }
48356         //if(c.titlebar !== false){
48357             if((!c.titlebar && !c.title) || c.titlebar === false){
48358                 this.titleEl.hide();
48359             }else{
48360                 this.titleEl.show();
48361                 if(c.title){
48362                     this.titleTextEl.innerHTML = c.title;
48363                 }
48364             }
48365         //}
48366         this.duration = c.duration || .30;
48367         this.slideDuration = c.slideDuration || .45;
48368         this.config = c;
48369         if(c.collapsed){
48370             this.collapse(true);
48371         }
48372         if(c.hidden){
48373             this.hide();
48374         }
48375     },
48376     /**
48377      * Returns true if this region is currently visible.
48378      * @return {Boolean}
48379      */
48380     isVisible : function(){
48381         return this.visible;
48382     },
48383
48384     /**
48385      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
48386      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
48387      */
48388     setCollapsedTitle : function(title){
48389         title = title || "&#160;";
48390         if(this.collapsedTitleTextEl){
48391             this.collapsedTitleTextEl.innerHTML = title;
48392         }
48393     },
48394
48395     getBox : function(){
48396         var b;
48397         if(!this.collapsed){
48398             b = this.el.getBox(false, true);
48399         }else{
48400             b = this.collapsedEl.getBox(false, true);
48401         }
48402         return b;
48403     },
48404
48405     getMargins : function(){
48406         return this.collapsed ? this.cmargins : this.margins;
48407     },
48408
48409     highlight : function(){
48410         this.el.addClass("x-layout-panel-dragover");
48411     },
48412
48413     unhighlight : function(){
48414         this.el.removeClass("x-layout-panel-dragover");
48415     },
48416
48417     updateBox : function(box){
48418         this.box = box;
48419         if(!this.collapsed){
48420             this.el.dom.style.left = box.x + "px";
48421             this.el.dom.style.top = box.y + "px";
48422             this.updateBody(box.width, box.height);
48423         }else{
48424             this.collapsedEl.dom.style.left = box.x + "px";
48425             this.collapsedEl.dom.style.top = box.y + "px";
48426             this.collapsedEl.setSize(box.width, box.height);
48427         }
48428         if(this.tabs){
48429             this.tabs.autoSizeTabs();
48430         }
48431     },
48432
48433     updateBody : function(w, h){
48434         if(w !== null){
48435             this.el.setWidth(w);
48436             w -= this.el.getBorderWidth("rl");
48437             if(this.config.adjustments){
48438                 w += this.config.adjustments[0];
48439             }
48440         }
48441         if(h !== null){
48442             this.el.setHeight(h);
48443             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
48444             h -= this.el.getBorderWidth("tb");
48445             if(this.config.adjustments){
48446                 h += this.config.adjustments[1];
48447             }
48448             this.bodyEl.setHeight(h);
48449             if(this.tabs){
48450                 h = this.tabs.syncHeight(h);
48451             }
48452         }
48453         if(this.panelSize){
48454             w = w !== null ? w : this.panelSize.width;
48455             h = h !== null ? h : this.panelSize.height;
48456         }
48457         if(this.activePanel){
48458             var el = this.activePanel.getEl();
48459             w = w !== null ? w : el.getWidth();
48460             h = h !== null ? h : el.getHeight();
48461             this.panelSize = {width: w, height: h};
48462             this.activePanel.setSize(w, h);
48463         }
48464         if(Roo.isIE && this.tabs){
48465             this.tabs.el.repaint();
48466         }
48467     },
48468
48469     /**
48470      * Returns the container element for this region.
48471      * @return {Roo.Element}
48472      */
48473     getEl : function(){
48474         return this.el;
48475     },
48476
48477     /**
48478      * Hides this region.
48479      */
48480     hide : function(){
48481         if(!this.collapsed){
48482             this.el.dom.style.left = "-2000px";
48483             this.el.hide();
48484         }else{
48485             this.collapsedEl.dom.style.left = "-2000px";
48486             this.collapsedEl.hide();
48487         }
48488         this.visible = false;
48489         this.fireEvent("visibilitychange", this, false);
48490     },
48491
48492     /**
48493      * Shows this region if it was previously hidden.
48494      */
48495     show : function(){
48496         if(!this.collapsed){
48497             this.el.show();
48498         }else{
48499             this.collapsedEl.show();
48500         }
48501         this.visible = true;
48502         this.fireEvent("visibilitychange", this, true);
48503     },
48504
48505     closeClicked : function(){
48506         if(this.activePanel){
48507             this.remove(this.activePanel);
48508         }
48509     },
48510
48511     collapseClick : function(e){
48512         if(this.isSlid){
48513            e.stopPropagation();
48514            this.slideIn();
48515         }else{
48516            e.stopPropagation();
48517            this.slideOut();
48518         }
48519     },
48520
48521     /**
48522      * Collapses this region.
48523      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
48524      */
48525     collapse : function(skipAnim){
48526         if(this.collapsed) return;
48527         this.collapsed = true;
48528         if(this.split){
48529             this.split.el.hide();
48530         }
48531         if(this.config.animate && skipAnim !== true){
48532             this.fireEvent("invalidated", this);
48533             this.animateCollapse();
48534         }else{
48535             this.el.setLocation(-20000,-20000);
48536             this.el.hide();
48537             this.collapsedEl.show();
48538             this.fireEvent("collapsed", this);
48539             this.fireEvent("invalidated", this);
48540         }
48541     },
48542
48543     animateCollapse : function(){
48544         // overridden
48545     },
48546
48547     /**
48548      * Expands this region if it was previously collapsed.
48549      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
48550      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
48551      */
48552     expand : function(e, skipAnim){
48553         if(e) e.stopPropagation();
48554         if(!this.collapsed || this.el.hasActiveFx()) return;
48555         if(this.isSlid){
48556             this.afterSlideIn();
48557             skipAnim = true;
48558         }
48559         this.collapsed = false;
48560         if(this.config.animate && skipAnim !== true){
48561             this.animateExpand();
48562         }else{
48563             this.el.show();
48564             if(this.split){
48565                 this.split.el.show();
48566             }
48567             this.collapsedEl.setLocation(-2000,-2000);
48568             this.collapsedEl.hide();
48569             this.fireEvent("invalidated", this);
48570             this.fireEvent("expanded", this);
48571         }
48572     },
48573
48574     animateExpand : function(){
48575         // overridden
48576     },
48577
48578     initTabs : function()
48579     {
48580         this.bodyEl.setStyle("overflow", "hidden");
48581         var ts = new Roo.TabPanel(
48582                 this.bodyEl.dom,
48583                 {
48584                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
48585                     disableTooltips: this.config.disableTabTips,
48586                     toolbar : this.config.toolbar
48587                 }
48588         );
48589         if(this.config.hideTabs){
48590             ts.stripWrap.setDisplayed(false);
48591         }
48592         this.tabs = ts;
48593         ts.resizeTabs = this.config.resizeTabs === true;
48594         ts.minTabWidth = this.config.minTabWidth || 40;
48595         ts.maxTabWidth = this.config.maxTabWidth || 250;
48596         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
48597         ts.monitorResize = false;
48598         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
48599         ts.bodyEl.addClass('x-layout-tabs-body');
48600         this.panels.each(this.initPanelAsTab, this);
48601     },
48602
48603     initPanelAsTab : function(panel){
48604         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
48605                     this.config.closeOnTab && panel.isClosable());
48606         if(panel.tabTip !== undefined){
48607             ti.setTooltip(panel.tabTip);
48608         }
48609         ti.on("activate", function(){
48610               this.setActivePanel(panel);
48611         }, this);
48612         if(this.config.closeOnTab){
48613             ti.on("beforeclose", function(t, e){
48614                 e.cancel = true;
48615                 this.remove(panel);
48616             }, this);
48617         }
48618         return ti;
48619     },
48620
48621     updatePanelTitle : function(panel, title){
48622         if(this.activePanel == panel){
48623             this.updateTitle(title);
48624         }
48625         if(this.tabs){
48626             var ti = this.tabs.getTab(panel.getEl().id);
48627             ti.setText(title);
48628             if(panel.tabTip !== undefined){
48629                 ti.setTooltip(panel.tabTip);
48630             }
48631         }
48632     },
48633
48634     updateTitle : function(title){
48635         if(this.titleTextEl && !this.config.title){
48636             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
48637         }
48638     },
48639
48640     setActivePanel : function(panel){
48641         panel = this.getPanel(panel);
48642         if(this.activePanel && this.activePanel != panel){
48643             this.activePanel.setActiveState(false);
48644         }
48645         this.activePanel = panel;
48646         panel.setActiveState(true);
48647         if(this.panelSize){
48648             panel.setSize(this.panelSize.width, this.panelSize.height);
48649         }
48650         if(this.closeBtn){
48651             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
48652         }
48653         this.updateTitle(panel.getTitle());
48654         if(this.tabs){
48655             this.fireEvent("invalidated", this);
48656         }
48657         this.fireEvent("panelactivated", this, panel);
48658     },
48659
48660     /**
48661      * Shows the specified panel.
48662      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
48663      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
48664      */
48665     showPanel : function(panel){
48666         if(panel = this.getPanel(panel)){
48667             if(this.tabs){
48668                 var tab = this.tabs.getTab(panel.getEl().id);
48669                 if(tab.isHidden()){
48670                     this.tabs.unhideTab(tab.id);
48671                 }
48672                 tab.activate();
48673             }else{
48674                 this.setActivePanel(panel);
48675             }
48676         }
48677         return panel;
48678     },
48679
48680     /**
48681      * Get the active panel for this region.
48682      * @return {Roo.ContentPanel} The active panel or null
48683      */
48684     getActivePanel : function(){
48685         return this.activePanel;
48686     },
48687
48688     validateVisibility : function(){
48689         if(this.panels.getCount() < 1){
48690             this.updateTitle("&#160;");
48691             this.closeBtn.hide();
48692             this.hide();
48693         }else{
48694             if(!this.isVisible()){
48695                 this.show();
48696             }
48697         }
48698     },
48699
48700     /**
48701      * Adds the passed ContentPanel(s) to this region.
48702      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48703      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
48704      */
48705     add : function(panel){
48706         if(arguments.length > 1){
48707             for(var i = 0, len = arguments.length; i < len; i++) {
48708                 this.add(arguments[i]);
48709             }
48710             return null;
48711         }
48712         if(this.hasPanel(panel)){
48713             this.showPanel(panel);
48714             return panel;
48715         }
48716         panel.setRegion(this);
48717         this.panels.add(panel);
48718         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
48719             this.bodyEl.dom.appendChild(panel.getEl().dom);
48720             if(panel.background !== true){
48721                 this.setActivePanel(panel);
48722             }
48723             this.fireEvent("paneladded", this, panel);
48724             return panel;
48725         }
48726         if(!this.tabs){
48727             this.initTabs();
48728         }else{
48729             this.initPanelAsTab(panel);
48730         }
48731         if(panel.background !== true){
48732             this.tabs.activate(panel.getEl().id);
48733         }
48734         this.fireEvent("paneladded", this, panel);
48735         return panel;
48736     },
48737
48738     /**
48739      * Hides the tab for the specified panel.
48740      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48741      */
48742     hidePanel : function(panel){
48743         if(this.tabs && (panel = this.getPanel(panel))){
48744             this.tabs.hideTab(panel.getEl().id);
48745         }
48746     },
48747
48748     /**
48749      * Unhides the tab for a previously hidden panel.
48750      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48751      */
48752     unhidePanel : function(panel){
48753         if(this.tabs && (panel = this.getPanel(panel))){
48754             this.tabs.unhideTab(panel.getEl().id);
48755         }
48756     },
48757
48758     clearPanels : function(){
48759         while(this.panels.getCount() > 0){
48760              this.remove(this.panels.first());
48761         }
48762     },
48763
48764     /**
48765      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48766      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48767      * @param {Boolean} preservePanel Overrides the config preservePanel option
48768      * @return {Roo.ContentPanel} The panel that was removed
48769      */
48770     remove : function(panel, preservePanel){
48771         panel = this.getPanel(panel);
48772         if(!panel){
48773             return null;
48774         }
48775         var e = {};
48776         this.fireEvent("beforeremove", this, panel, e);
48777         if(e.cancel === true){
48778             return null;
48779         }
48780         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
48781         var panelId = panel.getId();
48782         this.panels.removeKey(panelId);
48783         if(preservePanel){
48784             document.body.appendChild(panel.getEl().dom);
48785         }
48786         if(this.tabs){
48787             this.tabs.removeTab(panel.getEl().id);
48788         }else if (!preservePanel){
48789             this.bodyEl.dom.removeChild(panel.getEl().dom);
48790         }
48791         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
48792             var p = this.panels.first();
48793             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
48794             tempEl.appendChild(p.getEl().dom);
48795             this.bodyEl.update("");
48796             this.bodyEl.dom.appendChild(p.getEl().dom);
48797             tempEl = null;
48798             this.updateTitle(p.getTitle());
48799             this.tabs = null;
48800             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
48801             this.setActivePanel(p);
48802         }
48803         panel.setRegion(null);
48804         if(this.activePanel == panel){
48805             this.activePanel = null;
48806         }
48807         if(this.config.autoDestroy !== false && preservePanel !== true){
48808             try{panel.destroy();}catch(e){}
48809         }
48810         this.fireEvent("panelremoved", this, panel);
48811         return panel;
48812     },
48813
48814     /**
48815      * Returns the TabPanel component used by this region
48816      * @return {Roo.TabPanel}
48817      */
48818     getTabs : function(){
48819         return this.tabs;
48820     },
48821
48822     createTool : function(parentEl, className){
48823         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
48824             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
48825         btn.addClassOnOver("x-layout-tools-button-over");
48826         return btn;
48827     }
48828 });/*
48829  * Based on:
48830  * Ext JS Library 1.1.1
48831  * Copyright(c) 2006-2007, Ext JS, LLC.
48832  *
48833  * Originally Released Under LGPL - original licence link has changed is not relivant.
48834  *
48835  * Fork - LGPL
48836  * <script type="text/javascript">
48837  */
48838  
48839
48840
48841 /**
48842  * @class Roo.SplitLayoutRegion
48843  * @extends Roo.LayoutRegion
48844  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
48845  */
48846 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
48847     this.cursor = cursor;
48848     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
48849 };
48850
48851 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
48852     splitTip : "Drag to resize.",
48853     collapsibleSplitTip : "Drag to resize. Double click to hide.",
48854     useSplitTips : false,
48855
48856     applyConfig : function(config){
48857         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
48858         if(config.split){
48859             if(!this.split){
48860                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
48861                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
48862                 /** The SplitBar for this region 
48863                 * @type Roo.SplitBar */
48864                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
48865                 this.split.on("moved", this.onSplitMove, this);
48866                 this.split.useShim = config.useShim === true;
48867                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
48868                 if(this.useSplitTips){
48869                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
48870                 }
48871                 if(config.collapsible){
48872                     this.split.el.on("dblclick", this.collapse,  this);
48873                 }
48874             }
48875             if(typeof config.minSize != "undefined"){
48876                 this.split.minSize = config.minSize;
48877             }
48878             if(typeof config.maxSize != "undefined"){
48879                 this.split.maxSize = config.maxSize;
48880             }
48881             if(config.hideWhenEmpty || config.hidden || config.collapsed){
48882                 this.hideSplitter();
48883             }
48884         }
48885     },
48886
48887     getHMaxSize : function(){
48888          var cmax = this.config.maxSize || 10000;
48889          var center = this.mgr.getRegion("center");
48890          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
48891     },
48892
48893     getVMaxSize : function(){
48894          var cmax = this.config.maxSize || 10000;
48895          var center = this.mgr.getRegion("center");
48896          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
48897     },
48898
48899     onSplitMove : function(split, newSize){
48900         this.fireEvent("resized", this, newSize);
48901     },
48902     
48903     /** 
48904      * Returns the {@link Roo.SplitBar} for this region.
48905      * @return {Roo.SplitBar}
48906      */
48907     getSplitBar : function(){
48908         return this.split;
48909     },
48910     
48911     hide : function(){
48912         this.hideSplitter();
48913         Roo.SplitLayoutRegion.superclass.hide.call(this);
48914     },
48915
48916     hideSplitter : function(){
48917         if(this.split){
48918             this.split.el.setLocation(-2000,-2000);
48919             this.split.el.hide();
48920         }
48921     },
48922
48923     show : function(){
48924         if(this.split){
48925             this.split.el.show();
48926         }
48927         Roo.SplitLayoutRegion.superclass.show.call(this);
48928     },
48929     
48930     beforeSlide: function(){
48931         if(Roo.isGecko){// firefox overflow auto bug workaround
48932             this.bodyEl.clip();
48933             if(this.tabs) this.tabs.bodyEl.clip();
48934             if(this.activePanel){
48935                 this.activePanel.getEl().clip();
48936                 
48937                 if(this.activePanel.beforeSlide){
48938                     this.activePanel.beforeSlide();
48939                 }
48940             }
48941         }
48942     },
48943     
48944     afterSlide : function(){
48945         if(Roo.isGecko){// firefox overflow auto bug workaround
48946             this.bodyEl.unclip();
48947             if(this.tabs) this.tabs.bodyEl.unclip();
48948             if(this.activePanel){
48949                 this.activePanel.getEl().unclip();
48950                 if(this.activePanel.afterSlide){
48951                     this.activePanel.afterSlide();
48952                 }
48953             }
48954         }
48955     },
48956
48957     initAutoHide : function(){
48958         if(this.autoHide !== false){
48959             if(!this.autoHideHd){
48960                 var st = new Roo.util.DelayedTask(this.slideIn, this);
48961                 this.autoHideHd = {
48962                     "mouseout": function(e){
48963                         if(!e.within(this.el, true)){
48964                             st.delay(500);
48965                         }
48966                     },
48967                     "mouseover" : function(e){
48968                         st.cancel();
48969                     },
48970                     scope : this
48971                 };
48972             }
48973             this.el.on(this.autoHideHd);
48974         }
48975     },
48976
48977     clearAutoHide : function(){
48978         if(this.autoHide !== false){
48979             this.el.un("mouseout", this.autoHideHd.mouseout);
48980             this.el.un("mouseover", this.autoHideHd.mouseover);
48981         }
48982     },
48983
48984     clearMonitor : function(){
48985         Roo.get(document).un("click", this.slideInIf, this);
48986     },
48987
48988     // these names are backwards but not changed for compat
48989     slideOut : function(){
48990         if(this.isSlid || this.el.hasActiveFx()){
48991             return;
48992         }
48993         this.isSlid = true;
48994         if(this.collapseBtn){
48995             this.collapseBtn.hide();
48996         }
48997         this.closeBtnState = this.closeBtn.getStyle('display');
48998         this.closeBtn.hide();
48999         if(this.stickBtn){
49000             this.stickBtn.show();
49001         }
49002         this.el.show();
49003         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49004         this.beforeSlide();
49005         this.el.setStyle("z-index", 10001);
49006         this.el.slideIn(this.getSlideAnchor(), {
49007             callback: function(){
49008                 this.afterSlide();
49009                 this.initAutoHide();
49010                 Roo.get(document).on("click", this.slideInIf, this);
49011                 this.fireEvent("slideshow", this);
49012             },
49013             scope: this,
49014             block: true
49015         });
49016     },
49017
49018     afterSlideIn : function(){
49019         this.clearAutoHide();
49020         this.isSlid = false;
49021         this.clearMonitor();
49022         this.el.setStyle("z-index", "");
49023         if(this.collapseBtn){
49024             this.collapseBtn.show();
49025         }
49026         this.closeBtn.setStyle('display', this.closeBtnState);
49027         if(this.stickBtn){
49028             this.stickBtn.hide();
49029         }
49030         this.fireEvent("slidehide", this);
49031     },
49032
49033     slideIn : function(cb){
49034         if(!this.isSlid || this.el.hasActiveFx()){
49035             Roo.callback(cb);
49036             return;
49037         }
49038         this.isSlid = false;
49039         this.beforeSlide();
49040         this.el.slideOut(this.getSlideAnchor(), {
49041             callback: function(){
49042                 this.el.setLeftTop(-10000, -10000);
49043                 this.afterSlide();
49044                 this.afterSlideIn();
49045                 Roo.callback(cb);
49046             },
49047             scope: this,
49048             block: true
49049         });
49050     },
49051     
49052     slideInIf : function(e){
49053         if(!e.within(this.el)){
49054             this.slideIn();
49055         }
49056     },
49057
49058     animateCollapse : function(){
49059         this.beforeSlide();
49060         this.el.setStyle("z-index", 20000);
49061         var anchor = this.getSlideAnchor();
49062         this.el.slideOut(anchor, {
49063             callback : function(){
49064                 this.el.setStyle("z-index", "");
49065                 this.collapsedEl.slideIn(anchor, {duration:.3});
49066                 this.afterSlide();
49067                 this.el.setLocation(-10000,-10000);
49068                 this.el.hide();
49069                 this.fireEvent("collapsed", this);
49070             },
49071             scope: this,
49072             block: true
49073         });
49074     },
49075
49076     animateExpand : function(){
49077         this.beforeSlide();
49078         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49079         this.el.setStyle("z-index", 20000);
49080         this.collapsedEl.hide({
49081             duration:.1
49082         });
49083         this.el.slideIn(this.getSlideAnchor(), {
49084             callback : function(){
49085                 this.el.setStyle("z-index", "");
49086                 this.afterSlide();
49087                 if(this.split){
49088                     this.split.el.show();
49089                 }
49090                 this.fireEvent("invalidated", this);
49091                 this.fireEvent("expanded", this);
49092             },
49093             scope: this,
49094             block: true
49095         });
49096     },
49097
49098     anchors : {
49099         "west" : "left",
49100         "east" : "right",
49101         "north" : "top",
49102         "south" : "bottom"
49103     },
49104
49105     sanchors : {
49106         "west" : "l",
49107         "east" : "r",
49108         "north" : "t",
49109         "south" : "b"
49110     },
49111
49112     canchors : {
49113         "west" : "tl-tr",
49114         "east" : "tr-tl",
49115         "north" : "tl-bl",
49116         "south" : "bl-tl"
49117     },
49118
49119     getAnchor : function(){
49120         return this.anchors[this.position];
49121     },
49122
49123     getCollapseAnchor : function(){
49124         return this.canchors[this.position];
49125     },
49126
49127     getSlideAnchor : function(){
49128         return this.sanchors[this.position];
49129     },
49130
49131     getAlignAdj : function(){
49132         var cm = this.cmargins;
49133         switch(this.position){
49134             case "west":
49135                 return [0, 0];
49136             break;
49137             case "east":
49138                 return [0, 0];
49139             break;
49140             case "north":
49141                 return [0, 0];
49142             break;
49143             case "south":
49144                 return [0, 0];
49145             break;
49146         }
49147     },
49148
49149     getExpandAdj : function(){
49150         var c = this.collapsedEl, cm = this.cmargins;
49151         switch(this.position){
49152             case "west":
49153                 return [-(cm.right+c.getWidth()+cm.left), 0];
49154             break;
49155             case "east":
49156                 return [cm.right+c.getWidth()+cm.left, 0];
49157             break;
49158             case "north":
49159                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49160             break;
49161             case "south":
49162                 return [0, cm.top+cm.bottom+c.getHeight()];
49163             break;
49164         }
49165     }
49166 });/*
49167  * Based on:
49168  * Ext JS Library 1.1.1
49169  * Copyright(c) 2006-2007, Ext JS, LLC.
49170  *
49171  * Originally Released Under LGPL - original licence link has changed is not relivant.
49172  *
49173  * Fork - LGPL
49174  * <script type="text/javascript">
49175  */
49176 /*
49177  * These classes are private internal classes
49178  */
49179 Roo.CenterLayoutRegion = function(mgr, config){
49180     Roo.LayoutRegion.call(this, mgr, config, "center");
49181     this.visible = true;
49182     this.minWidth = config.minWidth || 20;
49183     this.minHeight = config.minHeight || 20;
49184 };
49185
49186 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49187     hide : function(){
49188         // center panel can't be hidden
49189     },
49190     
49191     show : function(){
49192         // center panel can't be hidden
49193     },
49194     
49195     getMinWidth: function(){
49196         return this.minWidth;
49197     },
49198     
49199     getMinHeight: function(){
49200         return this.minHeight;
49201     }
49202 });
49203
49204
49205 Roo.NorthLayoutRegion = function(mgr, config){
49206     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49207     if(this.split){
49208         this.split.placement = Roo.SplitBar.TOP;
49209         this.split.orientation = Roo.SplitBar.VERTICAL;
49210         this.split.el.addClass("x-layout-split-v");
49211     }
49212     var size = config.initialSize || config.height;
49213     if(typeof size != "undefined"){
49214         this.el.setHeight(size);
49215     }
49216 };
49217 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49218     orientation: Roo.SplitBar.VERTICAL,
49219     getBox : function(){
49220         if(this.collapsed){
49221             return this.collapsedEl.getBox();
49222         }
49223         var box = this.el.getBox();
49224         if(this.split){
49225             box.height += this.split.el.getHeight();
49226         }
49227         return box;
49228     },
49229     
49230     updateBox : function(box){
49231         if(this.split && !this.collapsed){
49232             box.height -= this.split.el.getHeight();
49233             this.split.el.setLeft(box.x);
49234             this.split.el.setTop(box.y+box.height);
49235             this.split.el.setWidth(box.width);
49236         }
49237         if(this.collapsed){
49238             this.updateBody(box.width, null);
49239         }
49240         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49241     }
49242 });
49243
49244 Roo.SouthLayoutRegion = function(mgr, config){
49245     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49246     if(this.split){
49247         this.split.placement = Roo.SplitBar.BOTTOM;
49248         this.split.orientation = Roo.SplitBar.VERTICAL;
49249         this.split.el.addClass("x-layout-split-v");
49250     }
49251     var size = config.initialSize || config.height;
49252     if(typeof size != "undefined"){
49253         this.el.setHeight(size);
49254     }
49255 };
49256 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49257     orientation: Roo.SplitBar.VERTICAL,
49258     getBox : function(){
49259         if(this.collapsed){
49260             return this.collapsedEl.getBox();
49261         }
49262         var box = this.el.getBox();
49263         if(this.split){
49264             var sh = this.split.el.getHeight();
49265             box.height += sh;
49266             box.y -= sh;
49267         }
49268         return box;
49269     },
49270     
49271     updateBox : function(box){
49272         if(this.split && !this.collapsed){
49273             var sh = this.split.el.getHeight();
49274             box.height -= sh;
49275             box.y += sh;
49276             this.split.el.setLeft(box.x);
49277             this.split.el.setTop(box.y-sh);
49278             this.split.el.setWidth(box.width);
49279         }
49280         if(this.collapsed){
49281             this.updateBody(box.width, null);
49282         }
49283         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49284     }
49285 });
49286
49287 Roo.EastLayoutRegion = function(mgr, config){
49288     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49289     if(this.split){
49290         this.split.placement = Roo.SplitBar.RIGHT;
49291         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49292         this.split.el.addClass("x-layout-split-h");
49293     }
49294     var size = config.initialSize || config.width;
49295     if(typeof size != "undefined"){
49296         this.el.setWidth(size);
49297     }
49298 };
49299 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49300     orientation: Roo.SplitBar.HORIZONTAL,
49301     getBox : function(){
49302         if(this.collapsed){
49303             return this.collapsedEl.getBox();
49304         }
49305         var box = this.el.getBox();
49306         if(this.split){
49307             var sw = this.split.el.getWidth();
49308             box.width += sw;
49309             box.x -= sw;
49310         }
49311         return box;
49312     },
49313
49314     updateBox : function(box){
49315         if(this.split && !this.collapsed){
49316             var sw = this.split.el.getWidth();
49317             box.width -= sw;
49318             this.split.el.setLeft(box.x);
49319             this.split.el.setTop(box.y);
49320             this.split.el.setHeight(box.height);
49321             box.x += sw;
49322         }
49323         if(this.collapsed){
49324             this.updateBody(null, box.height);
49325         }
49326         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49327     }
49328 });
49329
49330 Roo.WestLayoutRegion = function(mgr, config){
49331     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49332     if(this.split){
49333         this.split.placement = Roo.SplitBar.LEFT;
49334         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49335         this.split.el.addClass("x-layout-split-h");
49336     }
49337     var size = config.initialSize || config.width;
49338     if(typeof size != "undefined"){
49339         this.el.setWidth(size);
49340     }
49341 };
49342 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
49343     orientation: Roo.SplitBar.HORIZONTAL,
49344     getBox : function(){
49345         if(this.collapsed){
49346             return this.collapsedEl.getBox();
49347         }
49348         var box = this.el.getBox();
49349         if(this.split){
49350             box.width += this.split.el.getWidth();
49351         }
49352         return box;
49353     },
49354     
49355     updateBox : function(box){
49356         if(this.split && !this.collapsed){
49357             var sw = this.split.el.getWidth();
49358             box.width -= sw;
49359             this.split.el.setLeft(box.x+box.width);
49360             this.split.el.setTop(box.y);
49361             this.split.el.setHeight(box.height);
49362         }
49363         if(this.collapsed){
49364             this.updateBody(null, box.height);
49365         }
49366         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49367     }
49368 });
49369 /*
49370  * Based on:
49371  * Ext JS Library 1.1.1
49372  * Copyright(c) 2006-2007, Ext JS, LLC.
49373  *
49374  * Originally Released Under LGPL - original licence link has changed is not relivant.
49375  *
49376  * Fork - LGPL
49377  * <script type="text/javascript">
49378  */
49379  
49380  
49381 /*
49382  * Private internal class for reading and applying state
49383  */
49384 Roo.LayoutStateManager = function(layout){
49385      // default empty state
49386      this.state = {
49387         north: {},
49388         south: {},
49389         east: {},
49390         west: {}       
49391     };
49392 };
49393
49394 Roo.LayoutStateManager.prototype = {
49395     init : function(layout, provider){
49396         this.provider = provider;
49397         var state = provider.get(layout.id+"-layout-state");
49398         if(state){
49399             var wasUpdating = layout.isUpdating();
49400             if(!wasUpdating){
49401                 layout.beginUpdate();
49402             }
49403             for(var key in state){
49404                 if(typeof state[key] != "function"){
49405                     var rstate = state[key];
49406                     var r = layout.getRegion(key);
49407                     if(r && rstate){
49408                         if(rstate.size){
49409                             r.resizeTo(rstate.size);
49410                         }
49411                         if(rstate.collapsed == true){
49412                             r.collapse(true);
49413                         }else{
49414                             r.expand(null, true);
49415                         }
49416                     }
49417                 }
49418             }
49419             if(!wasUpdating){
49420                 layout.endUpdate();
49421             }
49422             this.state = state; 
49423         }
49424         this.layout = layout;
49425         layout.on("regionresized", this.onRegionResized, this);
49426         layout.on("regioncollapsed", this.onRegionCollapsed, this);
49427         layout.on("regionexpanded", this.onRegionExpanded, this);
49428     },
49429     
49430     storeState : function(){
49431         this.provider.set(this.layout.id+"-layout-state", this.state);
49432     },
49433     
49434     onRegionResized : function(region, newSize){
49435         this.state[region.getPosition()].size = newSize;
49436         this.storeState();
49437     },
49438     
49439     onRegionCollapsed : function(region){
49440         this.state[region.getPosition()].collapsed = true;
49441         this.storeState();
49442     },
49443     
49444     onRegionExpanded : function(region){
49445         this.state[region.getPosition()].collapsed = false;
49446         this.storeState();
49447     }
49448 };/*
49449  * Based on:
49450  * Ext JS Library 1.1.1
49451  * Copyright(c) 2006-2007, Ext JS, LLC.
49452  *
49453  * Originally Released Under LGPL - original licence link has changed is not relivant.
49454  *
49455  * Fork - LGPL
49456  * <script type="text/javascript">
49457  */
49458 /**
49459  * @class Roo.ContentPanel
49460  * @extends Roo.util.Observable
49461  * A basic ContentPanel element.
49462  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
49463  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
49464  * @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
49465  * @cfg {Boolean}   closable      True if the panel can be closed/removed
49466  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
49467  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
49468  * @cfg {Toolbar}   toolbar       A toolbar for this panel
49469  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
49470  * @cfg {String} title          The title for this panel
49471  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
49472  * @cfg {String} url            Calls {@link #setUrl} with this value
49473  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
49474  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
49475  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
49476  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
49477
49478  * @constructor
49479  * Create a new ContentPanel.
49480  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
49481  * @param {String/Object} config A string to set only the title or a config object
49482  * @param {String} content (optional) Set the HTML content for this panel
49483  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
49484  */
49485 Roo.ContentPanel = function(el, config, content){
49486     
49487      
49488     /*
49489     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
49490         config = el;
49491         el = Roo.id();
49492     }
49493     if (config && config.parentLayout) { 
49494         el = config.parentLayout.el.createChild(); 
49495     }
49496     */
49497     if(el.autoCreate){ // xtype is available if this is called from factory
49498         config = el;
49499         el = Roo.id();
49500     }
49501     this.el = Roo.get(el);
49502     if(!this.el && config && config.autoCreate){
49503         if(typeof config.autoCreate == "object"){
49504             if(!config.autoCreate.id){
49505                 config.autoCreate.id = config.id||el;
49506             }
49507             this.el = Roo.DomHelper.append(document.body,
49508                         config.autoCreate, true);
49509         }else{
49510             this.el = Roo.DomHelper.append(document.body,
49511                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
49512         }
49513     }
49514     this.closable = false;
49515     this.loaded = false;
49516     this.active = false;
49517     if(typeof config == "string"){
49518         this.title = config;
49519     }else{
49520         Roo.apply(this, config);
49521     }
49522     
49523     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
49524         this.wrapEl = this.el.wrap();
49525         this.toolbar.container = this.el.insertSibling(false, 'before');
49526         this.toolbar = new Roo.Toolbar(this.toolbar);
49527     }
49528     
49529     // xtype created footer. - not sure if will work as we normally have to render first..
49530     if (this.footer && !this.footer.el && this.footer.xtype) {
49531         if (!this.wrapEl) {
49532             this.wrapEl = this.el.wrap();
49533         }
49534     
49535         this.footer.container = this.wrapEl.createChild();
49536          
49537         this.footer = Roo.factory(this.footer, Roo);
49538         
49539     }
49540     
49541     if(this.resizeEl){
49542         this.resizeEl = Roo.get(this.resizeEl, true);
49543     }else{
49544         this.resizeEl = this.el;
49545     }
49546     // handle view.xtype
49547     
49548  
49549     
49550     
49551     this.addEvents({
49552         /**
49553          * @event activate
49554          * Fires when this panel is activated. 
49555          * @param {Roo.ContentPanel} this
49556          */
49557         "activate" : true,
49558         /**
49559          * @event deactivate
49560          * Fires when this panel is activated. 
49561          * @param {Roo.ContentPanel} this
49562          */
49563         "deactivate" : true,
49564
49565         /**
49566          * @event resize
49567          * Fires when this panel is resized if fitToFrame is true.
49568          * @param {Roo.ContentPanel} this
49569          * @param {Number} width The width after any component adjustments
49570          * @param {Number} height The height after any component adjustments
49571          */
49572         "resize" : true,
49573         
49574          /**
49575          * @event render
49576          * Fires when this tab is created
49577          * @param {Roo.ContentPanel} this
49578          */
49579         "render" : true
49580         
49581         
49582         
49583     });
49584     
49585
49586     
49587     
49588     if(this.autoScroll){
49589         this.resizeEl.setStyle("overflow", "auto");
49590     } else {
49591         // fix randome scrolling
49592         this.el.on('scroll', function() {
49593             Roo.log('fix random scolling');
49594             this.scrollTo('top',0); 
49595         });
49596     }
49597     content = content || this.content;
49598     if(content){
49599         this.setContent(content);
49600     }
49601     if(config && config.url){
49602         this.setUrl(this.url, this.params, this.loadOnce);
49603     }
49604     
49605     
49606     
49607     Roo.ContentPanel.superclass.constructor.call(this);
49608     
49609     if (this.view && typeof(this.view.xtype) != 'undefined') {
49610         this.view.el = this.el.appendChild(document.createElement("div"));
49611         this.view = Roo.factory(this.view); 
49612         this.view.render  &&  this.view.render(false, '');  
49613     }
49614     
49615     
49616     this.fireEvent('render', this);
49617 };
49618
49619 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
49620     tabTip:'',
49621     setRegion : function(region){
49622         this.region = region;
49623         if(region){
49624            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
49625         }else{
49626            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
49627         } 
49628     },
49629     
49630     /**
49631      * Returns the toolbar for this Panel if one was configured. 
49632      * @return {Roo.Toolbar} 
49633      */
49634     getToolbar : function(){
49635         return this.toolbar;
49636     },
49637     
49638     setActiveState : function(active){
49639         this.active = active;
49640         if(!active){
49641             this.fireEvent("deactivate", this);
49642         }else{
49643             this.fireEvent("activate", this);
49644         }
49645     },
49646     /**
49647      * Updates this panel's element
49648      * @param {String} content The new content
49649      * @param {Boolean} loadScripts (optional) true to look for and process scripts
49650     */
49651     setContent : function(content, loadScripts){
49652         this.el.update(content, loadScripts);
49653     },
49654
49655     ignoreResize : function(w, h){
49656         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
49657             return true;
49658         }else{
49659             this.lastSize = {width: w, height: h};
49660             return false;
49661         }
49662     },
49663     /**
49664      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
49665      * @return {Roo.UpdateManager} The UpdateManager
49666      */
49667     getUpdateManager : function(){
49668         return this.el.getUpdateManager();
49669     },
49670      /**
49671      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
49672      * @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:
49673 <pre><code>
49674 panel.load({
49675     url: "your-url.php",
49676     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
49677     callback: yourFunction,
49678     scope: yourObject, //(optional scope)
49679     discardUrl: false,
49680     nocache: false,
49681     text: "Loading...",
49682     timeout: 30,
49683     scripts: false
49684 });
49685 </code></pre>
49686      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
49687      * 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.
49688      * @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}
49689      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
49690      * @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.
49691      * @return {Roo.ContentPanel} this
49692      */
49693     load : function(){
49694         var um = this.el.getUpdateManager();
49695         um.update.apply(um, arguments);
49696         return this;
49697     },
49698
49699
49700     /**
49701      * 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.
49702      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
49703      * @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)
49704      * @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)
49705      * @return {Roo.UpdateManager} The UpdateManager
49706      */
49707     setUrl : function(url, params, loadOnce){
49708         if(this.refreshDelegate){
49709             this.removeListener("activate", this.refreshDelegate);
49710         }
49711         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
49712         this.on("activate", this.refreshDelegate);
49713         return this.el.getUpdateManager();
49714     },
49715     
49716     _handleRefresh : function(url, params, loadOnce){
49717         if(!loadOnce || !this.loaded){
49718             var updater = this.el.getUpdateManager();
49719             updater.update(url, params, this._setLoaded.createDelegate(this));
49720         }
49721     },
49722     
49723     _setLoaded : function(){
49724         this.loaded = true;
49725     }, 
49726     
49727     /**
49728      * Returns this panel's id
49729      * @return {String} 
49730      */
49731     getId : function(){
49732         return this.el.id;
49733     },
49734     
49735     /** 
49736      * Returns this panel's element - used by regiosn to add.
49737      * @return {Roo.Element} 
49738      */
49739     getEl : function(){
49740         return this.wrapEl || this.el;
49741     },
49742     
49743     adjustForComponents : function(width, height)
49744     {
49745         //Roo.log('adjustForComponents ');
49746         if(this.resizeEl != this.el){
49747             width -= this.el.getFrameWidth('lr');
49748             height -= this.el.getFrameWidth('tb');
49749         }
49750         if(this.toolbar){
49751             var te = this.toolbar.getEl();
49752             height -= te.getHeight();
49753             te.setWidth(width);
49754         }
49755         if(this.footer){
49756             var te = this.footer.getEl();
49757             Roo.log("footer:" + te.getHeight());
49758             
49759             height -= te.getHeight();
49760             te.setWidth(width);
49761         }
49762         
49763         
49764         if(this.adjustments){
49765             width += this.adjustments[0];
49766             height += this.adjustments[1];
49767         }
49768         return {"width": width, "height": height};
49769     },
49770     
49771     setSize : function(width, height){
49772         if(this.fitToFrame && !this.ignoreResize(width, height)){
49773             if(this.fitContainer && this.resizeEl != this.el){
49774                 this.el.setSize(width, height);
49775             }
49776             var size = this.adjustForComponents(width, height);
49777             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
49778             this.fireEvent('resize', this, size.width, size.height);
49779         }
49780     },
49781     
49782     /**
49783      * Returns this panel's title
49784      * @return {String} 
49785      */
49786     getTitle : function(){
49787         return this.title;
49788     },
49789     
49790     /**
49791      * Set this panel's title
49792      * @param {String} title
49793      */
49794     setTitle : function(title){
49795         this.title = title;
49796         if(this.region){
49797             this.region.updatePanelTitle(this, title);
49798         }
49799     },
49800     
49801     /**
49802      * Returns true is this panel was configured to be closable
49803      * @return {Boolean} 
49804      */
49805     isClosable : function(){
49806         return this.closable;
49807     },
49808     
49809     beforeSlide : function(){
49810         this.el.clip();
49811         this.resizeEl.clip();
49812     },
49813     
49814     afterSlide : function(){
49815         this.el.unclip();
49816         this.resizeEl.unclip();
49817     },
49818     
49819     /**
49820      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
49821      *   Will fail silently if the {@link #setUrl} method has not been called.
49822      *   This does not activate the panel, just updates its content.
49823      */
49824     refresh : function(){
49825         if(this.refreshDelegate){
49826            this.loaded = false;
49827            this.refreshDelegate();
49828         }
49829     },
49830     
49831     /**
49832      * Destroys this panel
49833      */
49834     destroy : function(){
49835         this.el.removeAllListeners();
49836         var tempEl = document.createElement("span");
49837         tempEl.appendChild(this.el.dom);
49838         tempEl.innerHTML = "";
49839         this.el.remove();
49840         this.el = null;
49841     },
49842     
49843     /**
49844      * form - if the content panel contains a form - this is a reference to it.
49845      * @type {Roo.form.Form}
49846      */
49847     form : false,
49848     /**
49849      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
49850      *    This contains a reference to it.
49851      * @type {Roo.View}
49852      */
49853     view : false,
49854     
49855       /**
49856      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
49857      * <pre><code>
49858
49859 layout.addxtype({
49860        xtype : 'Form',
49861        items: [ .... ]
49862    }
49863 );
49864
49865 </code></pre>
49866      * @param {Object} cfg Xtype definition of item to add.
49867      */
49868     
49869     addxtype : function(cfg) {
49870         // add form..
49871         if (cfg.xtype.match(/^Form$/)) {
49872             
49873             var el;
49874             //if (this.footer) {
49875             //    el = this.footer.container.insertSibling(false, 'before');
49876             //} else {
49877                 el = this.el.createChild();
49878             //}
49879
49880             this.form = new  Roo.form.Form(cfg);
49881             
49882             
49883             if ( this.form.allItems.length) this.form.render(el.dom);
49884             return this.form;
49885         }
49886         // should only have one of theses..
49887         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
49888             // views.. should not be just added - used named prop 'view''
49889             
49890             cfg.el = this.el.appendChild(document.createElement("div"));
49891             // factory?
49892             
49893             var ret = new Roo.factory(cfg);
49894              
49895              ret.render && ret.render(false, ''); // render blank..
49896             this.view = ret;
49897             return ret;
49898         }
49899         return false;
49900     }
49901 });
49902
49903 /**
49904  * @class Roo.GridPanel
49905  * @extends Roo.ContentPanel
49906  * @constructor
49907  * Create a new GridPanel.
49908  * @param {Roo.grid.Grid} grid The grid for this panel
49909  * @param {String/Object} config A string to set only the panel's title, or a config object
49910  */
49911 Roo.GridPanel = function(grid, config){
49912     
49913   
49914     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
49915         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
49916         
49917     this.wrapper.dom.appendChild(grid.getGridEl().dom);
49918     
49919     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
49920     
49921     if(this.toolbar){
49922         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
49923     }
49924     // xtype created footer. - not sure if will work as we normally have to render first..
49925     if (this.footer && !this.footer.el && this.footer.xtype) {
49926         
49927         this.footer.container = this.grid.getView().getFooterPanel(true);
49928         this.footer.dataSource = this.grid.dataSource;
49929         this.footer = Roo.factory(this.footer, Roo);
49930         
49931     }
49932     
49933     grid.monitorWindowResize = false; // turn off autosizing
49934     grid.autoHeight = false;
49935     grid.autoWidth = false;
49936     this.grid = grid;
49937     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
49938 };
49939
49940 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
49941     getId : function(){
49942         return this.grid.id;
49943     },
49944     
49945     /**
49946      * Returns the grid for this panel
49947      * @return {Roo.grid.Grid} 
49948      */
49949     getGrid : function(){
49950         return this.grid;    
49951     },
49952     
49953     setSize : function(width, height){
49954         if(!this.ignoreResize(width, height)){
49955             var grid = this.grid;
49956             var size = this.adjustForComponents(width, height);
49957             grid.getGridEl().setSize(size.width, size.height);
49958             grid.autoSize();
49959         }
49960     },
49961     
49962     beforeSlide : function(){
49963         this.grid.getView().scroller.clip();
49964     },
49965     
49966     afterSlide : function(){
49967         this.grid.getView().scroller.unclip();
49968     },
49969     
49970     destroy : function(){
49971         this.grid.destroy();
49972         delete this.grid;
49973         Roo.GridPanel.superclass.destroy.call(this); 
49974     }
49975 });
49976
49977
49978 /**
49979  * @class Roo.NestedLayoutPanel
49980  * @extends Roo.ContentPanel
49981  * @constructor
49982  * Create a new NestedLayoutPanel.
49983  * 
49984  * 
49985  * @param {Roo.BorderLayout} layout The layout for this panel
49986  * @param {String/Object} config A string to set only the title or a config object
49987  */
49988 Roo.NestedLayoutPanel = function(layout, config)
49989 {
49990     // construct with only one argument..
49991     /* FIXME - implement nicer consturctors
49992     if (layout.layout) {
49993         config = layout;
49994         layout = config.layout;
49995         delete config.layout;
49996     }
49997     if (layout.xtype && !layout.getEl) {
49998         // then layout needs constructing..
49999         layout = Roo.factory(layout, Roo);
50000     }
50001     */
50002     
50003     
50004     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50005     
50006     layout.monitorWindowResize = false; // turn off autosizing
50007     this.layout = layout;
50008     this.layout.getEl().addClass("x-layout-nested-layout");
50009     
50010     
50011     
50012     
50013 };
50014
50015 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
50016
50017     setSize : function(width, height){
50018         if(!this.ignoreResize(width, height)){
50019             var size = this.adjustForComponents(width, height);
50020             var el = this.layout.getEl();
50021             el.setSize(size.width, size.height);
50022             var touch = el.dom.offsetWidth;
50023             this.layout.layout();
50024             // ie requires a double layout on the first pass
50025             if(Roo.isIE && !this.initialized){
50026                 this.initialized = true;
50027                 this.layout.layout();
50028             }
50029         }
50030     },
50031     
50032     // activate all subpanels if not currently active..
50033     
50034     setActiveState : function(active){
50035         this.active = active;
50036         if(!active){
50037             this.fireEvent("deactivate", this);
50038             return;
50039         }
50040         
50041         this.fireEvent("activate", this);
50042         // not sure if this should happen before or after..
50043         if (!this.layout) {
50044             return; // should not happen..
50045         }
50046         var reg = false;
50047         for (var r in this.layout.regions) {
50048             reg = this.layout.getRegion(r);
50049             if (reg.getActivePanel()) {
50050                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
50051                 reg.setActivePanel(reg.getActivePanel());
50052                 continue;
50053             }
50054             if (!reg.panels.length) {
50055                 continue;
50056             }
50057             reg.showPanel(reg.getPanel(0));
50058         }
50059         
50060         
50061         
50062         
50063     },
50064     
50065     /**
50066      * Returns the nested BorderLayout for this panel
50067      * @return {Roo.BorderLayout} 
50068      */
50069     getLayout : function(){
50070         return this.layout;
50071     },
50072     
50073      /**
50074      * Adds a xtype elements to the layout of the nested panel
50075      * <pre><code>
50076
50077 panel.addxtype({
50078        xtype : 'ContentPanel',
50079        region: 'west',
50080        items: [ .... ]
50081    }
50082 );
50083
50084 panel.addxtype({
50085         xtype : 'NestedLayoutPanel',
50086         region: 'west',
50087         layout: {
50088            center: { },
50089            west: { }   
50090         },
50091         items : [ ... list of content panels or nested layout panels.. ]
50092    }
50093 );
50094 </code></pre>
50095      * @param {Object} cfg Xtype definition of item to add.
50096      */
50097     addxtype : function(cfg) {
50098         return this.layout.addxtype(cfg);
50099     
50100     }
50101 });
50102
50103 Roo.ScrollPanel = function(el, config, content){
50104     config = config || {};
50105     config.fitToFrame = true;
50106     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50107     
50108     this.el.dom.style.overflow = "hidden";
50109     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50110     this.el.removeClass("x-layout-inactive-content");
50111     this.el.on("mousewheel", this.onWheel, this);
50112
50113     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50114     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50115     up.unselectable(); down.unselectable();
50116     up.on("click", this.scrollUp, this);
50117     down.on("click", this.scrollDown, this);
50118     up.addClassOnOver("x-scroller-btn-over");
50119     down.addClassOnOver("x-scroller-btn-over");
50120     up.addClassOnClick("x-scroller-btn-click");
50121     down.addClassOnClick("x-scroller-btn-click");
50122     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50123
50124     this.resizeEl = this.el;
50125     this.el = wrap; this.up = up; this.down = down;
50126 };
50127
50128 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50129     increment : 100,
50130     wheelIncrement : 5,
50131     scrollUp : function(){
50132         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50133     },
50134
50135     scrollDown : function(){
50136         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50137     },
50138
50139     afterScroll : function(){
50140         var el = this.resizeEl;
50141         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50142         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50143         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50144     },
50145
50146     setSize : function(){
50147         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50148         this.afterScroll();
50149     },
50150
50151     onWheel : function(e){
50152         var d = e.getWheelDelta();
50153         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50154         this.afterScroll();
50155         e.stopEvent();
50156     },
50157
50158     setContent : function(content, loadScripts){
50159         this.resizeEl.update(content, loadScripts);
50160     }
50161
50162 });
50163
50164
50165
50166
50167
50168
50169
50170
50171
50172 /**
50173  * @class Roo.TreePanel
50174  * @extends Roo.ContentPanel
50175  * @constructor
50176  * Create a new TreePanel. - defaults to fit/scoll contents.
50177  * @param {String/Object} config A string to set only the panel's title, or a config object
50178  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50179  */
50180 Roo.TreePanel = function(config){
50181     var el = config.el;
50182     var tree = config.tree;
50183     delete config.tree; 
50184     delete config.el; // hopefull!
50185     
50186     // wrapper for IE7 strict & safari scroll issue
50187     
50188     var treeEl = el.createChild();
50189     config.resizeEl = treeEl;
50190     
50191     
50192     
50193     Roo.TreePanel.superclass.constructor.call(this, el, config);
50194  
50195  
50196     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50197     //console.log(tree);
50198     this.on('activate', function()
50199     {
50200         if (this.tree.rendered) {
50201             return;
50202         }
50203         //console.log('render tree');
50204         this.tree.render();
50205     });
50206     // this should not be needed.. - it's actually the 'el' that resizes?
50207     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50208     
50209     //this.on('resize',  function (cp, w, h) {
50210     //        this.tree.innerCt.setWidth(w);
50211     //        this.tree.innerCt.setHeight(h);
50212     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50213     //});
50214
50215         
50216     
50217 };
50218
50219 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50220     fitToFrame : true,
50221     autoScroll : true
50222 });
50223
50224
50225
50226
50227
50228
50229
50230
50231
50232
50233
50234 /*
50235  * Based on:
50236  * Ext JS Library 1.1.1
50237  * Copyright(c) 2006-2007, Ext JS, LLC.
50238  *
50239  * Originally Released Under LGPL - original licence link has changed is not relivant.
50240  *
50241  * Fork - LGPL
50242  * <script type="text/javascript">
50243  */
50244  
50245
50246 /**
50247  * @class Roo.ReaderLayout
50248  * @extends Roo.BorderLayout
50249  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50250  * center region containing two nested regions (a top one for a list view and one for item preview below),
50251  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50252  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50253  * expedites the setup of the overall layout and regions for this common application style.
50254  * Example:
50255  <pre><code>
50256 var reader = new Roo.ReaderLayout();
50257 var CP = Roo.ContentPanel;  // shortcut for adding
50258
50259 reader.beginUpdate();
50260 reader.add("north", new CP("north", "North"));
50261 reader.add("west", new CP("west", {title: "West"}));
50262 reader.add("east", new CP("east", {title: "East"}));
50263
50264 reader.regions.listView.add(new CP("listView", "List"));
50265 reader.regions.preview.add(new CP("preview", "Preview"));
50266 reader.endUpdate();
50267 </code></pre>
50268 * @constructor
50269 * Create a new ReaderLayout
50270 * @param {Object} config Configuration options
50271 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50272 * document.body if omitted)
50273 */
50274 Roo.ReaderLayout = function(config, renderTo){
50275     var c = config || {size:{}};
50276     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50277         north: c.north !== false ? Roo.apply({
50278             split:false,
50279             initialSize: 32,
50280             titlebar: false
50281         }, c.north) : false,
50282         west: c.west !== false ? Roo.apply({
50283             split:true,
50284             initialSize: 200,
50285             minSize: 175,
50286             maxSize: 400,
50287             titlebar: true,
50288             collapsible: true,
50289             animate: true,
50290             margins:{left:5,right:0,bottom:5,top:5},
50291             cmargins:{left:5,right:5,bottom:5,top:5}
50292         }, c.west) : false,
50293         east: c.east !== false ? Roo.apply({
50294             split:true,
50295             initialSize: 200,
50296             minSize: 175,
50297             maxSize: 400,
50298             titlebar: true,
50299             collapsible: true,
50300             animate: true,
50301             margins:{left:0,right:5,bottom:5,top:5},
50302             cmargins:{left:5,right:5,bottom:5,top:5}
50303         }, c.east) : false,
50304         center: Roo.apply({
50305             tabPosition: 'top',
50306             autoScroll:false,
50307             closeOnTab: true,
50308             titlebar:false,
50309             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50310         }, c.center)
50311     });
50312
50313     this.el.addClass('x-reader');
50314
50315     this.beginUpdate();
50316
50317     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50318         south: c.preview !== false ? Roo.apply({
50319             split:true,
50320             initialSize: 200,
50321             minSize: 100,
50322             autoScroll:true,
50323             collapsible:true,
50324             titlebar: true,
50325             cmargins:{top:5,left:0, right:0, bottom:0}
50326         }, c.preview) : false,
50327         center: Roo.apply({
50328             autoScroll:false,
50329             titlebar:false,
50330             minHeight:200
50331         }, c.listView)
50332     });
50333     this.add('center', new Roo.NestedLayoutPanel(inner,
50334             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50335
50336     this.endUpdate();
50337
50338     this.regions.preview = inner.getRegion('south');
50339     this.regions.listView = inner.getRegion('center');
50340 };
50341
50342 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
50343  * Based on:
50344  * Ext JS Library 1.1.1
50345  * Copyright(c) 2006-2007, Ext JS, LLC.
50346  *
50347  * Originally Released Under LGPL - original licence link has changed is not relivant.
50348  *
50349  * Fork - LGPL
50350  * <script type="text/javascript">
50351  */
50352  
50353 /**
50354  * @class Roo.grid.Grid
50355  * @extends Roo.util.Observable
50356  * This class represents the primary interface of a component based grid control.
50357  * <br><br>Usage:<pre><code>
50358  var grid = new Roo.grid.Grid("my-container-id", {
50359      ds: myDataStore,
50360      cm: myColModel,
50361      selModel: mySelectionModel,
50362      autoSizeColumns: true,
50363      monitorWindowResize: false,
50364      trackMouseOver: true
50365  });
50366  // set any options
50367  grid.render();
50368  * </code></pre>
50369  * <b>Common Problems:</b><br/>
50370  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
50371  * element will correct this<br/>
50372  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
50373  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
50374  * are unpredictable.<br/>
50375  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
50376  * grid to calculate dimensions/offsets.<br/>
50377   * @constructor
50378  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50379  * The container MUST have some type of size defined for the grid to fill. The container will be
50380  * automatically set to position relative if it isn't already.
50381  * @param {Object} config A config object that sets properties on this grid.
50382  */
50383 Roo.grid.Grid = function(container, config){
50384         // initialize the container
50385         this.container = Roo.get(container);
50386         this.container.update("");
50387         this.container.setStyle("overflow", "hidden");
50388     this.container.addClass('x-grid-container');
50389
50390     this.id = this.container.id;
50391
50392     Roo.apply(this, config);
50393     // check and correct shorthanded configs
50394     if(this.ds){
50395         this.dataSource = this.ds;
50396         delete this.ds;
50397     }
50398     if(this.cm){
50399         this.colModel = this.cm;
50400         delete this.cm;
50401     }
50402     if(this.sm){
50403         this.selModel = this.sm;
50404         delete this.sm;
50405     }
50406
50407     if (this.selModel) {
50408         this.selModel = Roo.factory(this.selModel, Roo.grid);
50409         this.sm = this.selModel;
50410         this.sm.xmodule = this.xmodule || false;
50411     }
50412     if (typeof(this.colModel.config) == 'undefined') {
50413         this.colModel = new Roo.grid.ColumnModel(this.colModel);
50414         this.cm = this.colModel;
50415         this.cm.xmodule = this.xmodule || false;
50416     }
50417     if (this.dataSource) {
50418         this.dataSource= Roo.factory(this.dataSource, Roo.data);
50419         this.ds = this.dataSource;
50420         this.ds.xmodule = this.xmodule || false;
50421          
50422     }
50423     
50424     
50425     
50426     if(this.width){
50427         this.container.setWidth(this.width);
50428     }
50429
50430     if(this.height){
50431         this.container.setHeight(this.height);
50432     }
50433     /** @private */
50434         this.addEvents({
50435         // raw events
50436         /**
50437          * @event click
50438          * The raw click event for the entire grid.
50439          * @param {Roo.EventObject} e
50440          */
50441         "click" : true,
50442         /**
50443          * @event dblclick
50444          * The raw dblclick event for the entire grid.
50445          * @param {Roo.EventObject} e
50446          */
50447         "dblclick" : true,
50448         /**
50449          * @event contextmenu
50450          * The raw contextmenu event for the entire grid.
50451          * @param {Roo.EventObject} e
50452          */
50453         "contextmenu" : true,
50454         /**
50455          * @event mousedown
50456          * The raw mousedown event for the entire grid.
50457          * @param {Roo.EventObject} e
50458          */
50459         "mousedown" : true,
50460         /**
50461          * @event mouseup
50462          * The raw mouseup event for the entire grid.
50463          * @param {Roo.EventObject} e
50464          */
50465         "mouseup" : true,
50466         /**
50467          * @event mouseover
50468          * The raw mouseover event for the entire grid.
50469          * @param {Roo.EventObject} e
50470          */
50471         "mouseover" : true,
50472         /**
50473          * @event mouseout
50474          * The raw mouseout event for the entire grid.
50475          * @param {Roo.EventObject} e
50476          */
50477         "mouseout" : true,
50478         /**
50479          * @event keypress
50480          * The raw keypress event for the entire grid.
50481          * @param {Roo.EventObject} e
50482          */
50483         "keypress" : true,
50484         /**
50485          * @event keydown
50486          * The raw keydown event for the entire grid.
50487          * @param {Roo.EventObject} e
50488          */
50489         "keydown" : true,
50490
50491         // custom events
50492
50493         /**
50494          * @event cellclick
50495          * Fires when a cell is clicked
50496          * @param {Grid} this
50497          * @param {Number} rowIndex
50498          * @param {Number} columnIndex
50499          * @param {Roo.EventObject} e
50500          */
50501         "cellclick" : true,
50502         /**
50503          * @event celldblclick
50504          * Fires when a cell is double clicked
50505          * @param {Grid} this
50506          * @param {Number} rowIndex
50507          * @param {Number} columnIndex
50508          * @param {Roo.EventObject} e
50509          */
50510         "celldblclick" : true,
50511         /**
50512          * @event rowclick
50513          * Fires when a row is clicked
50514          * @param {Grid} this
50515          * @param {Number} rowIndex
50516          * @param {Roo.EventObject} e
50517          */
50518         "rowclick" : true,
50519         /**
50520          * @event rowdblclick
50521          * Fires when a row is double clicked
50522          * @param {Grid} this
50523          * @param {Number} rowIndex
50524          * @param {Roo.EventObject} e
50525          */
50526         "rowdblclick" : true,
50527         /**
50528          * @event headerclick
50529          * Fires when a header is clicked
50530          * @param {Grid} this
50531          * @param {Number} columnIndex
50532          * @param {Roo.EventObject} e
50533          */
50534         "headerclick" : true,
50535         /**
50536          * @event headerdblclick
50537          * Fires when a header cell is double clicked
50538          * @param {Grid} this
50539          * @param {Number} columnIndex
50540          * @param {Roo.EventObject} e
50541          */
50542         "headerdblclick" : true,
50543         /**
50544          * @event rowcontextmenu
50545          * Fires when a row is right clicked
50546          * @param {Grid} this
50547          * @param {Number} rowIndex
50548          * @param {Roo.EventObject} e
50549          */
50550         "rowcontextmenu" : true,
50551         /**
50552          * @event cellcontextmenu
50553          * Fires when a cell is right clicked
50554          * @param {Grid} this
50555          * @param {Number} rowIndex
50556          * @param {Number} cellIndex
50557          * @param {Roo.EventObject} e
50558          */
50559          "cellcontextmenu" : true,
50560         /**
50561          * @event headercontextmenu
50562          * Fires when a header is right clicked
50563          * @param {Grid} this
50564          * @param {Number} columnIndex
50565          * @param {Roo.EventObject} e
50566          */
50567         "headercontextmenu" : true,
50568         /**
50569          * @event bodyscroll
50570          * Fires when the body element is scrolled
50571          * @param {Number} scrollLeft
50572          * @param {Number} scrollTop
50573          */
50574         "bodyscroll" : true,
50575         /**
50576          * @event columnresize
50577          * Fires when the user resizes a column
50578          * @param {Number} columnIndex
50579          * @param {Number} newSize
50580          */
50581         "columnresize" : true,
50582         /**
50583          * @event columnmove
50584          * Fires when the user moves a column
50585          * @param {Number} oldIndex
50586          * @param {Number} newIndex
50587          */
50588         "columnmove" : true,
50589         /**
50590          * @event startdrag
50591          * Fires when row(s) start being dragged
50592          * @param {Grid} this
50593          * @param {Roo.GridDD} dd The drag drop object
50594          * @param {event} e The raw browser event
50595          */
50596         "startdrag" : true,
50597         /**
50598          * @event enddrag
50599          * Fires when a drag operation is complete
50600          * @param {Grid} this
50601          * @param {Roo.GridDD} dd The drag drop object
50602          * @param {event} e The raw browser event
50603          */
50604         "enddrag" : true,
50605         /**
50606          * @event dragdrop
50607          * Fires when dragged row(s) are dropped on a valid DD target
50608          * @param {Grid} this
50609          * @param {Roo.GridDD} dd The drag drop object
50610          * @param {String} targetId The target drag drop object
50611          * @param {event} e The raw browser event
50612          */
50613         "dragdrop" : true,
50614         /**
50615          * @event dragover
50616          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
50617          * @param {Grid} this
50618          * @param {Roo.GridDD} dd The drag drop object
50619          * @param {String} targetId The target drag drop object
50620          * @param {event} e The raw browser event
50621          */
50622         "dragover" : true,
50623         /**
50624          * @event dragenter
50625          *  Fires when the dragged row(s) first cross another DD target while being dragged
50626          * @param {Grid} this
50627          * @param {Roo.GridDD} dd The drag drop object
50628          * @param {String} targetId The target drag drop object
50629          * @param {event} e The raw browser event
50630          */
50631         "dragenter" : true,
50632         /**
50633          * @event dragout
50634          * Fires when the dragged row(s) leave another DD target while being dragged
50635          * @param {Grid} this
50636          * @param {Roo.GridDD} dd The drag drop object
50637          * @param {String} targetId The target drag drop object
50638          * @param {event} e The raw browser event
50639          */
50640         "dragout" : true,
50641         /**
50642          * @event rowclass
50643          * Fires when a row is rendered, so you can change add a style to it.
50644          * @param {GridView} gridview   The grid view
50645          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
50646          */
50647         'rowclass' : true,
50648
50649         /**
50650          * @event render
50651          * Fires when the grid is rendered
50652          * @param {Grid} grid
50653          */
50654         'render' : true
50655     });
50656
50657     Roo.grid.Grid.superclass.constructor.call(this);
50658 };
50659 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
50660     
50661     /**
50662      * @cfg {String} ddGroup - drag drop group.
50663      */
50664
50665     /**
50666      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
50667      */
50668     minColumnWidth : 25,
50669
50670     /**
50671      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
50672      * <b>on initial render.</b> It is more efficient to explicitly size the columns
50673      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
50674      */
50675     autoSizeColumns : false,
50676
50677     /**
50678      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
50679      */
50680     autoSizeHeaders : true,
50681
50682     /**
50683      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
50684      */
50685     monitorWindowResize : true,
50686
50687     /**
50688      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
50689      * rows measured to get a columns size. Default is 0 (all rows).
50690      */
50691     maxRowsToMeasure : 0,
50692
50693     /**
50694      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
50695      */
50696     trackMouseOver : true,
50697
50698     /**
50699     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
50700     */
50701     
50702     /**
50703     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
50704     */
50705     enableDragDrop : false,
50706     
50707     /**
50708     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
50709     */
50710     enableColumnMove : true,
50711     
50712     /**
50713     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
50714     */
50715     enableColumnHide : true,
50716     
50717     /**
50718     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
50719     */
50720     enableRowHeightSync : false,
50721     
50722     /**
50723     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
50724     */
50725     stripeRows : true,
50726     
50727     /**
50728     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
50729     */
50730     autoHeight : false,
50731
50732     /**
50733      * @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.
50734      */
50735     autoExpandColumn : false,
50736
50737     /**
50738     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
50739     * Default is 50.
50740     */
50741     autoExpandMin : 50,
50742
50743     /**
50744     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
50745     */
50746     autoExpandMax : 1000,
50747
50748     /**
50749     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
50750     */
50751     view : null,
50752
50753     /**
50754     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
50755     */
50756     loadMask : false,
50757     /**
50758     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
50759     */
50760     dropTarget: false,
50761     
50762    
50763     
50764     // private
50765     rendered : false,
50766
50767     /**
50768     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
50769     * of a fixed width. Default is false.
50770     */
50771     /**
50772     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
50773     */
50774     /**
50775      * Called once after all setup has been completed and the grid is ready to be rendered.
50776      * @return {Roo.grid.Grid} this
50777      */
50778     render : function()
50779     {
50780         var c = this.container;
50781         // try to detect autoHeight/width mode
50782         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
50783             this.autoHeight = true;
50784         }
50785         var view = this.getView();
50786         view.init(this);
50787
50788         c.on("click", this.onClick, this);
50789         c.on("dblclick", this.onDblClick, this);
50790         c.on("contextmenu", this.onContextMenu, this);
50791         c.on("keydown", this.onKeyDown, this);
50792         if (Roo.isTouch) {
50793             c.on("touchstart", this.onTouchStart, this);
50794         }
50795
50796         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
50797
50798         this.getSelectionModel().init(this);
50799
50800         view.render();
50801
50802         if(this.loadMask){
50803             this.loadMask = new Roo.LoadMask(this.container,
50804                     Roo.apply({store:this.dataSource}, this.loadMask));
50805         }
50806         
50807         
50808         if (this.toolbar && this.toolbar.xtype) {
50809             this.toolbar.container = this.getView().getHeaderPanel(true);
50810             this.toolbar = new Roo.Toolbar(this.toolbar);
50811         }
50812         if (this.footer && this.footer.xtype) {
50813             this.footer.dataSource = this.getDataSource();
50814             this.footer.container = this.getView().getFooterPanel(true);
50815             this.footer = Roo.factory(this.footer, Roo);
50816         }
50817         if (this.dropTarget && this.dropTarget.xtype) {
50818             delete this.dropTarget.xtype;
50819             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
50820         }
50821         
50822         
50823         this.rendered = true;
50824         this.fireEvent('render', this);
50825         return this;
50826     },
50827
50828         /**
50829          * Reconfigures the grid to use a different Store and Column Model.
50830          * The View will be bound to the new objects and refreshed.
50831          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
50832          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
50833          */
50834     reconfigure : function(dataSource, colModel){
50835         if(this.loadMask){
50836             this.loadMask.destroy();
50837             this.loadMask = new Roo.LoadMask(this.container,
50838                     Roo.apply({store:dataSource}, this.loadMask));
50839         }
50840         this.view.bind(dataSource, colModel);
50841         this.dataSource = dataSource;
50842         this.colModel = colModel;
50843         this.view.refresh(true);
50844     },
50845
50846     // private
50847     onKeyDown : function(e){
50848         this.fireEvent("keydown", e);
50849     },
50850
50851     /**
50852      * Destroy this grid.
50853      * @param {Boolean} removeEl True to remove the element
50854      */
50855     destroy : function(removeEl, keepListeners){
50856         if(this.loadMask){
50857             this.loadMask.destroy();
50858         }
50859         var c = this.container;
50860         c.removeAllListeners();
50861         this.view.destroy();
50862         this.colModel.purgeListeners();
50863         if(!keepListeners){
50864             this.purgeListeners();
50865         }
50866         c.update("");
50867         if(removeEl === true){
50868             c.remove();
50869         }
50870     },
50871
50872     // private
50873     processEvent : function(name, e){
50874         // does this fire select???
50875         Roo.log('grid:processEvent '  + name);
50876         
50877         if (name != 'touchstart' ) {
50878             this.fireEvent(name, e);    
50879         }
50880         
50881         var t = e.getTarget();
50882         var v = this.view;
50883         var header = v.findHeaderIndex(t);
50884         if(header !== false){
50885             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
50886         }else{
50887             var row = v.findRowIndex(t);
50888             var cell = v.findCellIndex(t);
50889             if (name == 'touchstart') {
50890                 // first touch is always a click.
50891                 // hopefull this happens after selection is updated.?
50892                 name = false;
50893                 
50894                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
50895                     var cs = this.selModel.getSelectedCell();
50896                     if (row == cs[0] && cell == cs[1]){
50897                         name = 'dblclick';
50898                     }
50899                 }
50900                 if (typeof(this.selModel.getSelections) != 'undefined') {
50901                     var cs = this.selModel.getSelections();
50902                     var ds = this.dataSource;
50903                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
50904                         name = 'dblclick';
50905                     }
50906                 }
50907                 if (!name) {
50908                     return;
50909                 }
50910             }
50911             
50912             
50913             if(row !== false){
50914                 this.fireEvent("row" + name, this, row, e);
50915                 if(cell !== false){
50916                     this.fireEvent("cell" + name, this, row, cell, e);
50917                 }
50918             }
50919         }
50920     },
50921
50922     // private
50923     onClick : function(e){
50924         this.processEvent("click", e);
50925     },
50926    // private
50927     onTouchStart : function(e){
50928         this.processEvent("touchstart", e);
50929     },
50930
50931     // private
50932     onContextMenu : function(e, t){
50933         this.processEvent("contextmenu", e);
50934     },
50935
50936     // private
50937     onDblClick : function(e){
50938         this.processEvent("dblclick", e);
50939     },
50940
50941     // private
50942     walkCells : function(row, col, step, fn, scope){
50943         var cm = this.colModel, clen = cm.getColumnCount();
50944         var ds = this.dataSource, rlen = ds.getCount(), first = true;
50945         if(step < 0){
50946             if(col < 0){
50947                 row--;
50948                 first = false;
50949             }
50950             while(row >= 0){
50951                 if(!first){
50952                     col = clen-1;
50953                 }
50954                 first = false;
50955                 while(col >= 0){
50956                     if(fn.call(scope || this, row, col, cm) === true){
50957                         return [row, col];
50958                     }
50959                     col--;
50960                 }
50961                 row--;
50962             }
50963         } else {
50964             if(col >= clen){
50965                 row++;
50966                 first = false;
50967             }
50968             while(row < rlen){
50969                 if(!first){
50970                     col = 0;
50971                 }
50972                 first = false;
50973                 while(col < clen){
50974                     if(fn.call(scope || this, row, col, cm) === true){
50975                         return [row, col];
50976                     }
50977                     col++;
50978                 }
50979                 row++;
50980             }
50981         }
50982         return null;
50983     },
50984
50985     // private
50986     getSelections : function(){
50987         return this.selModel.getSelections();
50988     },
50989
50990     /**
50991      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
50992      * but if manual update is required this method will initiate it.
50993      */
50994     autoSize : function(){
50995         if(this.rendered){
50996             this.view.layout();
50997             if(this.view.adjustForScroll){
50998                 this.view.adjustForScroll();
50999             }
51000         }
51001     },
51002
51003     /**
51004      * Returns the grid's underlying element.
51005      * @return {Element} The element
51006      */
51007     getGridEl : function(){
51008         return this.container;
51009     },
51010
51011     // private for compatibility, overridden by editor grid
51012     stopEditing : function(){},
51013
51014     /**
51015      * Returns the grid's SelectionModel.
51016      * @return {SelectionModel}
51017      */
51018     getSelectionModel : function(){
51019         if(!this.selModel){
51020             this.selModel = new Roo.grid.RowSelectionModel();
51021         }
51022         return this.selModel;
51023     },
51024
51025     /**
51026      * Returns the grid's DataSource.
51027      * @return {DataSource}
51028      */
51029     getDataSource : function(){
51030         return this.dataSource;
51031     },
51032
51033     /**
51034      * Returns the grid's ColumnModel.
51035      * @return {ColumnModel}
51036      */
51037     getColumnModel : function(){
51038         return this.colModel;
51039     },
51040
51041     /**
51042      * Returns the grid's GridView object.
51043      * @return {GridView}
51044      */
51045     getView : function(){
51046         if(!this.view){
51047             this.view = new Roo.grid.GridView(this.viewConfig);
51048         }
51049         return this.view;
51050     },
51051     /**
51052      * Called to get grid's drag proxy text, by default returns this.ddText.
51053      * @return {String}
51054      */
51055     getDragDropText : function(){
51056         var count = this.selModel.getCount();
51057         return String.format(this.ddText, count, count == 1 ? '' : 's');
51058     }
51059 });
51060 /**
51061  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
51062  * %0 is replaced with the number of selected rows.
51063  * @type String
51064  */
51065 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51066  * Based on:
51067  * Ext JS Library 1.1.1
51068  * Copyright(c) 2006-2007, Ext JS, LLC.
51069  *
51070  * Originally Released Under LGPL - original licence link has changed is not relivant.
51071  *
51072  * Fork - LGPL
51073  * <script type="text/javascript">
51074  */
51075  
51076 Roo.grid.AbstractGridView = function(){
51077         this.grid = null;
51078         
51079         this.events = {
51080             "beforerowremoved" : true,
51081             "beforerowsinserted" : true,
51082             "beforerefresh" : true,
51083             "rowremoved" : true,
51084             "rowsinserted" : true,
51085             "rowupdated" : true,
51086             "refresh" : true
51087         };
51088     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51089 };
51090
51091 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51092     rowClass : "x-grid-row",
51093     cellClass : "x-grid-cell",
51094     tdClass : "x-grid-td",
51095     hdClass : "x-grid-hd",
51096     splitClass : "x-grid-hd-split",
51097     
51098         init: function(grid){
51099         this.grid = grid;
51100                 var cid = this.grid.getGridEl().id;
51101         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51102         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51103         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51104         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51105         },
51106         
51107         getColumnRenderers : function(){
51108         var renderers = [];
51109         var cm = this.grid.colModel;
51110         var colCount = cm.getColumnCount();
51111         for(var i = 0; i < colCount; i++){
51112             renderers[i] = cm.getRenderer(i);
51113         }
51114         return renderers;
51115     },
51116     
51117     getColumnIds : function(){
51118         var ids = [];
51119         var cm = this.grid.colModel;
51120         var colCount = cm.getColumnCount();
51121         for(var i = 0; i < colCount; i++){
51122             ids[i] = cm.getColumnId(i);
51123         }
51124         return ids;
51125     },
51126     
51127     getDataIndexes : function(){
51128         if(!this.indexMap){
51129             this.indexMap = this.buildIndexMap();
51130         }
51131         return this.indexMap.colToData;
51132     },
51133     
51134     getColumnIndexByDataIndex : function(dataIndex){
51135         if(!this.indexMap){
51136             this.indexMap = this.buildIndexMap();
51137         }
51138         return this.indexMap.dataToCol[dataIndex];
51139     },
51140     
51141     /**
51142      * Set a css style for a column dynamically. 
51143      * @param {Number} colIndex The index of the column
51144      * @param {String} name The css property name
51145      * @param {String} value The css value
51146      */
51147     setCSSStyle : function(colIndex, name, value){
51148         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51149         Roo.util.CSS.updateRule(selector, name, value);
51150     },
51151     
51152     generateRules : function(cm){
51153         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51154         Roo.util.CSS.removeStyleSheet(rulesId);
51155         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51156             var cid = cm.getColumnId(i);
51157             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51158                          this.tdSelector, cid, " {\n}\n",
51159                          this.hdSelector, cid, " {\n}\n",
51160                          this.splitSelector, cid, " {\n}\n");
51161         }
51162         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51163     }
51164 });/*
51165  * Based on:
51166  * Ext JS Library 1.1.1
51167  * Copyright(c) 2006-2007, Ext JS, LLC.
51168  *
51169  * Originally Released Under LGPL - original licence link has changed is not relivant.
51170  *
51171  * Fork - LGPL
51172  * <script type="text/javascript">
51173  */
51174
51175 // private
51176 // This is a support class used internally by the Grid components
51177 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51178     this.grid = grid;
51179     this.view = grid.getView();
51180     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51181     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51182     if(hd2){
51183         this.setHandleElId(Roo.id(hd));
51184         this.setOuterHandleElId(Roo.id(hd2));
51185     }
51186     this.scroll = false;
51187 };
51188 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51189     maxDragWidth: 120,
51190     getDragData : function(e){
51191         var t = Roo.lib.Event.getTarget(e);
51192         var h = this.view.findHeaderCell(t);
51193         if(h){
51194             return {ddel: h.firstChild, header:h};
51195         }
51196         return false;
51197     },
51198
51199     onInitDrag : function(e){
51200         this.view.headersDisabled = true;
51201         var clone = this.dragData.ddel.cloneNode(true);
51202         clone.id = Roo.id();
51203         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51204         this.proxy.update(clone);
51205         return true;
51206     },
51207
51208     afterValidDrop : function(){
51209         var v = this.view;
51210         setTimeout(function(){
51211             v.headersDisabled = false;
51212         }, 50);
51213     },
51214
51215     afterInvalidDrop : function(){
51216         var v = this.view;
51217         setTimeout(function(){
51218             v.headersDisabled = false;
51219         }, 50);
51220     }
51221 });
51222 /*
51223  * Based on:
51224  * Ext JS Library 1.1.1
51225  * Copyright(c) 2006-2007, Ext JS, LLC.
51226  *
51227  * Originally Released Under LGPL - original licence link has changed is not relivant.
51228  *
51229  * Fork - LGPL
51230  * <script type="text/javascript">
51231  */
51232 // private
51233 // This is a support class used internally by the Grid components
51234 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51235     this.grid = grid;
51236     this.view = grid.getView();
51237     // split the proxies so they don't interfere with mouse events
51238     this.proxyTop = Roo.DomHelper.append(document.body, {
51239         cls:"col-move-top", html:"&#160;"
51240     }, true);
51241     this.proxyBottom = Roo.DomHelper.append(document.body, {
51242         cls:"col-move-bottom", html:"&#160;"
51243     }, true);
51244     this.proxyTop.hide = this.proxyBottom.hide = function(){
51245         this.setLeftTop(-100,-100);
51246         this.setStyle("visibility", "hidden");
51247     };
51248     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51249     // temporarily disabled
51250     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51251     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51252 };
51253 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51254     proxyOffsets : [-4, -9],
51255     fly: Roo.Element.fly,
51256
51257     getTargetFromEvent : function(e){
51258         var t = Roo.lib.Event.getTarget(e);
51259         var cindex = this.view.findCellIndex(t);
51260         if(cindex !== false){
51261             return this.view.getHeaderCell(cindex);
51262         }
51263         return null;
51264     },
51265
51266     nextVisible : function(h){
51267         var v = this.view, cm = this.grid.colModel;
51268         h = h.nextSibling;
51269         while(h){
51270             if(!cm.isHidden(v.getCellIndex(h))){
51271                 return h;
51272             }
51273             h = h.nextSibling;
51274         }
51275         return null;
51276     },
51277
51278     prevVisible : function(h){
51279         var v = this.view, cm = this.grid.colModel;
51280         h = h.prevSibling;
51281         while(h){
51282             if(!cm.isHidden(v.getCellIndex(h))){
51283                 return h;
51284             }
51285             h = h.prevSibling;
51286         }
51287         return null;
51288     },
51289
51290     positionIndicator : function(h, n, e){
51291         var x = Roo.lib.Event.getPageX(e);
51292         var r = Roo.lib.Dom.getRegion(n.firstChild);
51293         var px, pt, py = r.top + this.proxyOffsets[1];
51294         if((r.right - x) <= (r.right-r.left)/2){
51295             px = r.right+this.view.borderWidth;
51296             pt = "after";
51297         }else{
51298             px = r.left;
51299             pt = "before";
51300         }
51301         var oldIndex = this.view.getCellIndex(h);
51302         var newIndex = this.view.getCellIndex(n);
51303
51304         if(this.grid.colModel.isFixed(newIndex)){
51305             return false;
51306         }
51307
51308         var locked = this.grid.colModel.isLocked(newIndex);
51309
51310         if(pt == "after"){
51311             newIndex++;
51312         }
51313         if(oldIndex < newIndex){
51314             newIndex--;
51315         }
51316         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51317             return false;
51318         }
51319         px +=  this.proxyOffsets[0];
51320         this.proxyTop.setLeftTop(px, py);
51321         this.proxyTop.show();
51322         if(!this.bottomOffset){
51323             this.bottomOffset = this.view.mainHd.getHeight();
51324         }
51325         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51326         this.proxyBottom.show();
51327         return pt;
51328     },
51329
51330     onNodeEnter : function(n, dd, e, data){
51331         if(data.header != n){
51332             this.positionIndicator(data.header, n, e);
51333         }
51334     },
51335
51336     onNodeOver : function(n, dd, e, data){
51337         var result = false;
51338         if(data.header != n){
51339             result = this.positionIndicator(data.header, n, e);
51340         }
51341         if(!result){
51342             this.proxyTop.hide();
51343             this.proxyBottom.hide();
51344         }
51345         return result ? this.dropAllowed : this.dropNotAllowed;
51346     },
51347
51348     onNodeOut : function(n, dd, e, data){
51349         this.proxyTop.hide();
51350         this.proxyBottom.hide();
51351     },
51352
51353     onNodeDrop : function(n, dd, e, data){
51354         var h = data.header;
51355         if(h != n){
51356             var cm = this.grid.colModel;
51357             var x = Roo.lib.Event.getPageX(e);
51358             var r = Roo.lib.Dom.getRegion(n.firstChild);
51359             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
51360             var oldIndex = this.view.getCellIndex(h);
51361             var newIndex = this.view.getCellIndex(n);
51362             var locked = cm.isLocked(newIndex);
51363             if(pt == "after"){
51364                 newIndex++;
51365             }
51366             if(oldIndex < newIndex){
51367                 newIndex--;
51368             }
51369             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
51370                 return false;
51371             }
51372             cm.setLocked(oldIndex, locked, true);
51373             cm.moveColumn(oldIndex, newIndex);
51374             this.grid.fireEvent("columnmove", oldIndex, newIndex);
51375             return true;
51376         }
51377         return false;
51378     }
51379 });
51380 /*
51381  * Based on:
51382  * Ext JS Library 1.1.1
51383  * Copyright(c) 2006-2007, Ext JS, LLC.
51384  *
51385  * Originally Released Under LGPL - original licence link has changed is not relivant.
51386  *
51387  * Fork - LGPL
51388  * <script type="text/javascript">
51389  */
51390   
51391 /**
51392  * @class Roo.grid.GridView
51393  * @extends Roo.util.Observable
51394  *
51395  * @constructor
51396  * @param {Object} config
51397  */
51398 Roo.grid.GridView = function(config){
51399     Roo.grid.GridView.superclass.constructor.call(this);
51400     this.el = null;
51401
51402     Roo.apply(this, config);
51403 };
51404
51405 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
51406
51407     unselectable :  'unselectable="on"',
51408     unselectableCls :  'x-unselectable',
51409     
51410     
51411     rowClass : "x-grid-row",
51412
51413     cellClass : "x-grid-col",
51414
51415     tdClass : "x-grid-td",
51416
51417     hdClass : "x-grid-hd",
51418
51419     splitClass : "x-grid-split",
51420
51421     sortClasses : ["sort-asc", "sort-desc"],
51422
51423     enableMoveAnim : false,
51424
51425     hlColor: "C3DAF9",
51426
51427     dh : Roo.DomHelper,
51428
51429     fly : Roo.Element.fly,
51430
51431     css : Roo.util.CSS,
51432
51433     borderWidth: 1,
51434
51435     splitOffset: 3,
51436
51437     scrollIncrement : 22,
51438
51439     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
51440
51441     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
51442
51443     bind : function(ds, cm){
51444         if(this.ds){
51445             this.ds.un("load", this.onLoad, this);
51446             this.ds.un("datachanged", this.onDataChange, this);
51447             this.ds.un("add", this.onAdd, this);
51448             this.ds.un("remove", this.onRemove, this);
51449             this.ds.un("update", this.onUpdate, this);
51450             this.ds.un("clear", this.onClear, this);
51451         }
51452         if(ds){
51453             ds.on("load", this.onLoad, this);
51454             ds.on("datachanged", this.onDataChange, this);
51455             ds.on("add", this.onAdd, this);
51456             ds.on("remove", this.onRemove, this);
51457             ds.on("update", this.onUpdate, this);
51458             ds.on("clear", this.onClear, this);
51459         }
51460         this.ds = ds;
51461
51462         if(this.cm){
51463             this.cm.un("widthchange", this.onColWidthChange, this);
51464             this.cm.un("headerchange", this.onHeaderChange, this);
51465             this.cm.un("hiddenchange", this.onHiddenChange, this);
51466             this.cm.un("columnmoved", this.onColumnMove, this);
51467             this.cm.un("columnlockchange", this.onColumnLock, this);
51468         }
51469         if(cm){
51470             this.generateRules(cm);
51471             cm.on("widthchange", this.onColWidthChange, this);
51472             cm.on("headerchange", this.onHeaderChange, this);
51473             cm.on("hiddenchange", this.onHiddenChange, this);
51474             cm.on("columnmoved", this.onColumnMove, this);
51475             cm.on("columnlockchange", this.onColumnLock, this);
51476         }
51477         this.cm = cm;
51478     },
51479
51480     init: function(grid){
51481         Roo.grid.GridView.superclass.init.call(this, grid);
51482
51483         this.bind(grid.dataSource, grid.colModel);
51484
51485         grid.on("headerclick", this.handleHeaderClick, this);
51486
51487         if(grid.trackMouseOver){
51488             grid.on("mouseover", this.onRowOver, this);
51489             grid.on("mouseout", this.onRowOut, this);
51490         }
51491         grid.cancelTextSelection = function(){};
51492         this.gridId = grid.id;
51493
51494         var tpls = this.templates || {};
51495
51496         if(!tpls.master){
51497             tpls.master = new Roo.Template(
51498                '<div class="x-grid" hidefocus="true">',
51499                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
51500                   '<div class="x-grid-topbar"></div>',
51501                   '<div class="x-grid-scroller"><div></div></div>',
51502                   '<div class="x-grid-locked">',
51503                       '<div class="x-grid-header">{lockedHeader}</div>',
51504                       '<div class="x-grid-body">{lockedBody}</div>',
51505                   "</div>",
51506                   '<div class="x-grid-viewport">',
51507                       '<div class="x-grid-header">{header}</div>',
51508                       '<div class="x-grid-body">{body}</div>',
51509                   "</div>",
51510                   '<div class="x-grid-bottombar"></div>',
51511                  
51512                   '<div class="x-grid-resize-proxy">&#160;</div>',
51513                "</div>"
51514             );
51515             tpls.master.disableformats = true;
51516         }
51517
51518         if(!tpls.header){
51519             tpls.header = new Roo.Template(
51520                '<table border="0" cellspacing="0" cellpadding="0">',
51521                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
51522                "</table>{splits}"
51523             );
51524             tpls.header.disableformats = true;
51525         }
51526         tpls.header.compile();
51527
51528         if(!tpls.hcell){
51529             tpls.hcell = new Roo.Template(
51530                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
51531                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
51532                 "</div></td>"
51533              );
51534              tpls.hcell.disableFormats = true;
51535         }
51536         tpls.hcell.compile();
51537
51538         if(!tpls.hsplit){
51539             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
51540                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
51541             tpls.hsplit.disableFormats = true;
51542         }
51543         tpls.hsplit.compile();
51544
51545         if(!tpls.body){
51546             tpls.body = new Roo.Template(
51547                '<table border="0" cellspacing="0" cellpadding="0">',
51548                "<tbody>{rows}</tbody>",
51549                "</table>"
51550             );
51551             tpls.body.disableFormats = true;
51552         }
51553         tpls.body.compile();
51554
51555         if(!tpls.row){
51556             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
51557             tpls.row.disableFormats = true;
51558         }
51559         tpls.row.compile();
51560
51561         if(!tpls.cell){
51562             tpls.cell = new Roo.Template(
51563                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
51564                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
51565                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
51566                 "</td>"
51567             );
51568             tpls.cell.disableFormats = true;
51569         }
51570         tpls.cell.compile();
51571
51572         this.templates = tpls;
51573     },
51574
51575     // remap these for backwards compat
51576     onColWidthChange : function(){
51577         this.updateColumns.apply(this, arguments);
51578     },
51579     onHeaderChange : function(){
51580         this.updateHeaders.apply(this, arguments);
51581     }, 
51582     onHiddenChange : function(){
51583         this.handleHiddenChange.apply(this, arguments);
51584     },
51585     onColumnMove : function(){
51586         this.handleColumnMove.apply(this, arguments);
51587     },
51588     onColumnLock : function(){
51589         this.handleLockChange.apply(this, arguments);
51590     },
51591
51592     onDataChange : function(){
51593         this.refresh();
51594         this.updateHeaderSortState();
51595     },
51596
51597     onClear : function(){
51598         this.refresh();
51599     },
51600
51601     onUpdate : function(ds, record){
51602         this.refreshRow(record);
51603     },
51604
51605     refreshRow : function(record){
51606         var ds = this.ds, index;
51607         if(typeof record == 'number'){
51608             index = record;
51609             record = ds.getAt(index);
51610         }else{
51611             index = ds.indexOf(record);
51612         }
51613         this.insertRows(ds, index, index, true);
51614         this.onRemove(ds, record, index+1, true);
51615         this.syncRowHeights(index, index);
51616         this.layout();
51617         this.fireEvent("rowupdated", this, index, record);
51618     },
51619
51620     onAdd : function(ds, records, index){
51621         this.insertRows(ds, index, index + (records.length-1));
51622     },
51623
51624     onRemove : function(ds, record, index, isUpdate){
51625         if(isUpdate !== true){
51626             this.fireEvent("beforerowremoved", this, index, record);
51627         }
51628         var bt = this.getBodyTable(), lt = this.getLockedTable();
51629         if(bt.rows[index]){
51630             bt.firstChild.removeChild(bt.rows[index]);
51631         }
51632         if(lt.rows[index]){
51633             lt.firstChild.removeChild(lt.rows[index]);
51634         }
51635         if(isUpdate !== true){
51636             this.stripeRows(index);
51637             this.syncRowHeights(index, index);
51638             this.layout();
51639             this.fireEvent("rowremoved", this, index, record);
51640         }
51641     },
51642
51643     onLoad : function(){
51644         this.scrollToTop();
51645     },
51646
51647     /**
51648      * Scrolls the grid to the top
51649      */
51650     scrollToTop : function(){
51651         if(this.scroller){
51652             this.scroller.dom.scrollTop = 0;
51653             this.syncScroll();
51654         }
51655     },
51656
51657     /**
51658      * Gets a panel in the header of the grid that can be used for toolbars etc.
51659      * After modifying the contents of this panel a call to grid.autoSize() may be
51660      * required to register any changes in size.
51661      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
51662      * @return Roo.Element
51663      */
51664     getHeaderPanel : function(doShow){
51665         if(doShow){
51666             this.headerPanel.show();
51667         }
51668         return this.headerPanel;
51669     },
51670
51671     /**
51672      * Gets a panel in the footer of the grid that can be used for toolbars etc.
51673      * After modifying the contents of this panel a call to grid.autoSize() may be
51674      * required to register any changes in size.
51675      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
51676      * @return Roo.Element
51677      */
51678     getFooterPanel : function(doShow){
51679         if(doShow){
51680             this.footerPanel.show();
51681         }
51682         return this.footerPanel;
51683     },
51684
51685     initElements : function(){
51686         var E = Roo.Element;
51687         var el = this.grid.getGridEl().dom.firstChild;
51688         var cs = el.childNodes;
51689
51690         this.el = new E(el);
51691         
51692          this.focusEl = new E(el.firstChild);
51693         this.focusEl.swallowEvent("click", true);
51694         
51695         this.headerPanel = new E(cs[1]);
51696         this.headerPanel.enableDisplayMode("block");
51697
51698         this.scroller = new E(cs[2]);
51699         this.scrollSizer = new E(this.scroller.dom.firstChild);
51700
51701         this.lockedWrap = new E(cs[3]);
51702         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
51703         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
51704
51705         this.mainWrap = new E(cs[4]);
51706         this.mainHd = new E(this.mainWrap.dom.firstChild);
51707         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
51708
51709         this.footerPanel = new E(cs[5]);
51710         this.footerPanel.enableDisplayMode("block");
51711
51712         this.resizeProxy = new E(cs[6]);
51713
51714         this.headerSelector = String.format(
51715            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
51716            this.lockedHd.id, this.mainHd.id
51717         );
51718
51719         this.splitterSelector = String.format(
51720            '#{0} div.x-grid-split, #{1} div.x-grid-split',
51721            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
51722         );
51723     },
51724     idToCssName : function(s)
51725     {
51726         return s.replace(/[^a-z0-9]+/ig, '-');
51727     },
51728
51729     getHeaderCell : function(index){
51730         return Roo.DomQuery.select(this.headerSelector)[index];
51731     },
51732
51733     getHeaderCellMeasure : function(index){
51734         return this.getHeaderCell(index).firstChild;
51735     },
51736
51737     getHeaderCellText : function(index){
51738         return this.getHeaderCell(index).firstChild.firstChild;
51739     },
51740
51741     getLockedTable : function(){
51742         return this.lockedBody.dom.firstChild;
51743     },
51744
51745     getBodyTable : function(){
51746         return this.mainBody.dom.firstChild;
51747     },
51748
51749     getLockedRow : function(index){
51750         return this.getLockedTable().rows[index];
51751     },
51752
51753     getRow : function(index){
51754         return this.getBodyTable().rows[index];
51755     },
51756
51757     getRowComposite : function(index){
51758         if(!this.rowEl){
51759             this.rowEl = new Roo.CompositeElementLite();
51760         }
51761         var els = [], lrow, mrow;
51762         if(lrow = this.getLockedRow(index)){
51763             els.push(lrow);
51764         }
51765         if(mrow = this.getRow(index)){
51766             els.push(mrow);
51767         }
51768         this.rowEl.elements = els;
51769         return this.rowEl;
51770     },
51771     /**
51772      * Gets the 'td' of the cell
51773      * 
51774      * @param {Integer} rowIndex row to select
51775      * @param {Integer} colIndex column to select
51776      * 
51777      * @return {Object} 
51778      */
51779     getCell : function(rowIndex, colIndex){
51780         var locked = this.cm.getLockedCount();
51781         var source;
51782         if(colIndex < locked){
51783             source = this.lockedBody.dom.firstChild;
51784         }else{
51785             source = this.mainBody.dom.firstChild;
51786             colIndex -= locked;
51787         }
51788         return source.rows[rowIndex].childNodes[colIndex];
51789     },
51790
51791     getCellText : function(rowIndex, colIndex){
51792         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
51793     },
51794
51795     getCellBox : function(cell){
51796         var b = this.fly(cell).getBox();
51797         if(Roo.isOpera){ // opera fails to report the Y
51798             b.y = cell.offsetTop + this.mainBody.getY();
51799         }
51800         return b;
51801     },
51802
51803     getCellIndex : function(cell){
51804         var id = String(cell.className).match(this.cellRE);
51805         if(id){
51806             return parseInt(id[1], 10);
51807         }
51808         return 0;
51809     },
51810
51811     findHeaderIndex : function(n){
51812         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
51813         return r ? this.getCellIndex(r) : false;
51814     },
51815
51816     findHeaderCell : function(n){
51817         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
51818         return r ? r : false;
51819     },
51820
51821     findRowIndex : function(n){
51822         if(!n){
51823             return false;
51824         }
51825         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
51826         return r ? r.rowIndex : false;
51827     },
51828
51829     findCellIndex : function(node){
51830         var stop = this.el.dom;
51831         while(node && node != stop){
51832             if(this.findRE.test(node.className)){
51833                 return this.getCellIndex(node);
51834             }
51835             node = node.parentNode;
51836         }
51837         return false;
51838     },
51839
51840     getColumnId : function(index){
51841         return this.cm.getColumnId(index);
51842     },
51843
51844     getSplitters : function()
51845     {
51846         if(this.splitterSelector){
51847            return Roo.DomQuery.select(this.splitterSelector);
51848         }else{
51849             return null;
51850       }
51851     },
51852
51853     getSplitter : function(index){
51854         return this.getSplitters()[index];
51855     },
51856
51857     onRowOver : function(e, t){
51858         var row;
51859         if((row = this.findRowIndex(t)) !== false){
51860             this.getRowComposite(row).addClass("x-grid-row-over");
51861         }
51862     },
51863
51864     onRowOut : function(e, t){
51865         var row;
51866         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
51867             this.getRowComposite(row).removeClass("x-grid-row-over");
51868         }
51869     },
51870
51871     renderHeaders : function(){
51872         var cm = this.cm;
51873         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
51874         var cb = [], lb = [], sb = [], lsb = [], p = {};
51875         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51876             p.cellId = "x-grid-hd-0-" + i;
51877             p.splitId = "x-grid-csplit-0-" + i;
51878             p.id = cm.getColumnId(i);
51879             p.title = cm.getColumnTooltip(i) || "";
51880             p.value = cm.getColumnHeader(i) || "";
51881             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
51882             if(!cm.isLocked(i)){
51883                 cb[cb.length] = ct.apply(p);
51884                 sb[sb.length] = st.apply(p);
51885             }else{
51886                 lb[lb.length] = ct.apply(p);
51887                 lsb[lsb.length] = st.apply(p);
51888             }
51889         }
51890         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
51891                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
51892     },
51893
51894     updateHeaders : function(){
51895         var html = this.renderHeaders();
51896         this.lockedHd.update(html[0]);
51897         this.mainHd.update(html[1]);
51898     },
51899
51900     /**
51901      * Focuses the specified row.
51902      * @param {Number} row The row index
51903      */
51904     focusRow : function(row)
51905     {
51906         //Roo.log('GridView.focusRow');
51907         var x = this.scroller.dom.scrollLeft;
51908         this.focusCell(row, 0, false);
51909         this.scroller.dom.scrollLeft = x;
51910     },
51911
51912     /**
51913      * Focuses the specified cell.
51914      * @param {Number} row The row index
51915      * @param {Number} col The column index
51916      * @param {Boolean} hscroll false to disable horizontal scrolling
51917      */
51918     focusCell : function(row, col, hscroll)
51919     {
51920         //Roo.log('GridView.focusCell');
51921         var el = this.ensureVisible(row, col, hscroll);
51922         this.focusEl.alignTo(el, "tl-tl");
51923         if(Roo.isGecko){
51924             this.focusEl.focus();
51925         }else{
51926             this.focusEl.focus.defer(1, this.focusEl);
51927         }
51928     },
51929
51930     /**
51931      * Scrolls the specified cell into view
51932      * @param {Number} row The row index
51933      * @param {Number} col The column index
51934      * @param {Boolean} hscroll false to disable horizontal scrolling
51935      */
51936     ensureVisible : function(row, col, hscroll)
51937     {
51938         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
51939         //return null; //disable for testing.
51940         if(typeof row != "number"){
51941             row = row.rowIndex;
51942         }
51943         if(row < 0 && row >= this.ds.getCount()){
51944             return  null;
51945         }
51946         col = (col !== undefined ? col : 0);
51947         var cm = this.grid.colModel;
51948         while(cm.isHidden(col)){
51949             col++;
51950         }
51951
51952         var el = this.getCell(row, col);
51953         if(!el){
51954             return null;
51955         }
51956         var c = this.scroller.dom;
51957
51958         var ctop = parseInt(el.offsetTop, 10);
51959         var cleft = parseInt(el.offsetLeft, 10);
51960         var cbot = ctop + el.offsetHeight;
51961         var cright = cleft + el.offsetWidth;
51962         
51963         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
51964         var stop = parseInt(c.scrollTop, 10);
51965         var sleft = parseInt(c.scrollLeft, 10);
51966         var sbot = stop + ch;
51967         var sright = sleft + c.clientWidth;
51968         /*
51969         Roo.log('GridView.ensureVisible:' +
51970                 ' ctop:' + ctop +
51971                 ' c.clientHeight:' + c.clientHeight +
51972                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
51973                 ' stop:' + stop +
51974                 ' cbot:' + cbot +
51975                 ' sbot:' + sbot +
51976                 ' ch:' + ch  
51977                 );
51978         */
51979         if(ctop < stop){
51980              c.scrollTop = ctop;
51981             //Roo.log("set scrolltop to ctop DISABLE?");
51982         }else if(cbot > sbot){
51983             //Roo.log("set scrolltop to cbot-ch");
51984             c.scrollTop = cbot-ch;
51985         }
51986         
51987         if(hscroll !== false){
51988             if(cleft < sleft){
51989                 c.scrollLeft = cleft;
51990             }else if(cright > sright){
51991                 c.scrollLeft = cright-c.clientWidth;
51992             }
51993         }
51994          
51995         return el;
51996     },
51997
51998     updateColumns : function(){
51999         this.grid.stopEditing();
52000         var cm = this.grid.colModel, colIds = this.getColumnIds();
52001         //var totalWidth = cm.getTotalWidth();
52002         var pos = 0;
52003         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52004             //if(cm.isHidden(i)) continue;
52005             var w = cm.getColumnWidth(i);
52006             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52007             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52008         }
52009         this.updateSplitters();
52010     },
52011
52012     generateRules : function(cm){
52013         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
52014         Roo.util.CSS.removeStyleSheet(rulesId);
52015         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52016             var cid = cm.getColumnId(i);
52017             var align = '';
52018             if(cm.config[i].align){
52019                 align = 'text-align:'+cm.config[i].align+';';
52020             }
52021             var hidden = '';
52022             if(cm.isHidden(i)){
52023                 hidden = 'display:none;';
52024             }
52025             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
52026             ruleBuf.push(
52027                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
52028                     this.hdSelector, cid, " {\n", align, width, "}\n",
52029                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
52030                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
52031         }
52032         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52033     },
52034
52035     updateSplitters : function(){
52036         var cm = this.cm, s = this.getSplitters();
52037         if(s){ // splitters not created yet
52038             var pos = 0, locked = true;
52039             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52040                 if(cm.isHidden(i)) continue;
52041                 var w = cm.getColumnWidth(i); // make sure it's a number
52042                 if(!cm.isLocked(i) && locked){
52043                     pos = 0;
52044                     locked = false;
52045                 }
52046                 pos += w;
52047                 s[i].style.left = (pos-this.splitOffset) + "px";
52048             }
52049         }
52050     },
52051
52052     handleHiddenChange : function(colModel, colIndex, hidden){
52053         if(hidden){
52054             this.hideColumn(colIndex);
52055         }else{
52056             this.unhideColumn(colIndex);
52057         }
52058     },
52059
52060     hideColumn : function(colIndex){
52061         var cid = this.getColumnId(colIndex);
52062         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
52063         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52064         if(Roo.isSafari){
52065             this.updateHeaders();
52066         }
52067         this.updateSplitters();
52068         this.layout();
52069     },
52070
52071     unhideColumn : function(colIndex){
52072         var cid = this.getColumnId(colIndex);
52073         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52074         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52075
52076         if(Roo.isSafari){
52077             this.updateHeaders();
52078         }
52079         this.updateSplitters();
52080         this.layout();
52081     },
52082
52083     insertRows : function(dm, firstRow, lastRow, isUpdate){
52084         if(firstRow == 0 && lastRow == dm.getCount()-1){
52085             this.refresh();
52086         }else{
52087             if(!isUpdate){
52088                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52089             }
52090             var s = this.getScrollState();
52091             var markup = this.renderRows(firstRow, lastRow);
52092             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52093             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52094             this.restoreScroll(s);
52095             if(!isUpdate){
52096                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52097                 this.syncRowHeights(firstRow, lastRow);
52098                 this.stripeRows(firstRow);
52099                 this.layout();
52100             }
52101         }
52102     },
52103
52104     bufferRows : function(markup, target, index){
52105         var before = null, trows = target.rows, tbody = target.tBodies[0];
52106         if(index < trows.length){
52107             before = trows[index];
52108         }
52109         var b = document.createElement("div");
52110         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52111         var rows = b.firstChild.rows;
52112         for(var i = 0, len = rows.length; i < len; i++){
52113             if(before){
52114                 tbody.insertBefore(rows[0], before);
52115             }else{
52116                 tbody.appendChild(rows[0]);
52117             }
52118         }
52119         b.innerHTML = "";
52120         b = null;
52121     },
52122
52123     deleteRows : function(dm, firstRow, lastRow){
52124         if(dm.getRowCount()<1){
52125             this.fireEvent("beforerefresh", this);
52126             this.mainBody.update("");
52127             this.lockedBody.update("");
52128             this.fireEvent("refresh", this);
52129         }else{
52130             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52131             var bt = this.getBodyTable();
52132             var tbody = bt.firstChild;
52133             var rows = bt.rows;
52134             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52135                 tbody.removeChild(rows[firstRow]);
52136             }
52137             this.stripeRows(firstRow);
52138             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52139         }
52140     },
52141
52142     updateRows : function(dataSource, firstRow, lastRow){
52143         var s = this.getScrollState();
52144         this.refresh();
52145         this.restoreScroll(s);
52146     },
52147
52148     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52149         if(!noRefresh){
52150            this.refresh();
52151         }
52152         this.updateHeaderSortState();
52153     },
52154
52155     getScrollState : function(){
52156         
52157         var sb = this.scroller.dom;
52158         return {left: sb.scrollLeft, top: sb.scrollTop};
52159     },
52160
52161     stripeRows : function(startRow){
52162         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52163             return;
52164         }
52165         startRow = startRow || 0;
52166         var rows = this.getBodyTable().rows;
52167         var lrows = this.getLockedTable().rows;
52168         var cls = ' x-grid-row-alt ';
52169         for(var i = startRow, len = rows.length; i < len; i++){
52170             var row = rows[i], lrow = lrows[i];
52171             var isAlt = ((i+1) % 2 == 0);
52172             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52173             if(isAlt == hasAlt){
52174                 continue;
52175             }
52176             if(isAlt){
52177                 row.className += " x-grid-row-alt";
52178             }else{
52179                 row.className = row.className.replace("x-grid-row-alt", "");
52180             }
52181             if(lrow){
52182                 lrow.className = row.className;
52183             }
52184         }
52185     },
52186
52187     restoreScroll : function(state){
52188         //Roo.log('GridView.restoreScroll');
52189         var sb = this.scroller.dom;
52190         sb.scrollLeft = state.left;
52191         sb.scrollTop = state.top;
52192         this.syncScroll();
52193     },
52194
52195     syncScroll : function(){
52196         //Roo.log('GridView.syncScroll');
52197         var sb = this.scroller.dom;
52198         var sh = this.mainHd.dom;
52199         var bs = this.mainBody.dom;
52200         var lv = this.lockedBody.dom;
52201         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52202         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52203     },
52204
52205     handleScroll : function(e){
52206         this.syncScroll();
52207         var sb = this.scroller.dom;
52208         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52209         e.stopEvent();
52210     },
52211
52212     handleWheel : function(e){
52213         var d = e.getWheelDelta();
52214         this.scroller.dom.scrollTop -= d*22;
52215         // set this here to prevent jumpy scrolling on large tables
52216         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52217         e.stopEvent();
52218     },
52219
52220     renderRows : function(startRow, endRow){
52221         // pull in all the crap needed to render rows
52222         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52223         var colCount = cm.getColumnCount();
52224
52225         if(ds.getCount() < 1){
52226             return ["", ""];
52227         }
52228
52229         // build a map for all the columns
52230         var cs = [];
52231         for(var i = 0; i < colCount; i++){
52232             var name = cm.getDataIndex(i);
52233             cs[i] = {
52234                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52235                 renderer : cm.getRenderer(i),
52236                 id : cm.getColumnId(i),
52237                 locked : cm.isLocked(i)
52238             };
52239         }
52240
52241         startRow = startRow || 0;
52242         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52243
52244         // records to render
52245         var rs = ds.getRange(startRow, endRow);
52246
52247         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52248     },
52249
52250     // As much as I hate to duplicate code, this was branched because FireFox really hates
52251     // [].join("") on strings. The performance difference was substantial enough to
52252     // branch this function
52253     doRender : Roo.isGecko ?
52254             function(cs, rs, ds, startRow, colCount, stripe){
52255                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52256                 // buffers
52257                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52258                 
52259                 var hasListener = this.grid.hasListener('rowclass');
52260                 var rowcfg = {};
52261                 for(var j = 0, len = rs.length; j < len; j++){
52262                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52263                     for(var i = 0; i < colCount; i++){
52264                         c = cs[i];
52265                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52266                         p.id = c.id;
52267                         p.css = p.attr = "";
52268                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52269                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52270                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52271                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52272                         }
52273                         var markup = ct.apply(p);
52274                         if(!c.locked){
52275                             cb+= markup;
52276                         }else{
52277                             lcb+= markup;
52278                         }
52279                     }
52280                     var alt = [];
52281                     if(stripe && ((rowIndex+1) % 2 == 0)){
52282                         alt.push("x-grid-row-alt")
52283                     }
52284                     if(r.dirty){
52285                         alt.push(  " x-grid-dirty-row");
52286                     }
52287                     rp.cells = lcb;
52288                     if(this.getRowClass){
52289                         alt.push(this.getRowClass(r, rowIndex));
52290                     }
52291                     if (hasListener) {
52292                         rowcfg = {
52293                              
52294                             record: r,
52295                             rowIndex : rowIndex,
52296                             rowClass : ''
52297                         }
52298                         this.grid.fireEvent('rowclass', this, rowcfg);
52299                         alt.push(rowcfg.rowClass);
52300                     }
52301                     rp.alt = alt.join(" ");
52302                     lbuf+= rt.apply(rp);
52303                     rp.cells = cb;
52304                     buf+=  rt.apply(rp);
52305                 }
52306                 return [lbuf, buf];
52307             } :
52308             function(cs, rs, ds, startRow, colCount, stripe){
52309                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52310                 // buffers
52311                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52312                 var hasListener = this.grid.hasListener('rowclass');
52313  
52314                 var rowcfg = {};
52315                 for(var j = 0, len = rs.length; j < len; j++){
52316                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52317                     for(var i = 0; i < colCount; i++){
52318                         c = cs[i];
52319                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52320                         p.id = c.id;
52321                         p.css = p.attr = "";
52322                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52323                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52324                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52325                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52326                         }
52327                         
52328                         var markup = ct.apply(p);
52329                         if(!c.locked){
52330                             cb[cb.length] = markup;
52331                         }else{
52332                             lcb[lcb.length] = markup;
52333                         }
52334                     }
52335                     var alt = [];
52336                     if(stripe && ((rowIndex+1) % 2 == 0)){
52337                         alt.push( "x-grid-row-alt");
52338                     }
52339                     if(r.dirty){
52340                         alt.push(" x-grid-dirty-row");
52341                     }
52342                     rp.cells = lcb;
52343                     if(this.getRowClass){
52344                         alt.push( this.getRowClass(r, rowIndex));
52345                     }
52346                     if (hasListener) {
52347                         rowcfg = {
52348                              
52349                             record: r,
52350                             rowIndex : rowIndex,
52351                             rowClass : ''
52352                         }
52353                         this.grid.fireEvent('rowclass', this, rowcfg);
52354                         alt.push(rowcfg.rowClass);
52355                     }
52356                     rp.alt = alt.join(" ");
52357                     rp.cells = lcb.join("");
52358                     lbuf[lbuf.length] = rt.apply(rp);
52359                     rp.cells = cb.join("");
52360                     buf[buf.length] =  rt.apply(rp);
52361                 }
52362                 return [lbuf.join(""), buf.join("")];
52363             },
52364
52365     renderBody : function(){
52366         var markup = this.renderRows();
52367         var bt = this.templates.body;
52368         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
52369     },
52370
52371     /**
52372      * Refreshes the grid
52373      * @param {Boolean} headersToo
52374      */
52375     refresh : function(headersToo){
52376         this.fireEvent("beforerefresh", this);
52377         this.grid.stopEditing();
52378         var result = this.renderBody();
52379         this.lockedBody.update(result[0]);
52380         this.mainBody.update(result[1]);
52381         if(headersToo === true){
52382             this.updateHeaders();
52383             this.updateColumns();
52384             this.updateSplitters();
52385             this.updateHeaderSortState();
52386         }
52387         this.syncRowHeights();
52388         this.layout();
52389         this.fireEvent("refresh", this);
52390     },
52391
52392     handleColumnMove : function(cm, oldIndex, newIndex){
52393         this.indexMap = null;
52394         var s = this.getScrollState();
52395         this.refresh(true);
52396         this.restoreScroll(s);
52397         this.afterMove(newIndex);
52398     },
52399
52400     afterMove : function(colIndex){
52401         if(this.enableMoveAnim && Roo.enableFx){
52402             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
52403         }
52404         // if multisort - fix sortOrder, and reload..
52405         if (this.grid.dataSource.multiSort) {
52406             // the we can call sort again..
52407             var dm = this.grid.dataSource;
52408             var cm = this.grid.colModel;
52409             var so = [];
52410             for(var i = 0; i < cm.config.length; i++ ) {
52411                 
52412                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
52413                     continue; // dont' bother, it's not in sort list or being set.
52414                 }
52415                 
52416                 so.push(cm.config[i].dataIndex);
52417             };
52418             dm.sortOrder = so;
52419             dm.load(dm.lastOptions);
52420             
52421             
52422         }
52423         
52424     },
52425
52426     updateCell : function(dm, rowIndex, dataIndex){
52427         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
52428         if(typeof colIndex == "undefined"){ // not present in grid
52429             return;
52430         }
52431         var cm = this.grid.colModel;
52432         var cell = this.getCell(rowIndex, colIndex);
52433         var cellText = this.getCellText(rowIndex, colIndex);
52434
52435         var p = {
52436             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
52437             id : cm.getColumnId(colIndex),
52438             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
52439         };
52440         var renderer = cm.getRenderer(colIndex);
52441         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
52442         if(typeof val == "undefined" || val === "") val = "&#160;";
52443         cellText.innerHTML = val;
52444         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
52445         this.syncRowHeights(rowIndex, rowIndex);
52446     },
52447
52448     calcColumnWidth : function(colIndex, maxRowsToMeasure){
52449         var maxWidth = 0;
52450         if(this.grid.autoSizeHeaders){
52451             var h = this.getHeaderCellMeasure(colIndex);
52452             maxWidth = Math.max(maxWidth, h.scrollWidth);
52453         }
52454         var tb, index;
52455         if(this.cm.isLocked(colIndex)){
52456             tb = this.getLockedTable();
52457             index = colIndex;
52458         }else{
52459             tb = this.getBodyTable();
52460             index = colIndex - this.cm.getLockedCount();
52461         }
52462         if(tb && tb.rows){
52463             var rows = tb.rows;
52464             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
52465             for(var i = 0; i < stopIndex; i++){
52466                 var cell = rows[i].childNodes[index].firstChild;
52467                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
52468             }
52469         }
52470         return maxWidth + /*margin for error in IE*/ 5;
52471     },
52472     /**
52473      * Autofit a column to its content.
52474      * @param {Number} colIndex
52475      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
52476      */
52477      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
52478          if(this.cm.isHidden(colIndex)){
52479              return; // can't calc a hidden column
52480          }
52481         if(forceMinSize){
52482             var cid = this.cm.getColumnId(colIndex);
52483             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
52484            if(this.grid.autoSizeHeaders){
52485                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
52486            }
52487         }
52488         var newWidth = this.calcColumnWidth(colIndex);
52489         this.cm.setColumnWidth(colIndex,
52490             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
52491         if(!suppressEvent){
52492             this.grid.fireEvent("columnresize", colIndex, newWidth);
52493         }
52494     },
52495
52496     /**
52497      * Autofits all columns to their content and then expands to fit any extra space in the grid
52498      */
52499      autoSizeColumns : function(){
52500         var cm = this.grid.colModel;
52501         var colCount = cm.getColumnCount();
52502         for(var i = 0; i < colCount; i++){
52503             this.autoSizeColumn(i, true, true);
52504         }
52505         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
52506             this.fitColumns();
52507         }else{
52508             this.updateColumns();
52509             this.layout();
52510         }
52511     },
52512
52513     /**
52514      * Autofits all columns to the grid's width proportionate with their current size
52515      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
52516      */
52517     fitColumns : function(reserveScrollSpace){
52518         var cm = this.grid.colModel;
52519         var colCount = cm.getColumnCount();
52520         var cols = [];
52521         var width = 0;
52522         var i, w;
52523         for (i = 0; i < colCount; i++){
52524             if(!cm.isHidden(i) && !cm.isFixed(i)){
52525                 w = cm.getColumnWidth(i);
52526                 cols.push(i);
52527                 cols.push(w);
52528                 width += w;
52529             }
52530         }
52531         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
52532         if(reserveScrollSpace){
52533             avail -= 17;
52534         }
52535         var frac = (avail - cm.getTotalWidth())/width;
52536         while (cols.length){
52537             w = cols.pop();
52538             i = cols.pop();
52539             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
52540         }
52541         this.updateColumns();
52542         this.layout();
52543     },
52544
52545     onRowSelect : function(rowIndex){
52546         var row = this.getRowComposite(rowIndex);
52547         row.addClass("x-grid-row-selected");
52548     },
52549
52550     onRowDeselect : function(rowIndex){
52551         var row = this.getRowComposite(rowIndex);
52552         row.removeClass("x-grid-row-selected");
52553     },
52554
52555     onCellSelect : function(row, col){
52556         var cell = this.getCell(row, col);
52557         if(cell){
52558             Roo.fly(cell).addClass("x-grid-cell-selected");
52559         }
52560     },
52561
52562     onCellDeselect : function(row, col){
52563         var cell = this.getCell(row, col);
52564         if(cell){
52565             Roo.fly(cell).removeClass("x-grid-cell-selected");
52566         }
52567     },
52568
52569     updateHeaderSortState : function(){
52570         
52571         // sort state can be single { field: xxx, direction : yyy}
52572         // or   { xxx=>ASC , yyy : DESC ..... }
52573         
52574         var mstate = {};
52575         if (!this.ds.multiSort) { 
52576             var state = this.ds.getSortState();
52577             if(!state){
52578                 return;
52579             }
52580             mstate[state.field] = state.direction;
52581             // FIXME... - this is not used here.. but might be elsewhere..
52582             this.sortState = state;
52583             
52584         } else {
52585             mstate = this.ds.sortToggle;
52586         }
52587         //remove existing sort classes..
52588         
52589         var sc = this.sortClasses;
52590         var hds = this.el.select(this.headerSelector).removeClass(sc);
52591         
52592         for(var f in mstate) {
52593         
52594             var sortColumn = this.cm.findColumnIndex(f);
52595             
52596             if(sortColumn != -1){
52597                 var sortDir = mstate[f];        
52598                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
52599             }
52600         }
52601         
52602          
52603         
52604     },
52605
52606
52607     handleHeaderClick : function(g, index){
52608         if(this.headersDisabled){
52609             return;
52610         }
52611         var dm = g.dataSource, cm = g.colModel;
52612         if(!cm.isSortable(index)){
52613             return;
52614         }
52615         g.stopEditing();
52616         
52617         if (dm.multiSort) {
52618             // update the sortOrder
52619             var so = [];
52620             for(var i = 0; i < cm.config.length; i++ ) {
52621                 
52622                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
52623                     continue; // dont' bother, it's not in sort list or being set.
52624                 }
52625                 
52626                 so.push(cm.config[i].dataIndex);
52627             };
52628             dm.sortOrder = so;
52629         }
52630         
52631         
52632         dm.sort(cm.getDataIndex(index));
52633     },
52634
52635
52636     destroy : function(){
52637         if(this.colMenu){
52638             this.colMenu.removeAll();
52639             Roo.menu.MenuMgr.unregister(this.colMenu);
52640             this.colMenu.getEl().remove();
52641             delete this.colMenu;
52642         }
52643         if(this.hmenu){
52644             this.hmenu.removeAll();
52645             Roo.menu.MenuMgr.unregister(this.hmenu);
52646             this.hmenu.getEl().remove();
52647             delete this.hmenu;
52648         }
52649         if(this.grid.enableColumnMove){
52650             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
52651             if(dds){
52652                 for(var dd in dds){
52653                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
52654                         var elid = dds[dd].dragElId;
52655                         dds[dd].unreg();
52656                         Roo.get(elid).remove();
52657                     } else if(dds[dd].config.isTarget){
52658                         dds[dd].proxyTop.remove();
52659                         dds[dd].proxyBottom.remove();
52660                         dds[dd].unreg();
52661                     }
52662                     if(Roo.dd.DDM.locationCache[dd]){
52663                         delete Roo.dd.DDM.locationCache[dd];
52664                     }
52665                 }
52666                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
52667             }
52668         }
52669         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
52670         this.bind(null, null);
52671         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
52672     },
52673
52674     handleLockChange : function(){
52675         this.refresh(true);
52676     },
52677
52678     onDenyColumnLock : function(){
52679
52680     },
52681
52682     onDenyColumnHide : function(){
52683
52684     },
52685
52686     handleHdMenuClick : function(item){
52687         var index = this.hdCtxIndex;
52688         var cm = this.cm, ds = this.ds;
52689         switch(item.id){
52690             case "asc":
52691                 ds.sort(cm.getDataIndex(index), "ASC");
52692                 break;
52693             case "desc":
52694                 ds.sort(cm.getDataIndex(index), "DESC");
52695                 break;
52696             case "lock":
52697                 var lc = cm.getLockedCount();
52698                 if(cm.getColumnCount(true) <= lc+1){
52699                     this.onDenyColumnLock();
52700                     return;
52701                 }
52702                 if(lc != index){
52703                     cm.setLocked(index, true, true);
52704                     cm.moveColumn(index, lc);
52705                     this.grid.fireEvent("columnmove", index, lc);
52706                 }else{
52707                     cm.setLocked(index, true);
52708                 }
52709             break;
52710             case "unlock":
52711                 var lc = cm.getLockedCount();
52712                 if((lc-1) != index){
52713                     cm.setLocked(index, false, true);
52714                     cm.moveColumn(index, lc-1);
52715                     this.grid.fireEvent("columnmove", index, lc-1);
52716                 }else{
52717                     cm.setLocked(index, false);
52718                 }
52719             break;
52720             default:
52721                 index = cm.getIndexById(item.id.substr(4));
52722                 if(index != -1){
52723                     if(item.checked && cm.getColumnCount(true) <= 1){
52724                         this.onDenyColumnHide();
52725                         return false;
52726                     }
52727                     cm.setHidden(index, item.checked);
52728                 }
52729         }
52730         return true;
52731     },
52732
52733     beforeColMenuShow : function(){
52734         var cm = this.cm,  colCount = cm.getColumnCount();
52735         this.colMenu.removeAll();
52736         for(var i = 0; i < colCount; i++){
52737             this.colMenu.add(new Roo.menu.CheckItem({
52738                 id: "col-"+cm.getColumnId(i),
52739                 text: cm.getColumnHeader(i),
52740                 checked: !cm.isHidden(i),
52741                 hideOnClick:false
52742             }));
52743         }
52744     },
52745
52746     handleHdCtx : function(g, index, e){
52747         e.stopEvent();
52748         var hd = this.getHeaderCell(index);
52749         this.hdCtxIndex = index;
52750         var ms = this.hmenu.items, cm = this.cm;
52751         ms.get("asc").setDisabled(!cm.isSortable(index));
52752         ms.get("desc").setDisabled(!cm.isSortable(index));
52753         if(this.grid.enableColLock !== false){
52754             ms.get("lock").setDisabled(cm.isLocked(index));
52755             ms.get("unlock").setDisabled(!cm.isLocked(index));
52756         }
52757         this.hmenu.show(hd, "tl-bl");
52758     },
52759
52760     handleHdOver : function(e){
52761         var hd = this.findHeaderCell(e.getTarget());
52762         if(hd && !this.headersDisabled){
52763             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
52764                this.fly(hd).addClass("x-grid-hd-over");
52765             }
52766         }
52767     },
52768
52769     handleHdOut : function(e){
52770         var hd = this.findHeaderCell(e.getTarget());
52771         if(hd){
52772             this.fly(hd).removeClass("x-grid-hd-over");
52773         }
52774     },
52775
52776     handleSplitDblClick : function(e, t){
52777         var i = this.getCellIndex(t);
52778         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
52779             this.autoSizeColumn(i, true);
52780             this.layout();
52781         }
52782     },
52783
52784     render : function(){
52785
52786         var cm = this.cm;
52787         var colCount = cm.getColumnCount();
52788
52789         if(this.grid.monitorWindowResize === true){
52790             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52791         }
52792         var header = this.renderHeaders();
52793         var body = this.templates.body.apply({rows:""});
52794         var html = this.templates.master.apply({
52795             lockedBody: body,
52796             body: body,
52797             lockedHeader: header[0],
52798             header: header[1]
52799         });
52800
52801         //this.updateColumns();
52802
52803         this.grid.getGridEl().dom.innerHTML = html;
52804
52805         this.initElements();
52806         
52807         // a kludge to fix the random scolling effect in webkit
52808         this.el.on("scroll", function() {
52809             this.el.dom.scrollTop=0; // hopefully not recursive..
52810         },this);
52811
52812         this.scroller.on("scroll", this.handleScroll, this);
52813         this.lockedBody.on("mousewheel", this.handleWheel, this);
52814         this.mainBody.on("mousewheel", this.handleWheel, this);
52815
52816         this.mainHd.on("mouseover", this.handleHdOver, this);
52817         this.mainHd.on("mouseout", this.handleHdOut, this);
52818         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
52819                 {delegate: "."+this.splitClass});
52820
52821         this.lockedHd.on("mouseover", this.handleHdOver, this);
52822         this.lockedHd.on("mouseout", this.handleHdOut, this);
52823         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
52824                 {delegate: "."+this.splitClass});
52825
52826         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
52827             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52828         }
52829
52830         this.updateSplitters();
52831
52832         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
52833             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52834             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52835         }
52836
52837         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
52838             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
52839             this.hmenu.add(
52840                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
52841                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
52842             );
52843             if(this.grid.enableColLock !== false){
52844                 this.hmenu.add('-',
52845                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
52846                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
52847                 );
52848             }
52849             if(this.grid.enableColumnHide !== false){
52850
52851                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
52852                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
52853                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
52854
52855                 this.hmenu.add('-',
52856                     {id:"columns", text: this.columnsText, menu: this.colMenu}
52857                 );
52858             }
52859             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
52860
52861             this.grid.on("headercontextmenu", this.handleHdCtx, this);
52862         }
52863
52864         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
52865             this.dd = new Roo.grid.GridDragZone(this.grid, {
52866                 ddGroup : this.grid.ddGroup || 'GridDD'
52867             });
52868             
52869         }
52870
52871         /*
52872         for(var i = 0; i < colCount; i++){
52873             if(cm.isHidden(i)){
52874                 this.hideColumn(i);
52875             }
52876             if(cm.config[i].align){
52877                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
52878                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
52879             }
52880         }*/
52881         
52882         this.updateHeaderSortState();
52883
52884         this.beforeInitialResize();
52885         this.layout(true);
52886
52887         // two part rendering gives faster view to the user
52888         this.renderPhase2.defer(1, this);
52889     },
52890
52891     renderPhase2 : function(){
52892         // render the rows now
52893         this.refresh();
52894         if(this.grid.autoSizeColumns){
52895             this.autoSizeColumns();
52896         }
52897     },
52898
52899     beforeInitialResize : function(){
52900
52901     },
52902
52903     onColumnSplitterMoved : function(i, w){
52904         this.userResized = true;
52905         var cm = this.grid.colModel;
52906         cm.setColumnWidth(i, w, true);
52907         var cid = cm.getColumnId(i);
52908         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
52909         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
52910         this.updateSplitters();
52911         this.layout();
52912         this.grid.fireEvent("columnresize", i, w);
52913     },
52914
52915     syncRowHeights : function(startIndex, endIndex){
52916         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
52917             startIndex = startIndex || 0;
52918             var mrows = this.getBodyTable().rows;
52919             var lrows = this.getLockedTable().rows;
52920             var len = mrows.length-1;
52921             endIndex = Math.min(endIndex || len, len);
52922             for(var i = startIndex; i <= endIndex; i++){
52923                 var m = mrows[i], l = lrows[i];
52924                 var h = Math.max(m.offsetHeight, l.offsetHeight);
52925                 m.style.height = l.style.height = h + "px";
52926             }
52927         }
52928     },
52929
52930     layout : function(initialRender, is2ndPass){
52931         var g = this.grid;
52932         var auto = g.autoHeight;
52933         var scrollOffset = 16;
52934         var c = g.getGridEl(), cm = this.cm,
52935                 expandCol = g.autoExpandColumn,
52936                 gv = this;
52937         //c.beginMeasure();
52938
52939         if(!c.dom.offsetWidth){ // display:none?
52940             if(initialRender){
52941                 this.lockedWrap.show();
52942                 this.mainWrap.show();
52943             }
52944             return;
52945         }
52946
52947         var hasLock = this.cm.isLocked(0);
52948
52949         var tbh = this.headerPanel.getHeight();
52950         var bbh = this.footerPanel.getHeight();
52951
52952         if(auto){
52953             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
52954             var newHeight = ch + c.getBorderWidth("tb");
52955             if(g.maxHeight){
52956                 newHeight = Math.min(g.maxHeight, newHeight);
52957             }
52958             c.setHeight(newHeight);
52959         }
52960
52961         if(g.autoWidth){
52962             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
52963         }
52964
52965         var s = this.scroller;
52966
52967         var csize = c.getSize(true);
52968
52969         this.el.setSize(csize.width, csize.height);
52970
52971         this.headerPanel.setWidth(csize.width);
52972         this.footerPanel.setWidth(csize.width);
52973
52974         var hdHeight = this.mainHd.getHeight();
52975         var vw = csize.width;
52976         var vh = csize.height - (tbh + bbh);
52977
52978         s.setSize(vw, vh);
52979
52980         var bt = this.getBodyTable();
52981         var ltWidth = hasLock ?
52982                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
52983
52984         var scrollHeight = bt.offsetHeight;
52985         var scrollWidth = ltWidth + bt.offsetWidth;
52986         var vscroll = false, hscroll = false;
52987
52988         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
52989
52990         var lw = this.lockedWrap, mw = this.mainWrap;
52991         var lb = this.lockedBody, mb = this.mainBody;
52992
52993         setTimeout(function(){
52994             var t = s.dom.offsetTop;
52995             var w = s.dom.clientWidth,
52996                 h = s.dom.clientHeight;
52997
52998             lw.setTop(t);
52999             lw.setSize(ltWidth, h);
53000
53001             mw.setLeftTop(ltWidth, t);
53002             mw.setSize(w-ltWidth, h);
53003
53004             lb.setHeight(h-hdHeight);
53005             mb.setHeight(h-hdHeight);
53006
53007             if(is2ndPass !== true && !gv.userResized && expandCol){
53008                 // high speed resize without full column calculation
53009                 
53010                 var ci = cm.getIndexById(expandCol);
53011                 if (ci < 0) {
53012                     ci = cm.findColumnIndex(expandCol);
53013                 }
53014                 ci = Math.max(0, ci); // make sure it's got at least the first col.
53015                 var expandId = cm.getColumnId(ci);
53016                 var  tw = cm.getTotalWidth(false);
53017                 var currentWidth = cm.getColumnWidth(ci);
53018                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
53019                 if(currentWidth != cw){
53020                     cm.setColumnWidth(ci, cw, true);
53021                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53022                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53023                     gv.updateSplitters();
53024                     gv.layout(false, true);
53025                 }
53026             }
53027
53028             if(initialRender){
53029                 lw.show();
53030                 mw.show();
53031             }
53032             //c.endMeasure();
53033         }, 10);
53034     },
53035
53036     onWindowResize : function(){
53037         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
53038             return;
53039         }
53040         this.layout();
53041     },
53042
53043     appendFooter : function(parentEl){
53044         return null;
53045     },
53046
53047     sortAscText : "Sort Ascending",
53048     sortDescText : "Sort Descending",
53049     lockText : "Lock Column",
53050     unlockText : "Unlock Column",
53051     columnsText : "Columns"
53052 });
53053
53054
53055 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
53056     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
53057     this.proxy.el.addClass('x-grid3-col-dd');
53058 };
53059
53060 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
53061     handleMouseDown : function(e){
53062
53063     },
53064
53065     callHandleMouseDown : function(e){
53066         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53067     }
53068 });
53069 /*
53070  * Based on:
53071  * Ext JS Library 1.1.1
53072  * Copyright(c) 2006-2007, Ext JS, LLC.
53073  *
53074  * Originally Released Under LGPL - original licence link has changed is not relivant.
53075  *
53076  * Fork - LGPL
53077  * <script type="text/javascript">
53078  */
53079  
53080 // private
53081 // This is a support class used internally by the Grid components
53082 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53083     this.grid = grid;
53084     this.view = grid.getView();
53085     this.proxy = this.view.resizeProxy;
53086     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53087         "gridSplitters" + this.grid.getGridEl().id, {
53088         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53089     });
53090     this.setHandleElId(Roo.id(hd));
53091     this.setOuterHandleElId(Roo.id(hd2));
53092     this.scroll = false;
53093 };
53094 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53095     fly: Roo.Element.fly,
53096
53097     b4StartDrag : function(x, y){
53098         this.view.headersDisabled = true;
53099         this.proxy.setHeight(this.view.mainWrap.getHeight());
53100         var w = this.cm.getColumnWidth(this.cellIndex);
53101         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53102         this.resetConstraints();
53103         this.setXConstraint(minw, 1000);
53104         this.setYConstraint(0, 0);
53105         this.minX = x - minw;
53106         this.maxX = x + 1000;
53107         this.startPos = x;
53108         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53109     },
53110
53111
53112     handleMouseDown : function(e){
53113         ev = Roo.EventObject.setEvent(e);
53114         var t = this.fly(ev.getTarget());
53115         if(t.hasClass("x-grid-split")){
53116             this.cellIndex = this.view.getCellIndex(t.dom);
53117             this.split = t.dom;
53118             this.cm = this.grid.colModel;
53119             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53120                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53121             }
53122         }
53123     },
53124
53125     endDrag : function(e){
53126         this.view.headersDisabled = false;
53127         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53128         var diff = endX - this.startPos;
53129         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53130     },
53131
53132     autoOffset : function(){
53133         this.setDelta(0,0);
53134     }
53135 });/*
53136  * Based on:
53137  * Ext JS Library 1.1.1
53138  * Copyright(c) 2006-2007, Ext JS, LLC.
53139  *
53140  * Originally Released Under LGPL - original licence link has changed is not relivant.
53141  *
53142  * Fork - LGPL
53143  * <script type="text/javascript">
53144  */
53145  
53146 // private
53147 // This is a support class used internally by the Grid components
53148 Roo.grid.GridDragZone = function(grid, config){
53149     this.view = grid.getView();
53150     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53151     if(this.view.lockedBody){
53152         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53153         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53154     }
53155     this.scroll = false;
53156     this.grid = grid;
53157     this.ddel = document.createElement('div');
53158     this.ddel.className = 'x-grid-dd-wrap';
53159 };
53160
53161 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53162     ddGroup : "GridDD",
53163
53164     getDragData : function(e){
53165         var t = Roo.lib.Event.getTarget(e);
53166         var rowIndex = this.view.findRowIndex(t);
53167         var sm = this.grid.selModel;
53168             
53169         //Roo.log(rowIndex);
53170         
53171         if (sm.getSelectedCell) {
53172             // cell selection..
53173             if (!sm.getSelectedCell()) {
53174                 return false;
53175             }
53176             if (rowIndex != sm.getSelectedCell()[0]) {
53177                 return false;
53178             }
53179         
53180         }
53181         
53182         if(rowIndex !== false){
53183             
53184             // if editorgrid.. 
53185             
53186             
53187             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53188                
53189             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53190               //  
53191             //}
53192             if (e.hasModifier()){
53193                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53194             }
53195             
53196             Roo.log("getDragData");
53197             
53198             return {
53199                 grid: this.grid,
53200                 ddel: this.ddel,
53201                 rowIndex: rowIndex,
53202                 selections:sm.getSelections ? sm.getSelections() : (
53203                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53204                 )
53205             };
53206         }
53207         return false;
53208     },
53209
53210     onInitDrag : function(e){
53211         var data = this.dragData;
53212         this.ddel.innerHTML = this.grid.getDragDropText();
53213         this.proxy.update(this.ddel);
53214         // fire start drag?
53215     },
53216
53217     afterRepair : function(){
53218         this.dragging = false;
53219     },
53220
53221     getRepairXY : function(e, data){
53222         return false;
53223     },
53224
53225     onEndDrag : function(data, e){
53226         // fire end drag?
53227     },
53228
53229     onValidDrop : function(dd, e, id){
53230         // fire drag drop?
53231         this.hideProxy();
53232     },
53233
53234     beforeInvalidDrop : function(e, id){
53235
53236     }
53237 });/*
53238  * Based on:
53239  * Ext JS Library 1.1.1
53240  * Copyright(c) 2006-2007, Ext JS, LLC.
53241  *
53242  * Originally Released Under LGPL - original licence link has changed is not relivant.
53243  *
53244  * Fork - LGPL
53245  * <script type="text/javascript">
53246  */
53247  
53248
53249 /**
53250  * @class Roo.grid.ColumnModel
53251  * @extends Roo.util.Observable
53252  * This is the default implementation of a ColumnModel used by the Grid. It defines
53253  * the columns in the grid.
53254  * <br>Usage:<br>
53255  <pre><code>
53256  var colModel = new Roo.grid.ColumnModel([
53257         {header: "Ticker", width: 60, sortable: true, locked: true},
53258         {header: "Company Name", width: 150, sortable: true},
53259         {header: "Market Cap.", width: 100, sortable: true},
53260         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53261         {header: "Employees", width: 100, sortable: true, resizable: false}
53262  ]);
53263  </code></pre>
53264  * <p>
53265  
53266  * The config options listed for this class are options which may appear in each
53267  * individual column definition.
53268  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53269  * @constructor
53270  * @param {Object} config An Array of column config objects. See this class's
53271  * config objects for details.
53272 */
53273 Roo.grid.ColumnModel = function(config){
53274         /**
53275      * The config passed into the constructor
53276      */
53277     this.config = config;
53278     this.lookup = {};
53279
53280     // if no id, create one
53281     // if the column does not have a dataIndex mapping,
53282     // map it to the order it is in the config
53283     for(var i = 0, len = config.length; i < len; i++){
53284         var c = config[i];
53285         if(typeof c.dataIndex == "undefined"){
53286             c.dataIndex = i;
53287         }
53288         if(typeof c.renderer == "string"){
53289             c.renderer = Roo.util.Format[c.renderer];
53290         }
53291         if(typeof c.id == "undefined"){
53292             c.id = Roo.id();
53293         }
53294         if(c.editor && c.editor.xtype){
53295             c.editor  = Roo.factory(c.editor, Roo.grid);
53296         }
53297         if(c.editor && c.editor.isFormField){
53298             c.editor = new Roo.grid.GridEditor(c.editor);
53299         }
53300         this.lookup[c.id] = c;
53301     }
53302
53303     /**
53304      * The width of columns which have no width specified (defaults to 100)
53305      * @type Number
53306      */
53307     this.defaultWidth = 100;
53308
53309     /**
53310      * Default sortable of columns which have no sortable specified (defaults to false)
53311      * @type Boolean
53312      */
53313     this.defaultSortable = false;
53314
53315     this.addEvents({
53316         /**
53317              * @event widthchange
53318              * Fires when the width of a column changes.
53319              * @param {ColumnModel} this
53320              * @param {Number} columnIndex The column index
53321              * @param {Number} newWidth The new width
53322              */
53323             "widthchange": true,
53324         /**
53325              * @event headerchange
53326              * Fires when the text of a header changes.
53327              * @param {ColumnModel} this
53328              * @param {Number} columnIndex The column index
53329              * @param {Number} newText The new header text
53330              */
53331             "headerchange": true,
53332         /**
53333              * @event hiddenchange
53334              * Fires when a column is hidden or "unhidden".
53335              * @param {ColumnModel} this
53336              * @param {Number} columnIndex The column index
53337              * @param {Boolean} hidden true if hidden, false otherwise
53338              */
53339             "hiddenchange": true,
53340             /**
53341          * @event columnmoved
53342          * Fires when a column is moved.
53343          * @param {ColumnModel} this
53344          * @param {Number} oldIndex
53345          * @param {Number} newIndex
53346          */
53347         "columnmoved" : true,
53348         /**
53349          * @event columlockchange
53350          * Fires when a column's locked state is changed
53351          * @param {ColumnModel} this
53352          * @param {Number} colIndex
53353          * @param {Boolean} locked true if locked
53354          */
53355         "columnlockchange" : true
53356     });
53357     Roo.grid.ColumnModel.superclass.constructor.call(this);
53358 };
53359 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
53360     /**
53361      * @cfg {String} header The header text to display in the Grid view.
53362      */
53363     /**
53364      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
53365      * {@link Roo.data.Record} definition from which to draw the column's value. If not
53366      * specified, the column's index is used as an index into the Record's data Array.
53367      */
53368     /**
53369      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
53370      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
53371      */
53372     /**
53373      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
53374      * Defaults to the value of the {@link #defaultSortable} property.
53375      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
53376      */
53377     /**
53378      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
53379      */
53380     /**
53381      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
53382      */
53383     /**
53384      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
53385      */
53386     /**
53387      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
53388      */
53389     /**
53390      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
53391      * given the cell's data value. See {@link #setRenderer}. If not specified, the
53392      * default renderer uses the raw data value.
53393      */
53394        /**
53395      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
53396      */
53397     /**
53398      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
53399      */
53400
53401     /**
53402      * Returns the id of the column at the specified index.
53403      * @param {Number} index The column index
53404      * @return {String} the id
53405      */
53406     getColumnId : function(index){
53407         return this.config[index].id;
53408     },
53409
53410     /**
53411      * Returns the column for a specified id.
53412      * @param {String} id The column id
53413      * @return {Object} the column
53414      */
53415     getColumnById : function(id){
53416         return this.lookup[id];
53417     },
53418
53419     
53420     /**
53421      * Returns the column for a specified dataIndex.
53422      * @param {String} dataIndex The column dataIndex
53423      * @return {Object|Boolean} the column or false if not found
53424      */
53425     getColumnByDataIndex: function(dataIndex){
53426         var index = this.findColumnIndex(dataIndex);
53427         return index > -1 ? this.config[index] : false;
53428     },
53429     
53430     /**
53431      * Returns the index for a specified column id.
53432      * @param {String} id The column id
53433      * @return {Number} the index, or -1 if not found
53434      */
53435     getIndexById : function(id){
53436         for(var i = 0, len = this.config.length; i < len; i++){
53437             if(this.config[i].id == id){
53438                 return i;
53439             }
53440         }
53441         return -1;
53442     },
53443     
53444     /**
53445      * Returns the index for a specified column dataIndex.
53446      * @param {String} dataIndex The column dataIndex
53447      * @return {Number} the index, or -1 if not found
53448      */
53449     
53450     findColumnIndex : function(dataIndex){
53451         for(var i = 0, len = this.config.length; i < len; i++){
53452             if(this.config[i].dataIndex == dataIndex){
53453                 return i;
53454             }
53455         }
53456         return -1;
53457     },
53458     
53459     
53460     moveColumn : function(oldIndex, newIndex){
53461         var c = this.config[oldIndex];
53462         this.config.splice(oldIndex, 1);
53463         this.config.splice(newIndex, 0, c);
53464         this.dataMap = null;
53465         this.fireEvent("columnmoved", this, oldIndex, newIndex);
53466     },
53467
53468     isLocked : function(colIndex){
53469         return this.config[colIndex].locked === true;
53470     },
53471
53472     setLocked : function(colIndex, value, suppressEvent){
53473         if(this.isLocked(colIndex) == value){
53474             return;
53475         }
53476         this.config[colIndex].locked = value;
53477         if(!suppressEvent){
53478             this.fireEvent("columnlockchange", this, colIndex, value);
53479         }
53480     },
53481
53482     getTotalLockedWidth : function(){
53483         var totalWidth = 0;
53484         for(var i = 0; i < this.config.length; i++){
53485             if(this.isLocked(i) && !this.isHidden(i)){
53486                 this.totalWidth += this.getColumnWidth(i);
53487             }
53488         }
53489         return totalWidth;
53490     },
53491
53492     getLockedCount : function(){
53493         for(var i = 0, len = this.config.length; i < len; i++){
53494             if(!this.isLocked(i)){
53495                 return i;
53496             }
53497         }
53498     },
53499
53500     /**
53501      * Returns the number of columns.
53502      * @return {Number}
53503      */
53504     getColumnCount : function(visibleOnly){
53505         if(visibleOnly === true){
53506             var c = 0;
53507             for(var i = 0, len = this.config.length; i < len; i++){
53508                 if(!this.isHidden(i)){
53509                     c++;
53510                 }
53511             }
53512             return c;
53513         }
53514         return this.config.length;
53515     },
53516
53517     /**
53518      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
53519      * @param {Function} fn
53520      * @param {Object} scope (optional)
53521      * @return {Array} result
53522      */
53523     getColumnsBy : function(fn, scope){
53524         var r = [];
53525         for(var i = 0, len = this.config.length; i < len; i++){
53526             var c = this.config[i];
53527             if(fn.call(scope||this, c, i) === true){
53528                 r[r.length] = c;
53529             }
53530         }
53531         return r;
53532     },
53533
53534     /**
53535      * Returns true if the specified column is sortable.
53536      * @param {Number} col The column index
53537      * @return {Boolean}
53538      */
53539     isSortable : function(col){
53540         if(typeof this.config[col].sortable == "undefined"){
53541             return this.defaultSortable;
53542         }
53543         return this.config[col].sortable;
53544     },
53545
53546     /**
53547      * Returns the rendering (formatting) function defined for the column.
53548      * @param {Number} col The column index.
53549      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
53550      */
53551     getRenderer : function(col){
53552         if(!this.config[col].renderer){
53553             return Roo.grid.ColumnModel.defaultRenderer;
53554         }
53555         return this.config[col].renderer;
53556     },
53557
53558     /**
53559      * Sets the rendering (formatting) function for a column.
53560      * @param {Number} col The column index
53561      * @param {Function} fn The function to use to process the cell's raw data
53562      * to return HTML markup for the grid view. The render function is called with
53563      * the following parameters:<ul>
53564      * <li>Data value.</li>
53565      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
53566      * <li>css A CSS style string to apply to the table cell.</li>
53567      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
53568      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
53569      * <li>Row index</li>
53570      * <li>Column index</li>
53571      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
53572      */
53573     setRenderer : function(col, fn){
53574         this.config[col].renderer = fn;
53575     },
53576
53577     /**
53578      * Returns the width for the specified column.
53579      * @param {Number} col The column index
53580      * @return {Number}
53581      */
53582     getColumnWidth : function(col){
53583         return this.config[col].width * 1 || this.defaultWidth;
53584     },
53585
53586     /**
53587      * Sets the width for a column.
53588      * @param {Number} col The column index
53589      * @param {Number} width The new width
53590      */
53591     setColumnWidth : function(col, width, suppressEvent){
53592         this.config[col].width = width;
53593         this.totalWidth = null;
53594         if(!suppressEvent){
53595              this.fireEvent("widthchange", this, col, width);
53596         }
53597     },
53598
53599     /**
53600      * Returns the total width of all columns.
53601      * @param {Boolean} includeHidden True to include hidden column widths
53602      * @return {Number}
53603      */
53604     getTotalWidth : function(includeHidden){
53605         if(!this.totalWidth){
53606             this.totalWidth = 0;
53607             for(var i = 0, len = this.config.length; i < len; i++){
53608                 if(includeHidden || !this.isHidden(i)){
53609                     this.totalWidth += this.getColumnWidth(i);
53610                 }
53611             }
53612         }
53613         return this.totalWidth;
53614     },
53615
53616     /**
53617      * Returns the header for the specified column.
53618      * @param {Number} col The column index
53619      * @return {String}
53620      */
53621     getColumnHeader : function(col){
53622         return this.config[col].header;
53623     },
53624
53625     /**
53626      * Sets the header for a column.
53627      * @param {Number} col The column index
53628      * @param {String} header The new header
53629      */
53630     setColumnHeader : function(col, header){
53631         this.config[col].header = header;
53632         this.fireEvent("headerchange", this, col, header);
53633     },
53634
53635     /**
53636      * Returns the tooltip for the specified column.
53637      * @param {Number} col The column index
53638      * @return {String}
53639      */
53640     getColumnTooltip : function(col){
53641             return this.config[col].tooltip;
53642     },
53643     /**
53644      * Sets the tooltip for a column.
53645      * @param {Number} col The column index
53646      * @param {String} tooltip The new tooltip
53647      */
53648     setColumnTooltip : function(col, tooltip){
53649             this.config[col].tooltip = tooltip;
53650     },
53651
53652     /**
53653      * Returns the dataIndex for the specified column.
53654      * @param {Number} col The column index
53655      * @return {Number}
53656      */
53657     getDataIndex : function(col){
53658         return this.config[col].dataIndex;
53659     },
53660
53661     /**
53662      * Sets the dataIndex for a column.
53663      * @param {Number} col The column index
53664      * @param {Number} dataIndex The new dataIndex
53665      */
53666     setDataIndex : function(col, dataIndex){
53667         this.config[col].dataIndex = dataIndex;
53668     },
53669
53670     
53671     
53672     /**
53673      * Returns true if the cell is editable.
53674      * @param {Number} colIndex The column index
53675      * @param {Number} rowIndex The row index
53676      * @return {Boolean}
53677      */
53678     isCellEditable : function(colIndex, rowIndex){
53679         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
53680     },
53681
53682     /**
53683      * Returns the editor defined for the cell/column.
53684      * return false or null to disable editing.
53685      * @param {Number} colIndex The column index
53686      * @param {Number} rowIndex The row index
53687      * @return {Object}
53688      */
53689     getCellEditor : function(colIndex, rowIndex){
53690         return this.config[colIndex].editor;
53691     },
53692
53693     /**
53694      * Sets if a column is editable.
53695      * @param {Number} col The column index
53696      * @param {Boolean} editable True if the column is editable
53697      */
53698     setEditable : function(col, editable){
53699         this.config[col].editable = editable;
53700     },
53701
53702
53703     /**
53704      * Returns true if the column is hidden.
53705      * @param {Number} colIndex The column index
53706      * @return {Boolean}
53707      */
53708     isHidden : function(colIndex){
53709         return this.config[colIndex].hidden;
53710     },
53711
53712
53713     /**
53714      * Returns true if the column width cannot be changed
53715      */
53716     isFixed : function(colIndex){
53717         return this.config[colIndex].fixed;
53718     },
53719
53720     /**
53721      * Returns true if the column can be resized
53722      * @return {Boolean}
53723      */
53724     isResizable : function(colIndex){
53725         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
53726     },
53727     /**
53728      * Sets if a column is hidden.
53729      * @param {Number} colIndex The column index
53730      * @param {Boolean} hidden True if the column is hidden
53731      */
53732     setHidden : function(colIndex, hidden){
53733         this.config[colIndex].hidden = hidden;
53734         this.totalWidth = null;
53735         this.fireEvent("hiddenchange", this, colIndex, hidden);
53736     },
53737
53738     /**
53739      * Sets the editor for a column.
53740      * @param {Number} col The column index
53741      * @param {Object} editor The editor object
53742      */
53743     setEditor : function(col, editor){
53744         this.config[col].editor = editor;
53745     }
53746 });
53747
53748 Roo.grid.ColumnModel.defaultRenderer = function(value){
53749         if(typeof value == "string" && value.length < 1){
53750             return "&#160;";
53751         }
53752         return value;
53753 };
53754
53755 // Alias for backwards compatibility
53756 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
53757 /*
53758  * Based on:
53759  * Ext JS Library 1.1.1
53760  * Copyright(c) 2006-2007, Ext JS, LLC.
53761  *
53762  * Originally Released Under LGPL - original licence link has changed is not relivant.
53763  *
53764  * Fork - LGPL
53765  * <script type="text/javascript">
53766  */
53767
53768 /**
53769  * @class Roo.grid.AbstractSelectionModel
53770  * @extends Roo.util.Observable
53771  * Abstract base class for grid SelectionModels.  It provides the interface that should be
53772  * implemented by descendant classes.  This class should not be directly instantiated.
53773  * @constructor
53774  */
53775 Roo.grid.AbstractSelectionModel = function(){
53776     this.locked = false;
53777     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
53778 };
53779
53780 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
53781     /** @ignore Called by the grid automatically. Do not call directly. */
53782     init : function(grid){
53783         this.grid = grid;
53784         this.initEvents();
53785     },
53786
53787     /**
53788      * Locks the selections.
53789      */
53790     lock : function(){
53791         this.locked = true;
53792     },
53793
53794     /**
53795      * Unlocks the selections.
53796      */
53797     unlock : function(){
53798         this.locked = false;
53799     },
53800
53801     /**
53802      * Returns true if the selections are locked.
53803      * @return {Boolean}
53804      */
53805     isLocked : function(){
53806         return this.locked;
53807     }
53808 });/*
53809  * Based on:
53810  * Ext JS Library 1.1.1
53811  * Copyright(c) 2006-2007, Ext JS, LLC.
53812  *
53813  * Originally Released Under LGPL - original licence link has changed is not relivant.
53814  *
53815  * Fork - LGPL
53816  * <script type="text/javascript">
53817  */
53818 /**
53819  * @extends Roo.grid.AbstractSelectionModel
53820  * @class Roo.grid.RowSelectionModel
53821  * The default SelectionModel used by {@link Roo.grid.Grid}.
53822  * It supports multiple selections and keyboard selection/navigation. 
53823  * @constructor
53824  * @param {Object} config
53825  */
53826 Roo.grid.RowSelectionModel = function(config){
53827     Roo.apply(this, config);
53828     this.selections = new Roo.util.MixedCollection(false, function(o){
53829         return o.id;
53830     });
53831
53832     this.last = false;
53833     this.lastActive = false;
53834
53835     this.addEvents({
53836         /**
53837              * @event selectionchange
53838              * Fires when the selection changes
53839              * @param {SelectionModel} this
53840              */
53841             "selectionchange" : true,
53842         /**
53843              * @event afterselectionchange
53844              * Fires after the selection changes (eg. by key press or clicking)
53845              * @param {SelectionModel} this
53846              */
53847             "afterselectionchange" : true,
53848         /**
53849              * @event beforerowselect
53850              * Fires when a row is selected being selected, return false to cancel.
53851              * @param {SelectionModel} this
53852              * @param {Number} rowIndex The selected index
53853              * @param {Boolean} keepExisting False if other selections will be cleared
53854              */
53855             "beforerowselect" : true,
53856         /**
53857              * @event rowselect
53858              * Fires when a row is selected.
53859              * @param {SelectionModel} this
53860              * @param {Number} rowIndex The selected index
53861              * @param {Roo.data.Record} r The record
53862              */
53863             "rowselect" : true,
53864         /**
53865              * @event rowdeselect
53866              * Fires when a row is deselected.
53867              * @param {SelectionModel} this
53868              * @param {Number} rowIndex The selected index
53869              */
53870         "rowdeselect" : true
53871     });
53872     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
53873     this.locked = false;
53874 };
53875
53876 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
53877     /**
53878      * @cfg {Boolean} singleSelect
53879      * True to allow selection of only one row at a time (defaults to false)
53880      */
53881     singleSelect : false,
53882
53883     // private
53884     initEvents : function(){
53885
53886         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
53887             this.grid.on("mousedown", this.handleMouseDown, this);
53888         }else{ // allow click to work like normal
53889             this.grid.on("rowclick", this.handleDragableRowClick, this);
53890         }
53891
53892         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
53893             "up" : function(e){
53894                 if(!e.shiftKey){
53895                     this.selectPrevious(e.shiftKey);
53896                 }else if(this.last !== false && this.lastActive !== false){
53897                     var last = this.last;
53898                     this.selectRange(this.last,  this.lastActive-1);
53899                     this.grid.getView().focusRow(this.lastActive);
53900                     if(last !== false){
53901                         this.last = last;
53902                     }
53903                 }else{
53904                     this.selectFirstRow();
53905                 }
53906                 this.fireEvent("afterselectionchange", this);
53907             },
53908             "down" : function(e){
53909                 if(!e.shiftKey){
53910                     this.selectNext(e.shiftKey);
53911                 }else if(this.last !== false && this.lastActive !== false){
53912                     var last = this.last;
53913                     this.selectRange(this.last,  this.lastActive+1);
53914                     this.grid.getView().focusRow(this.lastActive);
53915                     if(last !== false){
53916                         this.last = last;
53917                     }
53918                 }else{
53919                     this.selectFirstRow();
53920                 }
53921                 this.fireEvent("afterselectionchange", this);
53922             },
53923             scope: this
53924         });
53925
53926         var view = this.grid.view;
53927         view.on("refresh", this.onRefresh, this);
53928         view.on("rowupdated", this.onRowUpdated, this);
53929         view.on("rowremoved", this.onRemove, this);
53930     },
53931
53932     // private
53933     onRefresh : function(){
53934         var ds = this.grid.dataSource, i, v = this.grid.view;
53935         var s = this.selections;
53936         s.each(function(r){
53937             if((i = ds.indexOfId(r.id)) != -1){
53938                 v.onRowSelect(i);
53939             }else{
53940                 s.remove(r);
53941             }
53942         });
53943     },
53944
53945     // private
53946     onRemove : function(v, index, r){
53947         this.selections.remove(r);
53948     },
53949
53950     // private
53951     onRowUpdated : function(v, index, r){
53952         if(this.isSelected(r)){
53953             v.onRowSelect(index);
53954         }
53955     },
53956
53957     /**
53958      * Select records.
53959      * @param {Array} records The records to select
53960      * @param {Boolean} keepExisting (optional) True to keep existing selections
53961      */
53962     selectRecords : function(records, keepExisting){
53963         if(!keepExisting){
53964             this.clearSelections();
53965         }
53966         var ds = this.grid.dataSource;
53967         for(var i = 0, len = records.length; i < len; i++){
53968             this.selectRow(ds.indexOf(records[i]), true);
53969         }
53970     },
53971
53972     /**
53973      * Gets the number of selected rows.
53974      * @return {Number}
53975      */
53976     getCount : function(){
53977         return this.selections.length;
53978     },
53979
53980     /**
53981      * Selects the first row in the grid.
53982      */
53983     selectFirstRow : function(){
53984         this.selectRow(0);
53985     },
53986
53987     /**
53988      * Select the last row.
53989      * @param {Boolean} keepExisting (optional) True to keep existing selections
53990      */
53991     selectLastRow : function(keepExisting){
53992         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
53993     },
53994
53995     /**
53996      * Selects the row immediately following the last selected row.
53997      * @param {Boolean} keepExisting (optional) True to keep existing selections
53998      */
53999     selectNext : function(keepExisting){
54000         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
54001             this.selectRow(this.last+1, keepExisting);
54002             this.grid.getView().focusRow(this.last);
54003         }
54004     },
54005
54006     /**
54007      * Selects the row that precedes the last selected row.
54008      * @param {Boolean} keepExisting (optional) True to keep existing selections
54009      */
54010     selectPrevious : function(keepExisting){
54011         if(this.last){
54012             this.selectRow(this.last-1, keepExisting);
54013             this.grid.getView().focusRow(this.last);
54014         }
54015     },
54016
54017     /**
54018      * Returns the selected records
54019      * @return {Array} Array of selected records
54020      */
54021     getSelections : function(){
54022         return [].concat(this.selections.items);
54023     },
54024
54025     /**
54026      * Returns the first selected record.
54027      * @return {Record}
54028      */
54029     getSelected : function(){
54030         return this.selections.itemAt(0);
54031     },
54032
54033
54034     /**
54035      * Clears all selections.
54036      */
54037     clearSelections : function(fast){
54038         if(this.locked) return;
54039         if(fast !== true){
54040             var ds = this.grid.dataSource;
54041             var s = this.selections;
54042             s.each(function(r){
54043                 this.deselectRow(ds.indexOfId(r.id));
54044             }, this);
54045             s.clear();
54046         }else{
54047             this.selections.clear();
54048         }
54049         this.last = false;
54050     },
54051
54052
54053     /**
54054      * Selects all rows.
54055      */
54056     selectAll : function(){
54057         if(this.locked) return;
54058         this.selections.clear();
54059         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
54060             this.selectRow(i, true);
54061         }
54062     },
54063
54064     /**
54065      * Returns True if there is a selection.
54066      * @return {Boolean}
54067      */
54068     hasSelection : function(){
54069         return this.selections.length > 0;
54070     },
54071
54072     /**
54073      * Returns True if the specified row is selected.
54074      * @param {Number/Record} record The record or index of the record to check
54075      * @return {Boolean}
54076      */
54077     isSelected : function(index){
54078         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54079         return (r && this.selections.key(r.id) ? true : false);
54080     },
54081
54082     /**
54083      * Returns True if the specified record id is selected.
54084      * @param {String} id The id of record to check
54085      * @return {Boolean}
54086      */
54087     isIdSelected : function(id){
54088         return (this.selections.key(id) ? true : false);
54089     },
54090
54091     // private
54092     handleMouseDown : function(e, t){
54093         var view = this.grid.getView(), rowIndex;
54094         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54095             return;
54096         };
54097         if(e.shiftKey && this.last !== false){
54098             var last = this.last;
54099             this.selectRange(last, rowIndex, e.ctrlKey);
54100             this.last = last; // reset the last
54101             view.focusRow(rowIndex);
54102         }else{
54103             var isSelected = this.isSelected(rowIndex);
54104             if(e.button !== 0 && isSelected){
54105                 view.focusRow(rowIndex);
54106             }else if(e.ctrlKey && isSelected){
54107                 this.deselectRow(rowIndex);
54108             }else if(!isSelected){
54109                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54110                 view.focusRow(rowIndex);
54111             }
54112         }
54113         this.fireEvent("afterselectionchange", this);
54114     },
54115     // private
54116     handleDragableRowClick :  function(grid, rowIndex, e) 
54117     {
54118         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54119             this.selectRow(rowIndex, false);
54120             grid.view.focusRow(rowIndex);
54121              this.fireEvent("afterselectionchange", this);
54122         }
54123     },
54124     
54125     /**
54126      * Selects multiple rows.
54127      * @param {Array} rows Array of the indexes of the row to select
54128      * @param {Boolean} keepExisting (optional) True to keep existing selections
54129      */
54130     selectRows : function(rows, keepExisting){
54131         if(!keepExisting){
54132             this.clearSelections();
54133         }
54134         for(var i = 0, len = rows.length; i < len; i++){
54135             this.selectRow(rows[i], true);
54136         }
54137     },
54138
54139     /**
54140      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54141      * @param {Number} startRow The index of the first row in the range
54142      * @param {Number} endRow The index of the last row in the range
54143      * @param {Boolean} keepExisting (optional) True to retain existing selections
54144      */
54145     selectRange : function(startRow, endRow, keepExisting){
54146         if(this.locked) return;
54147         if(!keepExisting){
54148             this.clearSelections();
54149         }
54150         if(startRow <= endRow){
54151             for(var i = startRow; i <= endRow; i++){
54152                 this.selectRow(i, true);
54153             }
54154         }else{
54155             for(var i = startRow; i >= endRow; i--){
54156                 this.selectRow(i, true);
54157             }
54158         }
54159     },
54160
54161     /**
54162      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54163      * @param {Number} startRow The index of the first row in the range
54164      * @param {Number} endRow The index of the last row in the range
54165      */
54166     deselectRange : function(startRow, endRow, preventViewNotify){
54167         if(this.locked) return;
54168         for(var i = startRow; i <= endRow; i++){
54169             this.deselectRow(i, preventViewNotify);
54170         }
54171     },
54172
54173     /**
54174      * Selects a row.
54175      * @param {Number} row The index of the row to select
54176      * @param {Boolean} keepExisting (optional) True to keep existing selections
54177      */
54178     selectRow : function(index, keepExisting, preventViewNotify){
54179         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54180         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54181             if(!keepExisting || this.singleSelect){
54182                 this.clearSelections();
54183             }
54184             var r = this.grid.dataSource.getAt(index);
54185             this.selections.add(r);
54186             this.last = this.lastActive = index;
54187             if(!preventViewNotify){
54188                 this.grid.getView().onRowSelect(index);
54189             }
54190             this.fireEvent("rowselect", this, index, r);
54191             this.fireEvent("selectionchange", this);
54192         }
54193     },
54194
54195     /**
54196      * Deselects a row.
54197      * @param {Number} row The index of the row to deselect
54198      */
54199     deselectRow : function(index, preventViewNotify){
54200         if(this.locked) return;
54201         if(this.last == index){
54202             this.last = false;
54203         }
54204         if(this.lastActive == index){
54205             this.lastActive = false;
54206         }
54207         var r = this.grid.dataSource.getAt(index);
54208         this.selections.remove(r);
54209         if(!preventViewNotify){
54210             this.grid.getView().onRowDeselect(index);
54211         }
54212         this.fireEvent("rowdeselect", this, index);
54213         this.fireEvent("selectionchange", this);
54214     },
54215
54216     // private
54217     restoreLast : function(){
54218         if(this._last){
54219             this.last = this._last;
54220         }
54221     },
54222
54223     // private
54224     acceptsNav : function(row, col, cm){
54225         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54226     },
54227
54228     // private
54229     onEditorKey : function(field, e){
54230         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54231         if(k == e.TAB){
54232             e.stopEvent();
54233             ed.completeEdit();
54234             if(e.shiftKey){
54235                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54236             }else{
54237                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54238             }
54239         }else if(k == e.ENTER && !e.ctrlKey){
54240             e.stopEvent();
54241             ed.completeEdit();
54242             if(e.shiftKey){
54243                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54244             }else{
54245                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54246             }
54247         }else if(k == e.ESC){
54248             ed.cancelEdit();
54249         }
54250         if(newCell){
54251             g.startEditing(newCell[0], newCell[1]);
54252         }
54253     }
54254 });/*
54255  * Based on:
54256  * Ext JS Library 1.1.1
54257  * Copyright(c) 2006-2007, Ext JS, LLC.
54258  *
54259  * Originally Released Under LGPL - original licence link has changed is not relivant.
54260  *
54261  * Fork - LGPL
54262  * <script type="text/javascript">
54263  */
54264 /**
54265  * @class Roo.grid.CellSelectionModel
54266  * @extends Roo.grid.AbstractSelectionModel
54267  * This class provides the basic implementation for cell selection in a grid.
54268  * @constructor
54269  * @param {Object} config The object containing the configuration of this model.
54270  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54271  */
54272 Roo.grid.CellSelectionModel = function(config){
54273     Roo.apply(this, config);
54274
54275     this.selection = null;
54276
54277     this.addEvents({
54278         /**
54279              * @event beforerowselect
54280              * Fires before a cell is selected.
54281              * @param {SelectionModel} this
54282              * @param {Number} rowIndex The selected row index
54283              * @param {Number} colIndex The selected cell index
54284              */
54285             "beforecellselect" : true,
54286         /**
54287              * @event cellselect
54288              * Fires when a cell is selected.
54289              * @param {SelectionModel} this
54290              * @param {Number} rowIndex The selected row index
54291              * @param {Number} colIndex The selected cell index
54292              */
54293             "cellselect" : true,
54294         /**
54295              * @event selectionchange
54296              * Fires when the active selection changes.
54297              * @param {SelectionModel} this
54298              * @param {Object} selection null for no selection or an object (o) with two properties
54299                 <ul>
54300                 <li>o.record: the record object for the row the selection is in</li>
54301                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54302                 </ul>
54303              */
54304             "selectionchange" : true,
54305         /**
54306              * @event tabend
54307              * Fires when the tab (or enter) was pressed on the last editable cell
54308              * You can use this to trigger add new row.
54309              * @param {SelectionModel} this
54310              */
54311             "tabend" : true,
54312          /**
54313              * @event beforeeditnext
54314              * Fires before the next editable sell is made active
54315              * You can use this to skip to another cell or fire the tabend
54316              *    if you set cell to false
54317              * @param {Object} eventdata object : { cell : [ row, col ] } 
54318              */
54319             "beforeeditnext" : true
54320     });
54321     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54322 };
54323
54324 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54325     
54326     enter_is_tab: false,
54327
54328     /** @ignore */
54329     initEvents : function(){
54330         this.grid.on("mousedown", this.handleMouseDown, this);
54331         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54332         var view = this.grid.view;
54333         view.on("refresh", this.onViewChange, this);
54334         view.on("rowupdated", this.onRowUpdated, this);
54335         view.on("beforerowremoved", this.clearSelections, this);
54336         view.on("beforerowsinserted", this.clearSelections, this);
54337         if(this.grid.isEditor){
54338             this.grid.on("beforeedit", this.beforeEdit,  this);
54339         }
54340     },
54341
54342         //private
54343     beforeEdit : function(e){
54344         this.select(e.row, e.column, false, true, e.record);
54345     },
54346
54347         //private
54348     onRowUpdated : function(v, index, r){
54349         if(this.selection && this.selection.record == r){
54350             v.onCellSelect(index, this.selection.cell[1]);
54351         }
54352     },
54353
54354         //private
54355     onViewChange : function(){
54356         this.clearSelections(true);
54357     },
54358
54359         /**
54360          * Returns the currently selected cell,.
54361          * @return {Array} The selected cell (row, column) or null if none selected.
54362          */
54363     getSelectedCell : function(){
54364         return this.selection ? this.selection.cell : null;
54365     },
54366
54367     /**
54368      * Clears all selections.
54369      * @param {Boolean} true to prevent the gridview from being notified about the change.
54370      */
54371     clearSelections : function(preventNotify){
54372         var s = this.selection;
54373         if(s){
54374             if(preventNotify !== true){
54375                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
54376             }
54377             this.selection = null;
54378             this.fireEvent("selectionchange", this, null);
54379         }
54380     },
54381
54382     /**
54383      * Returns true if there is a selection.
54384      * @return {Boolean}
54385      */
54386     hasSelection : function(){
54387         return this.selection ? true : false;
54388     },
54389
54390     /** @ignore */
54391     handleMouseDown : function(e, t){
54392         var v = this.grid.getView();
54393         if(this.isLocked()){
54394             return;
54395         };
54396         var row = v.findRowIndex(t);
54397         var cell = v.findCellIndex(t);
54398         if(row !== false && cell !== false){
54399             this.select(row, cell);
54400         }
54401     },
54402
54403     /**
54404      * Selects a cell.
54405      * @param {Number} rowIndex
54406      * @param {Number} collIndex
54407      */
54408     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
54409         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
54410             this.clearSelections();
54411             r = r || this.grid.dataSource.getAt(rowIndex);
54412             this.selection = {
54413                 record : r,
54414                 cell : [rowIndex, colIndex]
54415             };
54416             if(!preventViewNotify){
54417                 var v = this.grid.getView();
54418                 v.onCellSelect(rowIndex, colIndex);
54419                 if(preventFocus !== true){
54420                     v.focusCell(rowIndex, colIndex);
54421                 }
54422             }
54423             this.fireEvent("cellselect", this, rowIndex, colIndex);
54424             this.fireEvent("selectionchange", this, this.selection);
54425         }
54426     },
54427
54428         //private
54429     isSelectable : function(rowIndex, colIndex, cm){
54430         return !cm.isHidden(colIndex);
54431     },
54432
54433     /** @ignore */
54434     handleKeyDown : function(e){
54435         //Roo.log('Cell Sel Model handleKeyDown');
54436         if(!e.isNavKeyPress()){
54437             return;
54438         }
54439         var g = this.grid, s = this.selection;
54440         if(!s){
54441             e.stopEvent();
54442             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
54443             if(cell){
54444                 this.select(cell[0], cell[1]);
54445             }
54446             return;
54447         }
54448         var sm = this;
54449         var walk = function(row, col, step){
54450             return g.walkCells(row, col, step, sm.isSelectable,  sm);
54451         };
54452         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
54453         var newCell;
54454
54455       
54456
54457         switch(k){
54458             case e.TAB:
54459                 // handled by onEditorKey
54460                 if (g.isEditor && g.editing) {
54461                     return;
54462                 }
54463                 if(e.shiftKey) {
54464                     newCell = walk(r, c-1, -1);
54465                 } else {
54466                     newCell = walk(r, c+1, 1);
54467                 }
54468                 break;
54469             
54470             case e.DOWN:
54471                newCell = walk(r+1, c, 1);
54472                 break;
54473             
54474             case e.UP:
54475                 newCell = walk(r-1, c, -1);
54476                 break;
54477             
54478             case e.RIGHT:
54479                 newCell = walk(r, c+1, 1);
54480                 break;
54481             
54482             case e.LEFT:
54483                 newCell = walk(r, c-1, -1);
54484                 break;
54485             
54486             case e.ENTER:
54487                 
54488                 if(g.isEditor && !g.editing){
54489                    g.startEditing(r, c);
54490                    e.stopEvent();
54491                    return;
54492                 }
54493                 
54494                 
54495              break;
54496         };
54497         if(newCell){
54498             this.select(newCell[0], newCell[1]);
54499             e.stopEvent();
54500             
54501         }
54502     },
54503
54504     acceptsNav : function(row, col, cm){
54505         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54506     },
54507     /**
54508      * Selects a cell.
54509      * @param {Number} field (not used) - as it's normally used as a listener
54510      * @param {Number} e - event - fake it by using
54511      *
54512      * var e = Roo.EventObjectImpl.prototype;
54513      * e.keyCode = e.TAB
54514      *
54515      * 
54516      */
54517     onEditorKey : function(field, e){
54518         
54519         var k = e.getKey(),
54520             newCell,
54521             g = this.grid,
54522             ed = g.activeEditor,
54523             forward = false;
54524         ///Roo.log('onEditorKey' + k);
54525         
54526         
54527         if (this.enter_is_tab && k == e.ENTER) {
54528             k = e.TAB;
54529         }
54530         
54531         if(k == e.TAB){
54532             if(e.shiftKey){
54533                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54534             }else{
54535                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54536                 forward = true;
54537             }
54538             
54539             e.stopEvent();
54540             
54541         } else if(k == e.ENTER &&  !e.ctrlKey){
54542             ed.completeEdit();
54543             e.stopEvent();
54544             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54545         
54546                 } else if(k == e.ESC){
54547             ed.cancelEdit();
54548         }
54549                 
54550         if (newCell) {
54551             var ecall = { cell : newCell, forward : forward };
54552             this.fireEvent('beforeeditnext', ecall );
54553             newCell = ecall.cell;
54554                         forward = ecall.forward;
54555         }
54556                 
54557         if(newCell){
54558             //Roo.log('next cell after edit');
54559             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
54560         } else if (forward) {
54561             // tabbed past last
54562             this.fireEvent.defer(100, this, ['tabend',this]);
54563         }
54564     }
54565 });/*
54566  * Based on:
54567  * Ext JS Library 1.1.1
54568  * Copyright(c) 2006-2007, Ext JS, LLC.
54569  *
54570  * Originally Released Under LGPL - original licence link has changed is not relivant.
54571  *
54572  * Fork - LGPL
54573  * <script type="text/javascript">
54574  */
54575  
54576 /**
54577  * @class Roo.grid.EditorGrid
54578  * @extends Roo.grid.Grid
54579  * Class for creating and editable grid.
54580  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
54581  * The container MUST have some type of size defined for the grid to fill. The container will be 
54582  * automatically set to position relative if it isn't already.
54583  * @param {Object} dataSource The data model to bind to
54584  * @param {Object} colModel The column model with info about this grid's columns
54585  */
54586 Roo.grid.EditorGrid = function(container, config){
54587     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
54588     this.getGridEl().addClass("xedit-grid");
54589
54590     if(!this.selModel){
54591         this.selModel = new Roo.grid.CellSelectionModel();
54592     }
54593
54594     this.activeEditor = null;
54595
54596         this.addEvents({
54597             /**
54598              * @event beforeedit
54599              * Fires before cell editing is triggered. The edit event object has the following properties <br />
54600              * <ul style="padding:5px;padding-left:16px;">
54601              * <li>grid - This grid</li>
54602              * <li>record - The record being edited</li>
54603              * <li>field - The field name being edited</li>
54604              * <li>value - The value for the field being edited.</li>
54605              * <li>row - The grid row index</li>
54606              * <li>column - The grid column index</li>
54607              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
54608              * </ul>
54609              * @param {Object} e An edit event (see above for description)
54610              */
54611             "beforeedit" : true,
54612             /**
54613              * @event afteredit
54614              * Fires after a cell is edited. <br />
54615              * <ul style="padding:5px;padding-left:16px;">
54616              * <li>grid - This grid</li>
54617              * <li>record - The record being edited</li>
54618              * <li>field - The field name being edited</li>
54619              * <li>value - The value being set</li>
54620              * <li>originalValue - The original value for the field, before the edit.</li>
54621              * <li>row - The grid row index</li>
54622              * <li>column - The grid column index</li>
54623              * </ul>
54624              * @param {Object} e An edit event (see above for description)
54625              */
54626             "afteredit" : true,
54627             /**
54628              * @event validateedit
54629              * Fires after a cell is edited, but before the value is set in the record. 
54630          * You can use this to modify the value being set in the field, Return false
54631              * to cancel the change. The edit event object has the following properties <br />
54632              * <ul style="padding:5px;padding-left:16px;">
54633          * <li>editor - This editor</li>
54634              * <li>grid - This grid</li>
54635              * <li>record - The record being edited</li>
54636              * <li>field - The field name being edited</li>
54637              * <li>value - The value being set</li>
54638              * <li>originalValue - The original value for the field, before the edit.</li>
54639              * <li>row - The grid row index</li>
54640              * <li>column - The grid column index</li>
54641              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
54642              * </ul>
54643              * @param {Object} e An edit event (see above for description)
54644              */
54645             "validateedit" : true
54646         });
54647     this.on("bodyscroll", this.stopEditing,  this);
54648     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
54649 };
54650
54651 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
54652     /**
54653      * @cfg {Number} clicksToEdit
54654      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
54655      */
54656     clicksToEdit: 2,
54657
54658     // private
54659     isEditor : true,
54660     // private
54661     trackMouseOver: false, // causes very odd FF errors
54662
54663     onCellDblClick : function(g, row, col){
54664         this.startEditing(row, col);
54665     },
54666
54667     onEditComplete : function(ed, value, startValue){
54668         this.editing = false;
54669         this.activeEditor = null;
54670         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
54671         var r = ed.record;
54672         var field = this.colModel.getDataIndex(ed.col);
54673         var e = {
54674             grid: this,
54675             record: r,
54676             field: field,
54677             originalValue: startValue,
54678             value: value,
54679             row: ed.row,
54680             column: ed.col,
54681             cancel:false,
54682             editor: ed
54683         };
54684         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
54685         cell.show();
54686           
54687         if(String(value) !== String(startValue)){
54688             
54689             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
54690                 r.set(field, e.value);
54691                 // if we are dealing with a combo box..
54692                 // then we also set the 'name' colum to be the displayField
54693                 if (ed.field.displayField && ed.field.name) {
54694                     r.set(ed.field.name, ed.field.el.dom.value);
54695                 }
54696                 
54697                 delete e.cancel; //?? why!!!
54698                 this.fireEvent("afteredit", e);
54699             }
54700         } else {
54701             this.fireEvent("afteredit", e); // always fire it!
54702         }
54703         this.view.focusCell(ed.row, ed.col);
54704     },
54705
54706     /**
54707      * Starts editing the specified for the specified row/column
54708      * @param {Number} rowIndex
54709      * @param {Number} colIndex
54710      */
54711     startEditing : function(row, col){
54712         this.stopEditing();
54713         if(this.colModel.isCellEditable(col, row)){
54714             this.view.ensureVisible(row, col, true);
54715           
54716             var r = this.dataSource.getAt(row);
54717             var field = this.colModel.getDataIndex(col);
54718             var cell = Roo.get(this.view.getCell(row,col));
54719             var e = {
54720                 grid: this,
54721                 record: r,
54722                 field: field,
54723                 value: r.data[field],
54724                 row: row,
54725                 column: col,
54726                 cancel:false 
54727             };
54728             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
54729                 this.editing = true;
54730                 var ed = this.colModel.getCellEditor(col, row);
54731                 
54732                 if (!ed) {
54733                     return;
54734                 }
54735                 if(!ed.rendered){
54736                     ed.render(ed.parentEl || document.body);
54737                 }
54738                 ed.field.reset();
54739                
54740                 cell.hide();
54741                 
54742                 (function(){ // complex but required for focus issues in safari, ie and opera
54743                     ed.row = row;
54744                     ed.col = col;
54745                     ed.record = r;
54746                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
54747                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
54748                     this.activeEditor = ed;
54749                     var v = r.data[field];
54750                     ed.startEdit(this.view.getCell(row, col), v);
54751                     // combo's with 'displayField and name set
54752                     if (ed.field.displayField && ed.field.name) {
54753                         ed.field.el.dom.value = r.data[ed.field.name];
54754                     }
54755                     
54756                     
54757                 }).defer(50, this);
54758             }
54759         }
54760     },
54761         
54762     /**
54763      * Stops any active editing
54764      */
54765     stopEditing : function(){
54766         if(this.activeEditor){
54767             this.activeEditor.completeEdit();
54768         }
54769         this.activeEditor = null;
54770     },
54771         
54772          /**
54773      * Called to get grid's drag proxy text, by default returns this.ddText.
54774      * @return {String}
54775      */
54776     getDragDropText : function(){
54777         var count = this.selModel.getSelectedCell() ? 1 : 0;
54778         return String.format(this.ddText, count, count == 1 ? '' : 's');
54779     }
54780         
54781 });/*
54782  * Based on:
54783  * Ext JS Library 1.1.1
54784  * Copyright(c) 2006-2007, Ext JS, LLC.
54785  *
54786  * Originally Released Under LGPL - original licence link has changed is not relivant.
54787  *
54788  * Fork - LGPL
54789  * <script type="text/javascript">
54790  */
54791
54792 // private - not really -- you end up using it !
54793 // This is a support class used internally by the Grid components
54794
54795 /**
54796  * @class Roo.grid.GridEditor
54797  * @extends Roo.Editor
54798  * Class for creating and editable grid elements.
54799  * @param {Object} config any settings (must include field)
54800  */
54801 Roo.grid.GridEditor = function(field, config){
54802     if (!config && field.field) {
54803         config = field;
54804         field = Roo.factory(config.field, Roo.form);
54805     }
54806     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
54807     field.monitorTab = false;
54808 };
54809
54810 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
54811     
54812     /**
54813      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
54814      */
54815     
54816     alignment: "tl-tl",
54817     autoSize: "width",
54818     hideEl : false,
54819     cls: "x-small-editor x-grid-editor",
54820     shim:false,
54821     shadow:"frame"
54822 });/*
54823  * Based on:
54824  * Ext JS Library 1.1.1
54825  * Copyright(c) 2006-2007, Ext JS, LLC.
54826  *
54827  * Originally Released Under LGPL - original licence link has changed is not relivant.
54828  *
54829  * Fork - LGPL
54830  * <script type="text/javascript">
54831  */
54832   
54833
54834   
54835 Roo.grid.PropertyRecord = Roo.data.Record.create([
54836     {name:'name',type:'string'},  'value'
54837 ]);
54838
54839
54840 Roo.grid.PropertyStore = function(grid, source){
54841     this.grid = grid;
54842     this.store = new Roo.data.Store({
54843         recordType : Roo.grid.PropertyRecord
54844     });
54845     this.store.on('update', this.onUpdate,  this);
54846     if(source){
54847         this.setSource(source);
54848     }
54849     Roo.grid.PropertyStore.superclass.constructor.call(this);
54850 };
54851
54852
54853
54854 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
54855     setSource : function(o){
54856         this.source = o;
54857         this.store.removeAll();
54858         var data = [];
54859         for(var k in o){
54860             if(this.isEditableValue(o[k])){
54861                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
54862             }
54863         }
54864         this.store.loadRecords({records: data}, {}, true);
54865     },
54866
54867     onUpdate : function(ds, record, type){
54868         if(type == Roo.data.Record.EDIT){
54869             var v = record.data['value'];
54870             var oldValue = record.modified['value'];
54871             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
54872                 this.source[record.id] = v;
54873                 record.commit();
54874                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
54875             }else{
54876                 record.reject();
54877             }
54878         }
54879     },
54880
54881     getProperty : function(row){
54882        return this.store.getAt(row);
54883     },
54884
54885     isEditableValue: function(val){
54886         if(val && val instanceof Date){
54887             return true;
54888         }else if(typeof val == 'object' || typeof val == 'function'){
54889             return false;
54890         }
54891         return true;
54892     },
54893
54894     setValue : function(prop, value){
54895         this.source[prop] = value;
54896         this.store.getById(prop).set('value', value);
54897     },
54898
54899     getSource : function(){
54900         return this.source;
54901     }
54902 });
54903
54904 Roo.grid.PropertyColumnModel = function(grid, store){
54905     this.grid = grid;
54906     var g = Roo.grid;
54907     g.PropertyColumnModel.superclass.constructor.call(this, [
54908         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
54909         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
54910     ]);
54911     this.store = store;
54912     this.bselect = Roo.DomHelper.append(document.body, {
54913         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
54914             {tag: 'option', value: 'true', html: 'true'},
54915             {tag: 'option', value: 'false', html: 'false'}
54916         ]
54917     });
54918     Roo.id(this.bselect);
54919     var f = Roo.form;
54920     this.editors = {
54921         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
54922         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
54923         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
54924         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
54925         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
54926     };
54927     this.renderCellDelegate = this.renderCell.createDelegate(this);
54928     this.renderPropDelegate = this.renderProp.createDelegate(this);
54929 };
54930
54931 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
54932     
54933     
54934     nameText : 'Name',
54935     valueText : 'Value',
54936     
54937     dateFormat : 'm/j/Y',
54938     
54939     
54940     renderDate : function(dateVal){
54941         return dateVal.dateFormat(this.dateFormat);
54942     },
54943
54944     renderBool : function(bVal){
54945         return bVal ? 'true' : 'false';
54946     },
54947
54948     isCellEditable : function(colIndex, rowIndex){
54949         return colIndex == 1;
54950     },
54951
54952     getRenderer : function(col){
54953         return col == 1 ?
54954             this.renderCellDelegate : this.renderPropDelegate;
54955     },
54956
54957     renderProp : function(v){
54958         return this.getPropertyName(v);
54959     },
54960
54961     renderCell : function(val){
54962         var rv = val;
54963         if(val instanceof Date){
54964             rv = this.renderDate(val);
54965         }else if(typeof val == 'boolean'){
54966             rv = this.renderBool(val);
54967         }
54968         return Roo.util.Format.htmlEncode(rv);
54969     },
54970
54971     getPropertyName : function(name){
54972         var pn = this.grid.propertyNames;
54973         return pn && pn[name] ? pn[name] : name;
54974     },
54975
54976     getCellEditor : function(colIndex, rowIndex){
54977         var p = this.store.getProperty(rowIndex);
54978         var n = p.data['name'], val = p.data['value'];
54979         
54980         if(typeof(this.grid.customEditors[n]) == 'string'){
54981             return this.editors[this.grid.customEditors[n]];
54982         }
54983         if(typeof(this.grid.customEditors[n]) != 'undefined'){
54984             return this.grid.customEditors[n];
54985         }
54986         if(val instanceof Date){
54987             return this.editors['date'];
54988         }else if(typeof val == 'number'){
54989             return this.editors['number'];
54990         }else if(typeof val == 'boolean'){
54991             return this.editors['boolean'];
54992         }else{
54993             return this.editors['string'];
54994         }
54995     }
54996 });
54997
54998 /**
54999  * @class Roo.grid.PropertyGrid
55000  * @extends Roo.grid.EditorGrid
55001  * This class represents the  interface of a component based property grid control.
55002  * <br><br>Usage:<pre><code>
55003  var grid = new Roo.grid.PropertyGrid("my-container-id", {
55004       
55005  });
55006  // set any options
55007  grid.render();
55008  * </code></pre>
55009   
55010  * @constructor
55011  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55012  * The container MUST have some type of size defined for the grid to fill. The container will be
55013  * automatically set to position relative if it isn't already.
55014  * @param {Object} config A config object that sets properties on this grid.
55015  */
55016 Roo.grid.PropertyGrid = function(container, config){
55017     config = config || {};
55018     var store = new Roo.grid.PropertyStore(this);
55019     this.store = store;
55020     var cm = new Roo.grid.PropertyColumnModel(this, store);
55021     store.store.sort('name', 'ASC');
55022     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
55023         ds: store.store,
55024         cm: cm,
55025         enableColLock:false,
55026         enableColumnMove:false,
55027         stripeRows:false,
55028         trackMouseOver: false,
55029         clicksToEdit:1
55030     }, config));
55031     this.getGridEl().addClass('x-props-grid');
55032     this.lastEditRow = null;
55033     this.on('columnresize', this.onColumnResize, this);
55034     this.addEvents({
55035          /**
55036              * @event beforepropertychange
55037              * Fires before a property changes (return false to stop?)
55038              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55039              * @param {String} id Record Id
55040              * @param {String} newval New Value
55041          * @param {String} oldval Old Value
55042              */
55043         "beforepropertychange": true,
55044         /**
55045              * @event propertychange
55046              * Fires after a property changes
55047              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55048              * @param {String} id Record Id
55049              * @param {String} newval New Value
55050          * @param {String} oldval Old Value
55051              */
55052         "propertychange": true
55053     });
55054     this.customEditors = this.customEditors || {};
55055 };
55056 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
55057     
55058      /**
55059      * @cfg {Object} customEditors map of colnames=> custom editors.
55060      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
55061      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
55062      * false disables editing of the field.
55063          */
55064     
55065       /**
55066      * @cfg {Object} propertyNames map of property Names to their displayed value
55067          */
55068     
55069     render : function(){
55070         Roo.grid.PropertyGrid.superclass.render.call(this);
55071         this.autoSize.defer(100, this);
55072     },
55073
55074     autoSize : function(){
55075         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55076         if(this.view){
55077             this.view.fitColumns();
55078         }
55079     },
55080
55081     onColumnResize : function(){
55082         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55083         this.autoSize();
55084     },
55085     /**
55086      * Sets the data for the Grid
55087      * accepts a Key => Value object of all the elements avaiable.
55088      * @param {Object} data  to appear in grid.
55089      */
55090     setSource : function(source){
55091         this.store.setSource(source);
55092         //this.autoSize();
55093     },
55094     /**
55095      * Gets all the data from the grid.
55096      * @return {Object} data  data stored in grid
55097      */
55098     getSource : function(){
55099         return this.store.getSource();
55100     }
55101 });/*
55102  * Based on:
55103  * Ext JS Library 1.1.1
55104  * Copyright(c) 2006-2007, Ext JS, LLC.
55105  *
55106  * Originally Released Under LGPL - original licence link has changed is not relivant.
55107  *
55108  * Fork - LGPL
55109  * <script type="text/javascript">
55110  */
55111  
55112 /**
55113  * @class Roo.LoadMask
55114  * A simple utility class for generically masking elements while loading data.  If the element being masked has
55115  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
55116  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
55117  * element's UpdateManager load indicator and will be destroyed after the initial load.
55118  * @constructor
55119  * Create a new LoadMask
55120  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
55121  * @param {Object} config The config object
55122  */
55123 Roo.LoadMask = function(el, config){
55124     this.el = Roo.get(el);
55125     Roo.apply(this, config);
55126     if(this.store){
55127         this.store.on('beforeload', this.onBeforeLoad, this);
55128         this.store.on('load', this.onLoad, this);
55129         this.store.on('loadexception', this.onLoadException, this);
55130         this.removeMask = false;
55131     }else{
55132         var um = this.el.getUpdateManager();
55133         um.showLoadIndicator = false; // disable the default indicator
55134         um.on('beforeupdate', this.onBeforeLoad, this);
55135         um.on('update', this.onLoad, this);
55136         um.on('failure', this.onLoad, this);
55137         this.removeMask = true;
55138     }
55139 };
55140
55141 Roo.LoadMask.prototype = {
55142     /**
55143      * @cfg {Boolean} removeMask
55144      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
55145      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
55146      */
55147     /**
55148      * @cfg {String} msg
55149      * The text to display in a centered loading message box (defaults to 'Loading...')
55150      */
55151     msg : 'Loading...',
55152     /**
55153      * @cfg {String} msgCls
55154      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
55155      */
55156     msgCls : 'x-mask-loading',
55157
55158     /**
55159      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
55160      * @type Boolean
55161      */
55162     disabled: false,
55163
55164     /**
55165      * Disables the mask to prevent it from being displayed
55166      */
55167     disable : function(){
55168        this.disabled = true;
55169     },
55170
55171     /**
55172      * Enables the mask so that it can be displayed
55173      */
55174     enable : function(){
55175         this.disabled = false;
55176     },
55177     
55178     onLoadException : function()
55179     {
55180         Roo.log(arguments);
55181         
55182         if (typeof(arguments[3]) != 'undefined') {
55183             Roo.MessageBox.alert("Error loading",arguments[3]);
55184         } 
55185         /*
55186         try {
55187             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
55188                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
55189             }   
55190         } catch(e) {
55191             
55192         }
55193         */
55194     
55195         
55196         
55197         this.el.unmask(this.removeMask);
55198     },
55199     // private
55200     onLoad : function()
55201     {
55202         this.el.unmask(this.removeMask);
55203     },
55204
55205     // private
55206     onBeforeLoad : function(){
55207         if(!this.disabled){
55208             this.el.mask(this.msg, this.msgCls);
55209         }
55210     },
55211
55212     // private
55213     destroy : function(){
55214         if(this.store){
55215             this.store.un('beforeload', this.onBeforeLoad, this);
55216             this.store.un('load', this.onLoad, this);
55217             this.store.un('loadexception', this.onLoadException, this);
55218         }else{
55219             var um = this.el.getUpdateManager();
55220             um.un('beforeupdate', this.onBeforeLoad, this);
55221             um.un('update', this.onLoad, this);
55222             um.un('failure', this.onLoad, this);
55223         }
55224     }
55225 };/*
55226  * Based on:
55227  * Ext JS Library 1.1.1
55228  * Copyright(c) 2006-2007, Ext JS, LLC.
55229  *
55230  * Originally Released Under LGPL - original licence link has changed is not relivant.
55231  *
55232  * Fork - LGPL
55233  * <script type="text/javascript">
55234  */
55235
55236
55237 /**
55238  * @class Roo.XTemplate
55239  * @extends Roo.Template
55240  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
55241 <pre><code>
55242 var t = new Roo.XTemplate(
55243         '&lt;select name="{name}"&gt;',
55244                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
55245         '&lt;/select&gt;'
55246 );
55247  
55248 // then append, applying the master template values
55249  </code></pre>
55250  *
55251  * Supported features:
55252  *
55253  *  Tags:
55254
55255 <pre><code>
55256       {a_variable} - output encoded.
55257       {a_variable.format:("Y-m-d")} - call a method on the variable
55258       {a_variable:raw} - unencoded output
55259       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
55260       {a_variable:this.method_on_template(...)} - call a method on the template object.
55261  
55262 </code></pre>
55263  *  The tpl tag:
55264 <pre><code>
55265         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
55266         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
55267         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
55268         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
55269   
55270         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
55271         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
55272 </code></pre>
55273  *      
55274  */
55275 Roo.XTemplate = function()
55276 {
55277     Roo.XTemplate.superclass.constructor.apply(this, arguments);
55278     if (this.html) {
55279         this.compile();
55280     }
55281 };
55282
55283
55284 Roo.extend(Roo.XTemplate, Roo.Template, {
55285
55286     /**
55287      * The various sub templates
55288      */
55289     tpls : false,
55290     /**
55291      *
55292      * basic tag replacing syntax
55293      * WORD:WORD()
55294      *
55295      * // you can fake an object call by doing this
55296      *  x.t:(test,tesT) 
55297      * 
55298      */
55299     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
55300
55301     /**
55302      * compile the template
55303      *
55304      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
55305      *
55306      */
55307     compile: function()
55308     {
55309         var s = this.html;
55310      
55311         s = ['<tpl>', s, '</tpl>'].join('');
55312     
55313         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
55314             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
55315             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
55316             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
55317             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
55318             m,
55319             id     = 0,
55320             tpls   = [];
55321     
55322         while(true == !!(m = s.match(re))){
55323             var forMatch   = m[0].match(nameRe),
55324                 ifMatch   = m[0].match(ifRe),
55325                 execMatch   = m[0].match(execRe),
55326                 namedMatch   = m[0].match(namedRe),
55327                 
55328                 exp  = null, 
55329                 fn   = null,
55330                 exec = null,
55331                 name = forMatch && forMatch[1] ? forMatch[1] : '';
55332                 
55333             if (ifMatch) {
55334                 // if - puts fn into test..
55335                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
55336                 if(exp){
55337                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
55338                 }
55339             }
55340             
55341             if (execMatch) {
55342                 // exec - calls a function... returns empty if true is  returned.
55343                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
55344                 if(exp){
55345                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
55346                 }
55347             }
55348             
55349             
55350             if (name) {
55351                 // for = 
55352                 switch(name){
55353                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
55354                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
55355                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
55356                 }
55357             }
55358             var uid = namedMatch ? namedMatch[1] : id;
55359             
55360             
55361             tpls.push({
55362                 id:     namedMatch ? namedMatch[1] : id,
55363                 target: name,
55364                 exec:   exec,
55365                 test:   fn,
55366                 body:   m[1] || ''
55367             });
55368             if (namedMatch) {
55369                 s = s.replace(m[0], '');
55370             } else { 
55371                 s = s.replace(m[0], '{xtpl'+ id + '}');
55372             }
55373             ++id;
55374         }
55375         this.tpls = [];
55376         for(var i = tpls.length-1; i >= 0; --i){
55377             this.compileTpl(tpls[i]);
55378             this.tpls[tpls[i].id] = tpls[i];
55379         }
55380         this.master = tpls[tpls.length-1];
55381         return this;
55382     },
55383     /**
55384      * same as applyTemplate, except it's done to one of the subTemplates
55385      * when using named templates, you can do:
55386      *
55387      * var str = pl.applySubTemplate('your-name', values);
55388      *
55389      * 
55390      * @param {Number} id of the template
55391      * @param {Object} values to apply to template
55392      * @param {Object} parent (normaly the instance of this object)
55393      */
55394     applySubTemplate : function(id, values, parent)
55395     {
55396         
55397         
55398         var t = this.tpls[id];
55399         
55400         
55401         try { 
55402             if(t.test && !t.test.call(this, values, parent)){
55403                 return '';
55404             }
55405         } catch(e) {
55406             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
55407             Roo.log(e.toString());
55408             Roo.log(t.test);
55409             return ''
55410         }
55411         try { 
55412             
55413             if(t.exec && t.exec.call(this, values, parent)){
55414                 return '';
55415             }
55416         } catch(e) {
55417             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
55418             Roo.log(e.toString());
55419             Roo.log(t.exec);
55420             return ''
55421         }
55422         try {
55423             var vs = t.target ? t.target.call(this, values, parent) : values;
55424             parent = t.target ? values : parent;
55425             if(t.target && vs instanceof Array){
55426                 var buf = [];
55427                 for(var i = 0, len = vs.length; i < len; i++){
55428                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
55429                 }
55430                 return buf.join('');
55431             }
55432             return t.compiled.call(this, vs, parent);
55433         } catch (e) {
55434             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
55435             Roo.log(e.toString());
55436             Roo.log(t.compiled);
55437             return '';
55438         }
55439     },
55440
55441     compileTpl : function(tpl)
55442     {
55443         var fm = Roo.util.Format;
55444         var useF = this.disableFormats !== true;
55445         var sep = Roo.isGecko ? "+" : ",";
55446         var undef = function(str) {
55447             Roo.log("Property not found :"  + str);
55448             return '';
55449         };
55450         
55451         var fn = function(m, name, format, args)
55452         {
55453             //Roo.log(arguments);
55454             args = args ? args.replace(/\\'/g,"'") : args;
55455             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
55456             if (typeof(format) == 'undefined') {
55457                 format= 'htmlEncode';
55458             }
55459             if (format == 'raw' ) {
55460                 format = false;
55461             }
55462             
55463             if(name.substr(0, 4) == 'xtpl'){
55464                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
55465             }
55466             
55467             // build an array of options to determine if value is undefined..
55468             
55469             // basically get 'xxxx.yyyy' then do
55470             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
55471             //    (function () { Roo.log("Property not found"); return ''; })() :
55472             //    ......
55473             
55474             var udef_ar = [];
55475             var lookfor = '';
55476             Roo.each(name.split('.'), function(st) {
55477                 lookfor += (lookfor.length ? '.': '') + st;
55478                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
55479             });
55480             
55481             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
55482             
55483             
55484             if(format && useF){
55485                 
55486                 args = args ? ',' + args : "";
55487                  
55488                 if(format.substr(0, 5) != "this."){
55489                     format = "fm." + format + '(';
55490                 }else{
55491                     format = 'this.call("'+ format.substr(5) + '", ';
55492                     args = ", values";
55493                 }
55494                 
55495                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
55496             }
55497              
55498             if (args.length) {
55499                 // called with xxyx.yuu:(test,test)
55500                 // change to ()
55501                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
55502             }
55503             // raw.. - :raw modifier..
55504             return "'"+ sep + udef_st  + name + ")"+sep+"'";
55505             
55506         };
55507         var body;
55508         // branched to use + in gecko and [].join() in others
55509         if(Roo.isGecko){
55510             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
55511                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
55512                     "';};};";
55513         }else{
55514             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
55515             body.push(tpl.body.replace(/(\r\n|\n)/g,
55516                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
55517             body.push("'].join('');};};");
55518             body = body.join('');
55519         }
55520         
55521         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
55522        
55523         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
55524         eval(body);
55525         
55526         return this;
55527     },
55528
55529     applyTemplate : function(values){
55530         return this.master.compiled.call(this, values, {});
55531         //var s = this.subs;
55532     },
55533
55534     apply : function(){
55535         return this.applyTemplate.apply(this, arguments);
55536     }
55537
55538  });
55539
55540 Roo.XTemplate.from = function(el){
55541     el = Roo.getDom(el);
55542     return new Roo.XTemplate(el.value || el.innerHTML);
55543 };/*
55544  * Original code for Roojs - LGPL
55545  * <script type="text/javascript">
55546  */
55547  
55548 /**
55549  * @class Roo.XComponent
55550  * A delayed Element creator...
55551  * Or a way to group chunks of interface together.
55552  * 
55553  * Mypart.xyx = new Roo.XComponent({
55554
55555     parent : 'Mypart.xyz', // empty == document.element.!!
55556     order : '001',
55557     name : 'xxxx'
55558     region : 'xxxx'
55559     disabled : function() {} 
55560      
55561     tree : function() { // return an tree of xtype declared components
55562         var MODULE = this;
55563         return 
55564         {
55565             xtype : 'NestedLayoutPanel',
55566             // technicall
55567         }
55568      ]
55569  *})
55570  *
55571  *
55572  * It can be used to build a big heiracy, with parent etc.
55573  * or you can just use this to render a single compoent to a dom element
55574  * MYPART.render(Roo.Element | String(id) | dom_element )
55575  * 
55576  * @extends Roo.util.Observable
55577  * @constructor
55578  * @param cfg {Object} configuration of component
55579  * 
55580  */
55581 Roo.XComponent = function(cfg) {
55582     Roo.apply(this, cfg);
55583     this.addEvents({ 
55584         /**
55585              * @event built
55586              * Fires when this the componnt is built
55587              * @param {Roo.XComponent} c the component
55588              */
55589         'built' : true
55590         
55591     });
55592     this.region = this.region || 'center'; // default..
55593     Roo.XComponent.register(this);
55594     this.modules = false;
55595     this.el = false; // where the layout goes..
55596     
55597     
55598 }
55599 Roo.extend(Roo.XComponent, Roo.util.Observable, {
55600     /**
55601      * @property el
55602      * The created element (with Roo.factory())
55603      * @type {Roo.Layout}
55604      */
55605     el  : false,
55606     
55607     /**
55608      * @property el
55609      * for BC  - use el in new code
55610      * @type {Roo.Layout}
55611      */
55612     panel : false,
55613     
55614     /**
55615      * @property layout
55616      * for BC  - use el in new code
55617      * @type {Roo.Layout}
55618      */
55619     layout : false,
55620     
55621      /**
55622      * @cfg {Function|boolean} disabled
55623      * If this module is disabled by some rule, return true from the funtion
55624      */
55625     disabled : false,
55626     
55627     /**
55628      * @cfg {String} parent 
55629      * Name of parent element which it get xtype added to..
55630      */
55631     parent: false,
55632     
55633     /**
55634      * @cfg {String} order
55635      * Used to set the order in which elements are created (usefull for multiple tabs)
55636      */
55637     
55638     order : false,
55639     /**
55640      * @cfg {String} name
55641      * String to display while loading.
55642      */
55643     name : false,
55644     /**
55645      * @cfg {String} region
55646      * Region to render component to (defaults to center)
55647      */
55648     region : 'center',
55649     
55650     /**
55651      * @cfg {Array} items
55652      * A single item array - the first element is the root of the tree..
55653      * It's done this way to stay compatible with the Xtype system...
55654      */
55655     items : false,
55656     
55657     /**
55658      * @property _tree
55659      * The method that retuns the tree of parts that make up this compoennt 
55660      * @type {function}
55661      */
55662     _tree  : false,
55663     
55664      /**
55665      * render
55666      * render element to dom or tree
55667      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
55668      */
55669     
55670     render : function(el)
55671     {
55672         
55673         el = el || false;
55674         var hp = this.parent ? 1 : 0;
55675         
55676         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
55677             // if parent is a '#.....' string, then let's use that..
55678             var ename = this.parent.substr(1)
55679             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
55680             el = Roo.get(ename);
55681             if (!el && !this.parent) {
55682                 Roo.log("Warning - element can not be found :#" + ename );
55683                 return;
55684             }
55685         }
55686         
55687         
55688         if (!this.parent) {
55689             
55690             el = el ? Roo.get(el) : false;      
55691             
55692             // it's a top level one..
55693             this.parent =  {
55694                 el : new Roo.BorderLayout(el || document.body, {
55695                 
55696                      center: {
55697                          titlebar: false,
55698                          autoScroll:false,
55699                          closeOnTab: true,
55700                          tabPosition: 'top',
55701                           //resizeTabs: true,
55702                          alwaysShowTabs: el && hp? false :  true,
55703                          hideTabs: el || !hp ? true :  false,
55704                          minTabWidth: 140
55705                      }
55706                  })
55707             }
55708         }
55709         
55710                 if (!this.parent.el) {
55711                         // probably an old style ctor, which has been disabled.
55712                         return;
55713                         
55714                 }
55715                 // The 'tree' method is  '_tree now' 
55716             
55717         var tree = this._tree ? this._tree() : this.tree();
55718         tree.region = tree.region || this.region;
55719         this.el = this.parent.el.addxtype(tree);
55720         this.fireEvent('built', this);
55721         
55722         this.panel = this.el;
55723         this.layout = this.panel.layout;
55724                 this.parentLayout = this.parent.layout  || false;  
55725          
55726     }
55727     
55728 });
55729
55730 Roo.apply(Roo.XComponent, {
55731     /**
55732      * @property  hideProgress
55733      * true to disable the building progress bar.. usefull on single page renders.
55734      * @type Boolean
55735      */
55736     hideProgress : false,
55737     /**
55738      * @property  buildCompleted
55739      * True when the builder has completed building the interface.
55740      * @type Boolean
55741      */
55742     buildCompleted : false,
55743      
55744     /**
55745      * @property  topModule
55746      * the upper most module - uses document.element as it's constructor.
55747      * @type Object
55748      */
55749      
55750     topModule  : false,
55751       
55752     /**
55753      * @property  modules
55754      * array of modules to be created by registration system.
55755      * @type {Array} of Roo.XComponent
55756      */
55757     
55758     modules : [],
55759     /**
55760      * @property  elmodules
55761      * array of modules to be created by which use #ID 
55762      * @type {Array} of Roo.XComponent
55763      */
55764      
55765     elmodules : [],
55766
55767     
55768     /**
55769      * Register components to be built later.
55770      *
55771      * This solves the following issues
55772      * - Building is not done on page load, but after an authentication process has occured.
55773      * - Interface elements are registered on page load
55774      * - Parent Interface elements may not be loaded before child, so this handles that..
55775      * 
55776      *
55777      * example:
55778      * 
55779      * MyApp.register({
55780           order : '000001',
55781           module : 'Pman.Tab.projectMgr',
55782           region : 'center',
55783           parent : 'Pman.layout',
55784           disabled : false,  // or use a function..
55785         })
55786      
55787      * * @param {Object} details about module
55788      */
55789     register : function(obj) {
55790                 
55791         Roo.XComponent.event.fireEvent('register', obj);
55792         switch(typeof(obj.disabled) ) {
55793                 
55794             case 'undefined':
55795                 break;
55796             
55797             case 'function':
55798                 if ( obj.disabled() ) {
55799                         return;
55800                 }
55801                 break;
55802             
55803             default:
55804                 if (obj.disabled) {
55805                         return;
55806                 }
55807                 break;
55808         }
55809                 
55810         this.modules.push(obj);
55811          
55812     },
55813     /**
55814      * convert a string to an object..
55815      * eg. 'AAA.BBB' -> finds AAA.BBB
55816
55817      */
55818     
55819     toObject : function(str)
55820     {
55821         if (!str || typeof(str) == 'object') {
55822             return str;
55823         }
55824         if (str.substring(0,1) == '#') {
55825             return str;
55826         }
55827
55828         var ar = str.split('.');
55829         var rt, o;
55830         rt = ar.shift();
55831             /** eval:var:o */
55832         try {
55833             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
55834         } catch (e) {
55835             throw "Module not found : " + str;
55836         }
55837         
55838         if (o === false) {
55839             throw "Module not found : " + str;
55840         }
55841         Roo.each(ar, function(e) {
55842             if (typeof(o[e]) == 'undefined') {
55843                 throw "Module not found : " + str;
55844             }
55845             o = o[e];
55846         });
55847         
55848         return o;
55849         
55850     },
55851     
55852     
55853     /**
55854      * move modules into their correct place in the tree..
55855      * 
55856      */
55857     preBuild : function ()
55858     {
55859         var _t = this;
55860         Roo.each(this.modules , function (obj)
55861         {
55862             Roo.XComponent.event.fireEvent('beforebuild', obj);
55863             
55864             var opar = obj.parent;
55865             try { 
55866                 obj.parent = this.toObject(opar);
55867             } catch(e) {
55868                 Roo.log("parent:toObject failed: " + e.toString());
55869                 return;
55870             }
55871             
55872             if (!obj.parent) {
55873                 Roo.debug && Roo.log("GOT top level module");
55874                 Roo.debug && Roo.log(obj);
55875                 obj.modules = new Roo.util.MixedCollection(false, 
55876                     function(o) { return o.order + '' }
55877                 );
55878                 this.topModule = obj;
55879                 return;
55880             }
55881                         // parent is a string (usually a dom element name..)
55882             if (typeof(obj.parent) == 'string') {
55883                 this.elmodules.push(obj);
55884                 return;
55885             }
55886             if (obj.parent.constructor != Roo.XComponent) {
55887                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
55888             }
55889             if (!obj.parent.modules) {
55890                 obj.parent.modules = new Roo.util.MixedCollection(false, 
55891                     function(o) { return o.order + '' }
55892                 );
55893             }
55894             if (obj.parent.disabled) {
55895                 obj.disabled = true;
55896             }
55897             obj.parent.modules.add(obj);
55898         }, this);
55899     },
55900     
55901      /**
55902      * make a list of modules to build.
55903      * @return {Array} list of modules. 
55904      */ 
55905     
55906     buildOrder : function()
55907     {
55908         var _this = this;
55909         var cmp = function(a,b) {   
55910             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
55911         };
55912         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
55913             throw "No top level modules to build";
55914         }
55915         
55916         // make a flat list in order of modules to build.
55917         var mods = this.topModule ? [ this.topModule ] : [];
55918                 
55919         
55920         // elmodules (is a list of DOM based modules )
55921         Roo.each(this.elmodules, function(e) {
55922             mods.push(e);
55923             if (!this.topModule &&
55924                 typeof(e.parent) == 'string' &&
55925                 e.parent.substring(0,1) == '#' &&
55926                 Roo.get(e.parent.substr(1))
55927                ) {
55928                 
55929                 _this.topModule = e;
55930             }
55931             
55932         });
55933
55934         
55935         // add modules to their parents..
55936         var addMod = function(m) {
55937             Roo.debug && Roo.log("build Order: add: " + m.name);
55938                 
55939             mods.push(m);
55940             if (m.modules && !m.disabled) {
55941                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
55942                 m.modules.keySort('ASC',  cmp );
55943                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
55944     
55945                 m.modules.each(addMod);
55946             } else {
55947                 Roo.debug && Roo.log("build Order: no child modules");
55948             }
55949             // not sure if this is used any more..
55950             if (m.finalize) {
55951                 m.finalize.name = m.name + " (clean up) ";
55952                 mods.push(m.finalize);
55953             }
55954             
55955         }
55956         if (this.topModule && this.topModule.modules) { 
55957             this.topModule.modules.keySort('ASC',  cmp );
55958             this.topModule.modules.each(addMod);
55959         } 
55960         return mods;
55961     },
55962     
55963      /**
55964      * Build the registered modules.
55965      * @param {Object} parent element.
55966      * @param {Function} optional method to call after module has been added.
55967      * 
55968      */ 
55969    
55970     build : function() 
55971     {
55972         
55973         this.preBuild();
55974         var mods = this.buildOrder();
55975       
55976         //this.allmods = mods;
55977         //Roo.debug && Roo.log(mods);
55978         //return;
55979         if (!mods.length) { // should not happen
55980             throw "NO modules!!!";
55981         }
55982         
55983         
55984         var msg = "Building Interface...";
55985         // flash it up as modal - so we store the mask!?
55986         if (!this.hideProgress) {
55987             Roo.MessageBox.show({ title: 'loading' });
55988             Roo.MessageBox.show({
55989                title: "Please wait...",
55990                msg: msg,
55991                width:450,
55992                progress:true,
55993                closable:false,
55994                modal: false
55995               
55996             });
55997         }
55998         var total = mods.length;
55999         
56000         var _this = this;
56001         var progressRun = function() {
56002             if (!mods.length) {
56003                 Roo.debug && Roo.log('hide?');
56004                 if (!this.hideProgress) {
56005                     Roo.MessageBox.hide();
56006                 }
56007                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
56008                 
56009                 // THE END...
56010                 return false;   
56011             }
56012             
56013             var m = mods.shift();
56014             
56015             
56016             Roo.debug && Roo.log(m);
56017             // not sure if this is supported any more.. - modules that are are just function
56018             if (typeof(m) == 'function') { 
56019                 m.call(this);
56020                 return progressRun.defer(10, _this);
56021             } 
56022             
56023             
56024             msg = "Building Interface " + (total  - mods.length) + 
56025                     " of " + total + 
56026                     (m.name ? (' - ' + m.name) : '');
56027                         Roo.debug && Roo.log(msg);
56028             if (!this.hideProgress) { 
56029                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
56030             }
56031             
56032          
56033             // is the module disabled?
56034             var disabled = (typeof(m.disabled) == 'function') ?
56035                 m.disabled.call(m.module.disabled) : m.disabled;    
56036             
56037             
56038             if (disabled) {
56039                 return progressRun(); // we do not update the display!
56040             }
56041             
56042             // now build 
56043             
56044                         
56045                         
56046             m.render();
56047             // it's 10 on top level, and 1 on others??? why...
56048             return progressRun.defer(10, _this);
56049              
56050         }
56051         progressRun.defer(1, _this);
56052      
56053         
56054         
56055     },
56056         
56057         
56058         /**
56059          * Event Object.
56060          *
56061          *
56062          */
56063         event: false, 
56064     /**
56065          * wrapper for event.on - aliased later..  
56066          * Typically use to register a event handler for register:
56067          *
56068          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
56069          *
56070          */
56071     on : false
56072    
56073     
56074     
56075 });
56076
56077 Roo.XComponent.event = new Roo.util.Observable({
56078                 events : { 
56079                         /**
56080                          * @event register
56081                          * Fires when an Component is registered,
56082                          * set the disable property on the Component to stop registration.
56083                          * @param {Roo.XComponent} c the component being registerd.
56084                          * 
56085                          */
56086                         'register' : true,
56087             /**
56088                          * @event beforebuild
56089                          * Fires before each Component is built
56090                          * can be used to apply permissions.
56091                          * @param {Roo.XComponent} c the component being registerd.
56092                          * 
56093                          */
56094                         'beforebuild' : true,
56095                         /**
56096                          * @event buildcomplete
56097                          * Fires on the top level element when all elements have been built
56098                          * @param {Roo.XComponent} the top level component.
56099                          */
56100                         'buildcomplete' : true
56101                         
56102                 }
56103 });
56104
56105 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
56106