roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4848 <p>
4849 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4850
4851 <p>
4852 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String} name The attribute name
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (!this.dom.hasAttribute(name)) {
9582                 return undefined;
9583             }
9584             return this.dom.getAttribute(name);
9585         }
9586         
9587         
9588         
9589     };
9590
9591     var ep = El.prototype;
9592
9593     /**
9594      * Appends an event handler (Shorthand for addListener)
9595      * @param {String}   eventName     The type of event to append
9596      * @param {Function} fn        The method the event invokes
9597      * @param {Object} scope       (optional) The scope (this object) of the fn
9598      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9599      * @method
9600      */
9601     ep.on = ep.addListener;
9602         // backwards compat
9603     ep.mon = ep.addListener;
9604
9605     /**
9606      * Removes an event handler from this element (shorthand for removeListener)
9607      * @param {String} eventName the type of event to remove
9608      * @param {Function} fn the method the event invokes
9609      * @return {Roo.Element} this
9610      * @method
9611      */
9612     ep.un = ep.removeListener;
9613
9614     /**
9615      * true to automatically adjust width and height settings for box-model issues (default to true)
9616      */
9617     ep.autoBoxAdjust = true;
9618
9619     // private
9620     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9621
9622     // private
9623     El.addUnits = function(v, defaultUnit){
9624         if(v === "" || v == "auto"){
9625             return v;
9626         }
9627         if(v === undefined){
9628             return '';
9629         }
9630         if(typeof v == "number" || !El.unitPattern.test(v)){
9631             return v + (defaultUnit || 'px');
9632         }
9633         return v;
9634     };
9635
9636     // special markup used throughout Roo when box wrapping elements
9637     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9638     /**
9639      * Visibility mode constant - Use visibility to hide element
9640      * @static
9641      * @type Number
9642      */
9643     El.VISIBILITY = 1;
9644     /**
9645      * Visibility mode constant - Use display to hide element
9646      * @static
9647      * @type Number
9648      */
9649     El.DISPLAY = 2;
9650
9651     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9652     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9653     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9654
9655
9656
9657     /**
9658      * @private
9659      */
9660     El.cache = {};
9661
9662     var docEl;
9663
9664     /**
9665      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9666      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9667      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9668      * @return {Element} The Element object
9669      * @static
9670      */
9671     El.get = function(el){
9672         var ex, elm, id;
9673         if(!el){ return null; }
9674         if(typeof el == "string"){ // element id
9675             if(!(elm = document.getElementById(el))){
9676                 return null;
9677             }
9678             if(ex = El.cache[el]){
9679                 ex.dom = elm;
9680             }else{
9681                 ex = El.cache[el] = new El(elm);
9682             }
9683             return ex;
9684         }else if(el.tagName){ // dom element
9685             if(!(id = el.id)){
9686                 id = Roo.id(el);
9687             }
9688             if(ex = El.cache[id]){
9689                 ex.dom = el;
9690             }else{
9691                 ex = El.cache[id] = new El(el);
9692             }
9693             return ex;
9694         }else if(el instanceof El){
9695             if(el != docEl){
9696                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9697                                                               // catch case where it hasn't been appended
9698                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9699             }
9700             return el;
9701         }else if(el.isComposite){
9702             return el;
9703         }else if(el instanceof Array){
9704             return El.select(el);
9705         }else if(el == document){
9706             // create a bogus element object representing the document object
9707             if(!docEl){
9708                 var f = function(){};
9709                 f.prototype = El.prototype;
9710                 docEl = new f();
9711                 docEl.dom = document;
9712             }
9713             return docEl;
9714         }
9715         return null;
9716     };
9717
9718     // private
9719     El.uncache = function(el){
9720         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9721             if(a[i]){
9722                 delete El.cache[a[i].id || a[i]];
9723             }
9724         }
9725     };
9726
9727     // private
9728     // Garbage collection - uncache elements/purge listeners on orphaned elements
9729     // so we don't hold a reference and cause the browser to retain them
9730     El.garbageCollect = function(){
9731         if(!Roo.enableGarbageCollector){
9732             clearInterval(El.collectorThread);
9733             return;
9734         }
9735         for(var eid in El.cache){
9736             var el = El.cache[eid], d = el.dom;
9737             // -------------------------------------------------------
9738             // Determining what is garbage:
9739             // -------------------------------------------------------
9740             // !d
9741             // dom node is null, definitely garbage
9742             // -------------------------------------------------------
9743             // !d.parentNode
9744             // no parentNode == direct orphan, definitely garbage
9745             // -------------------------------------------------------
9746             // !d.offsetParent && !document.getElementById(eid)
9747             // display none elements have no offsetParent so we will
9748             // also try to look it up by it's id. However, check
9749             // offsetParent first so we don't do unneeded lookups.
9750             // This enables collection of elements that are not orphans
9751             // directly, but somewhere up the line they have an orphan
9752             // parent.
9753             // -------------------------------------------------------
9754             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9755                 delete El.cache[eid];
9756                 if(d && Roo.enableListenerCollection){
9757                     E.purgeElement(d);
9758                 }
9759             }
9760         }
9761     }
9762     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9763
9764
9765     // dom is optional
9766     El.Flyweight = function(dom){
9767         this.dom = dom;
9768     };
9769     El.Flyweight.prototype = El.prototype;
9770
9771     El._flyweights = {};
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * @param {String/HTMLElement} el The dom node or id
9776      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9777      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9778      * @static
9779      * @return {Element} The shared Element object
9780      */
9781     El.fly = function(el, named){
9782         named = named || '_global';
9783         el = Roo.getDom(el);
9784         if(!el){
9785             return null;
9786         }
9787         if(!El._flyweights[named]){
9788             El._flyweights[named] = new El.Flyweight();
9789         }
9790         El._flyweights[named].dom = el;
9791         return El._flyweights[named];
9792     };
9793
9794     /**
9795      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9796      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9797      * Shorthand of {@link Roo.Element#get}
9798      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9799      * @return {Element} The Element object
9800      * @member Roo
9801      * @method get
9802      */
9803     Roo.get = El.get;
9804     /**
9805      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9806      * the dom node can be overwritten by other code.
9807      * Shorthand of {@link Roo.Element#fly}
9808      * @param {String/HTMLElement} el The dom node or id
9809      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9810      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9811      * @static
9812      * @return {Element} The shared Element object
9813      * @member Roo
9814      * @method fly
9815      */
9816     Roo.fly = El.fly;
9817
9818     // speedy lookup for elements never to box adjust
9819     var noBoxAdjust = Roo.isStrict ? {
9820         select:1
9821     } : {
9822         input:1, select:1, textarea:1
9823     };
9824     if(Roo.isIE || Roo.isGecko){
9825         noBoxAdjust['button'] = 1;
9826     }
9827
9828
9829     Roo.EventManager.on(window, 'unload', function(){
9830         delete El.cache;
9831         delete El._flyweights;
9832     });
9833 })();
9834
9835
9836
9837
9838 if(Roo.DomQuery){
9839     Roo.Element.selectorFunction = Roo.DomQuery.select;
9840 }
9841
9842 Roo.Element.select = function(selector, unique, root){
9843     var els;
9844     if(typeof selector == "string"){
9845         els = Roo.Element.selectorFunction(selector, root);
9846     }else if(selector.length !== undefined){
9847         els = selector;
9848     }else{
9849         throw "Invalid selector";
9850     }
9851     if(unique === true){
9852         return new Roo.CompositeElement(els);
9853     }else{
9854         return new Roo.CompositeElementLite(els);
9855     }
9856 };
9857 /**
9858  * Selects elements based on the passed CSS selector to enable working on them as 1.
9859  * @param {String/Array} selector The CSS selector or an array of elements
9860  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9861  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9862  * @return {CompositeElementLite/CompositeElement}
9863  * @member Roo
9864  * @method select
9865  */
9866 Roo.select = Roo.Element.select;
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881 /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893
9894 //Notifies Element that fx methods are available
9895 Roo.enableFx = true;
9896
9897 /**
9898  * @class Roo.Fx
9899  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9900  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9901  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9902  * Element effects to work.</p><br/>
9903  *
9904  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9905  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9906  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9907  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9908  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9909  * expected results and should be done with care.</p><br/>
9910  *
9911  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9912  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9913 <pre>
9914 Value  Description
9915 -----  -----------------------------
9916 tl     The top left corner
9917 t      The center of the top edge
9918 tr     The top right corner
9919 l      The center of the left edge
9920 r      The center of the right edge
9921 bl     The bottom left corner
9922 b      The center of the bottom edge
9923 br     The bottom right corner
9924 </pre>
9925  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9926  * below are common options that can be passed to any Fx method.</b>
9927  * @cfg {Function} callback A function called when the effect is finished
9928  * @cfg {Object} scope The scope of the effect function
9929  * @cfg {String} easing A valid Easing value for the effect
9930  * @cfg {String} afterCls A css class to apply after the effect
9931  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9932  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9933  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9934  * effects that end with the element being visually hidden, ignored otherwise)
9935  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9936  * a function which returns such a specification that will be applied to the Element after the effect finishes
9937  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9938  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9939  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9940  */
9941 Roo.Fx = {
9942         /**
9943          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9944          * origin for the slide effect.  This function automatically handles wrapping the element with
9945          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element in from the top
9949 el.slideIn();
9950
9951 // custom: slide the element in from the right with a 2-second duration
9952 el.slideIn('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideIn('t', {
9956     easing: 'easeOut',
9957     duration: .5
9958 });
9959 </code></pre>
9960          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9961          * @param {Object} options (optional) Object literal with any of the Fx config options
9962          * @return {Roo.Element} The Element
9963          */
9964     slideIn : function(anchor, o){
9965         var el = this.getFxEl();
9966         o = o || {};
9967
9968         el.queueFx(o, function(){
9969
9970             anchor = anchor || "t";
9971
9972             // fix display to visibility
9973             this.fixDisplay();
9974
9975             // restore values after effect
9976             var r = this.getFxRestore();
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "hidden");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             // clear out temp styles after slide and unwrap
9989             var after = function(){
9990                 el.fxUnwrap(wrap, r.pos, o);
9991                 st.width = r.width;
9992                 st.height = r.height;
9993                 el.afterFx(o);
9994             };
9995             // time to calc the positions
9996             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9997
9998             switch(anchor.toLowerCase()){
9999                 case "t":
10000                     wrap.setSize(b.width, 0);
10001                     st.left = st.bottom = "0";
10002                     a = {height: bh};
10003                 break;
10004                 case "l":
10005                     wrap.setSize(0, b.height);
10006                     st.right = st.top = "0";
10007                     a = {width: bw};
10008                 break;
10009                 case "r":
10010                     wrap.setSize(0, b.height);
10011                     wrap.setX(b.right);
10012                     st.left = st.top = "0";
10013                     a = {width: bw, points: pt};
10014                 break;
10015                 case "b":
10016                     wrap.setSize(b.width, 0);
10017                     wrap.setY(b.bottom);
10018                     st.left = st.top = "0";
10019                     a = {height: bh, points: pt};
10020                 break;
10021                 case "tl":
10022                     wrap.setSize(0, 0);
10023                     st.right = st.bottom = "0";
10024                     a = {width: bw, height: bh};
10025                 break;
10026                 case "bl":
10027                     wrap.setSize(0, 0);
10028                     wrap.setY(b.y+b.height);
10029                     st.right = st.top = "0";
10030                     a = {width: bw, height: bh, points: pt};
10031                 break;
10032                 case "br":
10033                     wrap.setSize(0, 0);
10034                     wrap.setXY([b.right, b.bottom]);
10035                     st.left = st.top = "0";
10036                     a = {width: bw, height: bh, points: pt};
10037                 break;
10038                 case "tr":
10039                     wrap.setSize(0, 0);
10040                     wrap.setX(b.x+b.width);
10041                     st.left = st.bottom = "0";
10042                     a = {width: bw, height: bh, points: pt};
10043                 break;
10044             }
10045             this.dom.style.visibility = "visible";
10046             wrap.show();
10047
10048             arguments.callee.anim = wrap.fxanim(a,
10049                 o,
10050                 'motion',
10051                 .5,
10052                 'easeOut', after);
10053         });
10054         return this;
10055     },
10056     
10057         /**
10058          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10059          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10060          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10061          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10062          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10063          * Usage:
10064          *<pre><code>
10065 // default: slide the element out to the top
10066 el.slideOut();
10067
10068 // custom: slide the element out to the right with a 2-second duration
10069 el.slideOut('r', { duration: 2 });
10070
10071 // common config options shown with default values
10072 el.slideOut('t', {
10073     easing: 'easeOut',
10074     duration: .5,
10075     remove: false,
10076     useDisplay: false
10077 });
10078 </code></pre>
10079          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10080          * @param {Object} options (optional) Object literal with any of the Fx config options
10081          * @return {Roo.Element} The Element
10082          */
10083     slideOut : function(anchor, o){
10084         var el = this.getFxEl();
10085         o = o || {};
10086
10087         el.queueFx(o, function(){
10088
10089             anchor = anchor || "t";
10090
10091             // restore values after effect
10092             var r = this.getFxRestore();
10093             
10094             var b = this.getBox();
10095             // fixed size for slide
10096             this.setSize(b);
10097
10098             // wrap if needed
10099             var wrap = this.fxWrap(r.pos, o, "visible");
10100
10101             var st = this.dom.style;
10102             st.visibility = "visible";
10103             st.position = "absolute";
10104
10105             wrap.setSize(b);
10106
10107             var after = function(){
10108                 if(o.useDisplay){
10109                     el.setDisplayed(false);
10110                 }else{
10111                     el.hide();
10112                 }
10113
10114                 el.fxUnwrap(wrap, r.pos, o);
10115
10116                 st.width = r.width;
10117                 st.height = r.height;
10118
10119                 el.afterFx(o);
10120             };
10121
10122             var a, zero = {to: 0};
10123             switch(anchor.toLowerCase()){
10124                 case "t":
10125                     st.left = st.bottom = "0";
10126                     a = {height: zero};
10127                 break;
10128                 case "l":
10129                     st.right = st.top = "0";
10130                     a = {width: zero};
10131                 break;
10132                 case "r":
10133                     st.left = st.top = "0";
10134                     a = {width: zero, points: {to:[b.right, b.y]}};
10135                 break;
10136                 case "b":
10137                     st.left = st.top = "0";
10138                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10139                 break;
10140                 case "tl":
10141                     st.right = st.bottom = "0";
10142                     a = {width: zero, height: zero};
10143                 break;
10144                 case "bl":
10145                     st.right = st.top = "0";
10146                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "br":
10149                     st.left = st.top = "0";
10150                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10151                 break;
10152                 case "tr":
10153                     st.left = st.bottom = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10155                 break;
10156             }
10157
10158             arguments.callee.anim = wrap.fxanim(a,
10159                 o,
10160                 'motion',
10161                 .5,
10162                 "easeOut", after);
10163         });
10164         return this;
10165     },
10166
10167         /**
10168          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10169          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10170          * The element must be removed from the DOM using the 'remove' config option if desired.
10171          * Usage:
10172          *<pre><code>
10173 // default
10174 el.puff();
10175
10176 // common config options shown with default values
10177 el.puff({
10178     easing: 'easeOut',
10179     duration: .5,
10180     remove: false,
10181     useDisplay: false
10182 });
10183 </code></pre>
10184          * @param {Object} options (optional) Object literal with any of the Fx config options
10185          * @return {Roo.Element} The Element
10186          */
10187     puff : function(o){
10188         var el = this.getFxEl();
10189         o = o || {};
10190
10191         el.queueFx(o, function(){
10192             this.clearOpacity();
10193             this.show();
10194
10195             // restore values after effect
10196             var r = this.getFxRestore();
10197             var st = this.dom.style;
10198
10199             var after = function(){
10200                 if(o.useDisplay){
10201                     el.setDisplayed(false);
10202                 }else{
10203                     el.hide();
10204                 }
10205
10206                 el.clearOpacity();
10207
10208                 el.setPositioning(r.pos);
10209                 st.width = r.width;
10210                 st.height = r.height;
10211                 st.fontSize = '';
10212                 el.afterFx(o);
10213             };
10214
10215             var width = this.getWidth();
10216             var height = this.getHeight();
10217
10218             arguments.callee.anim = this.fxanim({
10219                     width : {to: this.adjustWidth(width * 2)},
10220                     height : {to: this.adjustHeight(height * 2)},
10221                     points : {by: [-(width * .5), -(height * .5)]},
10222                     opacity : {to: 0},
10223                     fontSize: {to:200, unit: "%"}
10224                 },
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10235          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10236          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.switchOff();
10241
10242 // all config options shown with default values
10243 el.switchOff({
10244     easing: 'easeIn',
10245     duration: .3,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     switchOff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.clip();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273                 el.setPositioning(r.pos);
10274                 st.width = r.width;
10275                 st.height = r.height;
10276
10277                 el.afterFx(o);
10278             };
10279
10280             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10281                 this.clearOpacity();
10282                 (function(){
10283                     this.fxanim({
10284                         height:{to:1},
10285                         points:{by:[0, this.getHeight() * .5]}
10286                     }, o, 'motion', 0.3, 'easeIn', after);
10287                 }).defer(100, this);
10288             });
10289         });
10290         return this;
10291     },
10292
10293     /**
10294      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10295      * changed using the "attr" config option) and then fading back to the original color. If no original
10296      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10297      * Usage:
10298 <pre><code>
10299 // default: highlight background to yellow
10300 el.highlight();
10301
10302 // custom: highlight foreground text to blue for 2 seconds
10303 el.highlight("0000ff", { attr: 'color', duration: 2 });
10304
10305 // common config options shown with default values
10306 el.highlight("ffff9c", {
10307     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10308     endColor: (current color) or "ffffff",
10309     easing: 'easeIn',
10310     duration: 1
10311 });
10312 </code></pre>
10313      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10314      * @param {Object} options (optional) Object literal with any of the Fx config options
10315      * @return {Roo.Element} The Element
10316      */ 
10317     highlight : function(color, o){
10318         var el = this.getFxEl();
10319         o = o || {};
10320
10321         el.queueFx(o, function(){
10322             color = color || "ffff9c";
10323             attr = o.attr || "backgroundColor";
10324
10325             this.clearOpacity();
10326             this.show();
10327
10328             var origColor = this.getColor(attr);
10329             var restoreColor = this.dom.style[attr];
10330             endColor = (o.endColor || origColor) || "ffffff";
10331
10332             var after = function(){
10333                 el.dom.style[attr] = restoreColor;
10334                 el.afterFx(o);
10335             };
10336
10337             var a = {};
10338             a[attr] = {from: color, to: endColor};
10339             arguments.callee.anim = this.fxanim(a,
10340                 o,
10341                 'color',
10342                 1,
10343                 'easeIn', after);
10344         });
10345         return this;
10346     },
10347
10348    /**
10349     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10350     * Usage:
10351 <pre><code>
10352 // default: a single light blue ripple
10353 el.frame();
10354
10355 // custom: 3 red ripples lasting 3 seconds total
10356 el.frame("ff0000", 3, { duration: 3 });
10357
10358 // common config options shown with default values
10359 el.frame("C3DAF9", 1, {
10360     duration: 1 //duration of entire animation (not each individual ripple)
10361     // Note: Easing is not configurable and will be ignored if included
10362 });
10363 </code></pre>
10364     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10365     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10366     * @param {Object} options (optional) Object literal with any of the Fx config options
10367     * @return {Roo.Element} The Element
10368     */
10369     frame : function(color, count, o){
10370         var el = this.getFxEl();
10371         o = o || {};
10372
10373         el.queueFx(o, function(){
10374             color = color || "#C3DAF9";
10375             if(color.length == 6){
10376                 color = "#" + color;
10377             }
10378             count = count || 1;
10379             duration = o.duration || 1;
10380             this.show();
10381
10382             var b = this.getBox();
10383             var animFn = function(){
10384                 var proxy = this.createProxy({
10385
10386                      style:{
10387                         visbility:"hidden",
10388                         position:"absolute",
10389                         "z-index":"35000", // yee haw
10390                         border:"0px solid " + color
10391                      }
10392                   });
10393                 var scale = Roo.isBorderBox ? 2 : 1;
10394                 proxy.animate({
10395                     top:{from:b.y, to:b.y - 20},
10396                     left:{from:b.x, to:b.x - 20},
10397                     borderWidth:{from:0, to:10},
10398                     opacity:{from:1, to:0},
10399                     height:{from:b.height, to:(b.height + (20*scale))},
10400                     width:{from:b.width, to:(b.width + (20*scale))}
10401                 }, duration, function(){
10402                     proxy.remove();
10403                 });
10404                 if(--count > 0){
10405                      animFn.defer((duration/2)*1000, this);
10406                 }else{
10407                     el.afterFx(o);
10408                 }
10409             };
10410             animFn.call(this);
10411         });
10412         return this;
10413     },
10414
10415    /**
10416     * Creates a pause before any subsequent queued effects begin.  If there are
10417     * no effects queued after the pause it will have no effect.
10418     * Usage:
10419 <pre><code>
10420 el.pause(1);
10421 </code></pre>
10422     * @param {Number} seconds The length of time to pause (in seconds)
10423     * @return {Roo.Element} The Element
10424     */
10425     pause : function(seconds){
10426         var el = this.getFxEl();
10427         var o = {};
10428
10429         el.queueFx(o, function(){
10430             setTimeout(function(){
10431                 el.afterFx(o);
10432             }, seconds * 1000);
10433         });
10434         return this;
10435     },
10436
10437    /**
10438     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10439     * using the "endOpacity" config option.
10440     * Usage:
10441 <pre><code>
10442 // default: fade in from opacity 0 to 100%
10443 el.fadeIn();
10444
10445 // custom: fade in from opacity 0 to 75% over 2 seconds
10446 el.fadeIn({ endOpacity: .75, duration: 2});
10447
10448 // common config options shown with default values
10449 el.fadeIn({
10450     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10451     easing: 'easeOut',
10452     duration: .5
10453 });
10454 </code></pre>
10455     * @param {Object} options (optional) Object literal with any of the Fx config options
10456     * @return {Roo.Element} The Element
10457     */
10458     fadeIn : function(o){
10459         var el = this.getFxEl();
10460         o = o || {};
10461         el.queueFx(o, function(){
10462             this.setOpacity(0);
10463             this.fixDisplay();
10464             this.dom.style.visibility = 'visible';
10465             var to = o.endOpacity || 1;
10466             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10467                 o, null, .5, "easeOut", function(){
10468                 if(to == 1){
10469                     this.clearOpacity();
10470                 }
10471                 el.afterFx(o);
10472             });
10473         });
10474         return this;
10475     },
10476
10477    /**
10478     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10479     * using the "endOpacity" config option.
10480     * Usage:
10481 <pre><code>
10482 // default: fade out from the element's current opacity to 0
10483 el.fadeOut();
10484
10485 // custom: fade out from the element's current opacity to 25% over 2 seconds
10486 el.fadeOut({ endOpacity: .25, duration: 2});
10487
10488 // common config options shown with default values
10489 el.fadeOut({
10490     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10491     easing: 'easeOut',
10492     duration: .5
10493     remove: false,
10494     useDisplay: false
10495 });
10496 </code></pre>
10497     * @param {Object} options (optional) Object literal with any of the Fx config options
10498     * @return {Roo.Element} The Element
10499     */
10500     fadeOut : function(o){
10501         var el = this.getFxEl();
10502         o = o || {};
10503         el.queueFx(o, function(){
10504             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10505                 o, null, .5, "easeOut", function(){
10506                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10507                      this.dom.style.display = "none";
10508                 }else{
10509                      this.dom.style.visibility = "hidden";
10510                 }
10511                 this.clearOpacity();
10512                 el.afterFx(o);
10513             });
10514         });
10515         return this;
10516     },
10517
10518    /**
10519     * Animates the transition of an element's dimensions from a starting height/width
10520     * to an ending height/width.
10521     * Usage:
10522 <pre><code>
10523 // change height and width to 100x100 pixels
10524 el.scale(100, 100);
10525
10526 // common config options shown with default values.  The height and width will default to
10527 // the element's existing values if passed as null.
10528 el.scale(
10529     [element's width],
10530     [element's height], {
10531     easing: 'easeOut',
10532     duration: .35
10533 });
10534 </code></pre>
10535     * @param {Number} width  The new width (pass undefined to keep the original width)
10536     * @param {Number} height  The new height (pass undefined to keep the original height)
10537     * @param {Object} options (optional) Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     scale : function(w, h, o){
10541         this.shift(Roo.apply({}, o, {
10542             width: w,
10543             height: h
10544         }));
10545         return this;
10546     },
10547
10548    /**
10549     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10550     * Any of these properties not specified in the config object will not be changed.  This effect 
10551     * requires that at least one new dimension, position or opacity setting must be passed in on
10552     * the config object in order for the function to have any effect.
10553     * Usage:
10554 <pre><code>
10555 // slide the element horizontally to x position 200 while changing the height and opacity
10556 el.shift({ x: 200, height: 50, opacity: .8 });
10557
10558 // common config options shown with default values.
10559 el.shift({
10560     width: [element's width],
10561     height: [element's height],
10562     x: [element's x position],
10563     y: [element's y position],
10564     opacity: [element's opacity],
10565     easing: 'easeOut',
10566     duration: .35
10567 });
10568 </code></pre>
10569     * @param {Object} options  Object literal with any of the Fx config options
10570     * @return {Roo.Element} The Element
10571     */
10572     shift : function(o){
10573         var el = this.getFxEl();
10574         o = o || {};
10575         el.queueFx(o, function(){
10576             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10577             if(w !== undefined){
10578                 a.width = {to: this.adjustWidth(w)};
10579             }
10580             if(h !== undefined){
10581                 a.height = {to: this.adjustHeight(h)};
10582             }
10583             if(x !== undefined || y !== undefined){
10584                 a.points = {to: [
10585                     x !== undefined ? x : this.getX(),
10586                     y !== undefined ? y : this.getY()
10587                 ]};
10588             }
10589             if(op !== undefined){
10590                 a.opacity = {to: op};
10591             }
10592             if(o.xy !== undefined){
10593                 a.points = {to: o.xy};
10594             }
10595             arguments.callee.anim = this.fxanim(a,
10596                 o, 'motion', .35, "easeOut", function(){
10597                 el.afterFx(o);
10598             });
10599         });
10600         return this;
10601     },
10602
10603         /**
10604          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10605          * ending point of the effect.
10606          * Usage:
10607          *<pre><code>
10608 // default: slide the element downward while fading out
10609 el.ghost();
10610
10611 // custom: slide the element out to the right with a 2-second duration
10612 el.ghost('r', { duration: 2 });
10613
10614 // common config options shown with default values
10615 el.ghost('b', {
10616     easing: 'easeOut',
10617     duration: .5
10618     remove: false,
10619     useDisplay: false
10620 });
10621 </code></pre>
10622          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10623          * @param {Object} options (optional) Object literal with any of the Fx config options
10624          * @return {Roo.Element} The Element
10625          */
10626     ghost : function(anchor, o){
10627         var el = this.getFxEl();
10628         o = o || {};
10629
10630         el.queueFx(o, function(){
10631             anchor = anchor || "b";
10632
10633             // restore values after effect
10634             var r = this.getFxRestore();
10635             var w = this.getWidth(),
10636                 h = this.getHeight();
10637
10638             var st = this.dom.style;
10639
10640             var after = function(){
10641                 if(o.useDisplay){
10642                     el.setDisplayed(false);
10643                 }else{
10644                     el.hide();
10645                 }
10646
10647                 el.clearOpacity();
10648                 el.setPositioning(r.pos);
10649                 st.width = r.width;
10650                 st.height = r.height;
10651
10652                 el.afterFx(o);
10653             };
10654
10655             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10656             switch(anchor.toLowerCase()){
10657                 case "t":
10658                     pt.by = [0, -h];
10659                 break;
10660                 case "l":
10661                     pt.by = [-w, 0];
10662                 break;
10663                 case "r":
10664                     pt.by = [w, 0];
10665                 break;
10666                 case "b":
10667                     pt.by = [0, h];
10668                 break;
10669                 case "tl":
10670                     pt.by = [-w, -h];
10671                 break;
10672                 case "bl":
10673                     pt.by = [-w, h];
10674                 break;
10675                 case "br":
10676                     pt.by = [w, h];
10677                 break;
10678                 case "tr":
10679                     pt.by = [w, -h];
10680                 break;
10681             }
10682
10683             arguments.callee.anim = this.fxanim(a,
10684                 o,
10685                 'motion',
10686                 .5,
10687                 "easeOut", after);
10688         });
10689         return this;
10690     },
10691
10692         /**
10693          * Ensures that all effects queued after syncFx is called on the element are
10694          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10695          * @return {Roo.Element} The Element
10696          */
10697     syncFx : function(){
10698         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10699             block : false,
10700             concurrent : true,
10701             stopFx : false
10702         });
10703         return this;
10704     },
10705
10706         /**
10707          * Ensures that all effects queued after sequenceFx is called on the element are
10708          * run in sequence.  This is the opposite of {@link #syncFx}.
10709          * @return {Roo.Element} The Element
10710          */
10711     sequenceFx : function(){
10712         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10713             block : false,
10714             concurrent : false,
10715             stopFx : false
10716         });
10717         return this;
10718     },
10719
10720         /* @private */
10721     nextFx : function(){
10722         var ef = this.fxQueue[0];
10723         if(ef){
10724             ef.call(this);
10725         }
10726     },
10727
10728         /**
10729          * Returns true if the element has any effects actively running or queued, else returns false.
10730          * @return {Boolean} True if element has active effects, else false
10731          */
10732     hasActiveFx : function(){
10733         return this.fxQueue && this.fxQueue[0];
10734     },
10735
10736         /**
10737          * Stops any running effects and clears the element's internal effects queue if it contains
10738          * any additional effects that haven't started yet.
10739          * @return {Roo.Element} The Element
10740          */
10741     stopFx : function(){
10742         if(this.hasActiveFx()){
10743             var cur = this.fxQueue[0];
10744             if(cur && cur.anim && cur.anim.isAnimated()){
10745                 this.fxQueue = [cur]; // clear out others
10746                 cur.anim.stop(true);
10747             }
10748         }
10749         return this;
10750     },
10751
10752         /* @private */
10753     beforeFx : function(o){
10754         if(this.hasActiveFx() && !o.concurrent){
10755            if(o.stopFx){
10756                this.stopFx();
10757                return true;
10758            }
10759            return false;
10760         }
10761         return true;
10762     },
10763
10764         /**
10765          * Returns true if the element is currently blocking so that no other effect can be queued
10766          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10767          * used to ensure that an effect initiated by a user action runs to completion prior to the
10768          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10769          * @return {Boolean} True if blocking, else false
10770          */
10771     hasFxBlock : function(){
10772         var q = this.fxQueue;
10773         return q && q[0] && q[0].block;
10774     },
10775
10776         /* @private */
10777     queueFx : function(o, fn){
10778         if(!this.fxQueue){
10779             this.fxQueue = [];
10780         }
10781         if(!this.hasFxBlock()){
10782             Roo.applyIf(o, this.fxDefaults);
10783             if(!o.concurrent){
10784                 var run = this.beforeFx(o);
10785                 fn.block = o.block;
10786                 this.fxQueue.push(fn);
10787                 if(run){
10788                     this.nextFx();
10789                 }
10790             }else{
10791                 fn.call(this);
10792             }
10793         }
10794         return this;
10795     },
10796
10797         /* @private */
10798     fxWrap : function(pos, o, vis){
10799         var wrap;
10800         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10801             var wrapXY;
10802             if(o.fixPosition){
10803                 wrapXY = this.getXY();
10804             }
10805             var div = document.createElement("div");
10806             div.style.visibility = vis;
10807             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10808             wrap.setPositioning(pos);
10809             if(wrap.getStyle("position") == "static"){
10810                 wrap.position("relative");
10811             }
10812             this.clearPositioning('auto');
10813             wrap.clip();
10814             wrap.dom.appendChild(this.dom);
10815             if(wrapXY){
10816                 wrap.setXY(wrapXY);
10817             }
10818         }
10819         return wrap;
10820     },
10821
10822         /* @private */
10823     fxUnwrap : function(wrap, pos, o){
10824         this.clearPositioning();
10825         this.setPositioning(pos);
10826         if(!o.wrap){
10827             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10828             wrap.remove();
10829         }
10830     },
10831
10832         /* @private */
10833     getFxRestore : function(){
10834         var st = this.dom.style;
10835         return {pos: this.getPositioning(), width: st.width, height : st.height};
10836     },
10837
10838         /* @private */
10839     afterFx : function(o){
10840         if(o.afterStyle){
10841             this.applyStyles(o.afterStyle);
10842         }
10843         if(o.afterCls){
10844             this.addClass(o.afterCls);
10845         }
10846         if(o.remove === true){
10847             this.remove();
10848         }
10849         Roo.callback(o.callback, o.scope, [this]);
10850         if(!o.concurrent){
10851             this.fxQueue.shift();
10852             this.nextFx();
10853         }
10854     },
10855
10856         /* @private */
10857     getFxEl : function(){ // support for composite element fx
10858         return Roo.get(this.dom);
10859     },
10860
10861         /* @private */
10862     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10863         animType = animType || 'run';
10864         opt = opt || {};
10865         var anim = Roo.lib.Anim[animType](
10866             this.dom, args,
10867             (opt.duration || defaultDur) || .35,
10868             (opt.easing || defaultEase) || 'easeOut',
10869             function(){
10870                 Roo.callback(cb, this);
10871             },
10872             this
10873         );
10874         opt.anim = anim;
10875         return anim;
10876     }
10877 };
10878
10879 // backwords compat
10880 Roo.Fx.resize = Roo.Fx.scale;
10881
10882 //When included, Roo.Fx is automatically applied to Element so that all basic
10883 //effects are available directly via the Element API
10884 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10885  * Based on:
10886  * Ext JS Library 1.1.1
10887  * Copyright(c) 2006-2007, Ext JS, LLC.
10888  *
10889  * Originally Released Under LGPL - original licence link has changed is not relivant.
10890  *
10891  * Fork - LGPL
10892  * <script type="text/javascript">
10893  */
10894
10895
10896 /**
10897  * @class Roo.CompositeElement
10898  * Standard composite class. Creates a Roo.Element for every element in the collection.
10899  * <br><br>
10900  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10901  * actions will be performed on all the elements in this collection.</b>
10902  * <br><br>
10903  * All methods return <i>this</i> and can be chained.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class", true);
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class', true);
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre>
10915  */
10916 Roo.CompositeElement = function(els){
10917     this.elements = [];
10918     this.addElements(els);
10919 };
10920 Roo.CompositeElement.prototype = {
10921     isComposite: true,
10922     addElements : function(els){
10923         if(!els) return this;
10924         if(typeof els == "string"){
10925             els = Roo.Element.selectorFunction(els);
10926         }
10927         var yels = this.elements;
10928         var index = yels.length-1;
10929         for(var i = 0, len = els.length; i < len; i++) {
10930                 yels[++index] = Roo.get(els[i]);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Clears this composite and adds the elements returned by the passed selector.
10937     * @param {String/Array} els A string CSS selector, an array of elements or an element
10938     * @return {CompositeElement} this
10939     */
10940     fill : function(els){
10941         this.elements = [];
10942         this.add(els);
10943         return this;
10944     },
10945
10946     /**
10947     * Filters this composite to only elements that match the passed selector.
10948     * @param {String} selector A string CSS selector
10949     * @return {CompositeElement} this
10950     */
10951     filter : function(selector){
10952         var els = [];
10953         this.each(function(el){
10954             if(el.is(selector)){
10955                 els[els.length] = el.dom;
10956             }
10957         });
10958         this.fill(els);
10959         return this;
10960     },
10961
10962     invoke : function(fn, args){
10963         var els = this.elements;
10964         for(var i = 0, len = els.length; i < len; i++) {
10965                 Roo.Element.prototype[fn].apply(els[i], args);
10966         }
10967         return this;
10968     },
10969     /**
10970     * Adds elements to this composite.
10971     * @param {String/Array} els A string CSS selector, an array of elements or an element
10972     * @return {CompositeElement} this
10973     */
10974     add : function(els){
10975         if(typeof els == "string"){
10976             this.addElements(Roo.Element.selectorFunction(els));
10977         }else if(els.length !== undefined){
10978             this.addElements(els);
10979         }else{
10980             this.addElements([els]);
10981         }
10982         return this;
10983     },
10984     /**
10985     * Calls the passed function passing (el, this, index) for each element in this composite.
10986     * @param {Function} fn The function to call
10987     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10988     * @return {CompositeElement} this
10989     */
10990     each : function(fn, scope){
10991         var els = this.elements;
10992         for(var i = 0, len = els.length; i < len; i++){
10993             if(fn.call(scope || els[i], els[i], this, i) === false) {
10994                 break;
10995             }
10996         }
10997         return this;
10998     },
10999
11000     /**
11001      * Returns the Element object at the specified index
11002      * @param {Number} index
11003      * @return {Roo.Element}
11004      */
11005     item : function(index){
11006         return this.elements[index] || null;
11007     },
11008
11009     /**
11010      * Returns the first Element
11011      * @return {Roo.Element}
11012      */
11013     first : function(){
11014         return this.item(0);
11015     },
11016
11017     /**
11018      * Returns the last Element
11019      * @return {Roo.Element}
11020      */
11021     last : function(){
11022         return this.item(this.elements.length-1);
11023     },
11024
11025     /**
11026      * Returns the number of elements in this composite
11027      * @return Number
11028      */
11029     getCount : function(){
11030         return this.elements.length;
11031     },
11032
11033     /**
11034      * Returns true if this composite contains the passed element
11035      * @return Boolean
11036      */
11037     contains : function(el){
11038         return this.indexOf(el) !== -1;
11039     },
11040
11041     /**
11042      * Returns true if this composite contains the passed element
11043      * @return Boolean
11044      */
11045     indexOf : function(el){
11046         return this.elements.indexOf(Roo.get(el));
11047     },
11048
11049
11050     /**
11051     * Removes the specified element(s).
11052     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11053     * or an array of any of those.
11054     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11055     * @return {CompositeElement} this
11056     */
11057     removeElement : function(el, removeDom){
11058         if(el instanceof Array){
11059             for(var i = 0, len = el.length; i < len; i++){
11060                 this.removeElement(el[i]);
11061             }
11062             return this;
11063         }
11064         var index = typeof el == 'number' ? el : this.indexOf(el);
11065         if(index !== -1){
11066             if(removeDom){
11067                 var d = this.elements[index];
11068                 if(d.dom){
11069                     d.remove();
11070                 }else{
11071                     d.parentNode.removeChild(d);
11072                 }
11073             }
11074             this.elements.splice(index, 1);
11075         }
11076         return this;
11077     },
11078
11079     /**
11080     * Replaces the specified element with the passed element.
11081     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11082     * to replace.
11083     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11084     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11085     * @return {CompositeElement} this
11086     */
11087     replaceElement : function(el, replacement, domReplace){
11088         var index = typeof el == 'number' ? el : this.indexOf(el);
11089         if(index !== -1){
11090             if(domReplace){
11091                 this.elements[index].replaceWith(replacement);
11092             }else{
11093                 this.elements.splice(index, 1, Roo.get(replacement))
11094             }
11095         }
11096         return this;
11097     },
11098
11099     /**
11100      * Removes all elements.
11101      */
11102     clear : function(){
11103         this.elements = [];
11104     }
11105 };
11106 (function(){
11107     Roo.CompositeElement.createCall = function(proto, fnName){
11108         if(!proto[fnName]){
11109             proto[fnName] = function(){
11110                 return this.invoke(fnName, arguments);
11111             };
11112         }
11113     };
11114     for(var fnName in Roo.Element.prototype){
11115         if(typeof Roo.Element.prototype[fnName] == "function"){
11116             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11117         }
11118     };
11119 })();
11120 /*
11121  * Based on:
11122  * Ext JS Library 1.1.1
11123  * Copyright(c) 2006-2007, Ext JS, LLC.
11124  *
11125  * Originally Released Under LGPL - original licence link has changed is not relivant.
11126  *
11127  * Fork - LGPL
11128  * <script type="text/javascript">
11129  */
11130
11131 /**
11132  * @class Roo.CompositeElementLite
11133  * @extends Roo.CompositeElement
11134  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11135  <pre><code>
11136  var els = Roo.select("#some-el div.some-class");
11137  // or select directly from an existing element
11138  var el = Roo.get('some-el');
11139  el.select('div.some-class');
11140
11141  els.setWidth(100); // all elements become 100 width
11142  els.hide(true); // all elements fade out and hide
11143  // or
11144  els.setWidth(100).hide(true);
11145  </code></pre><br><br>
11146  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11147  * actions will be performed on all the elements in this collection.</b>
11148  */
11149 Roo.CompositeElementLite = function(els){
11150     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11151     this.el = new Roo.Element.Flyweight();
11152 };
11153 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11154     addElements : function(els){
11155         if(els){
11156             if(els instanceof Array){
11157                 this.elements = this.elements.concat(els);
11158             }else{
11159                 var yels = this.elements;
11160                 var index = yels.length-1;
11161                 for(var i = 0, len = els.length; i < len; i++) {
11162                     yels[++index] = els[i];
11163                 }
11164             }
11165         }
11166         return this;
11167     },
11168     invoke : function(fn, args){
11169         var els = this.elements;
11170         var el = this.el;
11171         for(var i = 0, len = els.length; i < len; i++) {
11172             el.dom = els[i];
11173                 Roo.Element.prototype[fn].apply(el, args);
11174         }
11175         return this;
11176     },
11177     /**
11178      * Returns a flyweight Element of the dom element object at the specified index
11179      * @param {Number} index
11180      * @return {Roo.Element}
11181      */
11182     item : function(index){
11183         if(!this.elements[index]){
11184             return null;
11185         }
11186         this.el.dom = this.elements[index];
11187         return this.el;
11188     },
11189
11190     // fixes scope with flyweight
11191     addListener : function(eventName, handler, scope, opt){
11192         var els = this.elements;
11193         for(var i = 0, len = els.length; i < len; i++) {
11194             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11195         }
11196         return this;
11197     },
11198
11199     /**
11200     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11201     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11202     * a reference to the dom node, use el.dom.</b>
11203     * @param {Function} fn The function to call
11204     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11205     * @return {CompositeElement} this
11206     */
11207     each : function(fn, scope){
11208         var els = this.elements;
11209         var el = this.el;
11210         for(var i = 0, len = els.length; i < len; i++){
11211             el.dom = els[i];
11212                 if(fn.call(scope || el, el, this, i) === false){
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     indexOf : function(el){
11220         return this.elements.indexOf(Roo.getDom(el));
11221     },
11222
11223     replaceElement : function(el, replacement, domReplace){
11224         var index = typeof el == 'number' ? el : this.indexOf(el);
11225         if(index !== -1){
11226             replacement = Roo.getDom(replacement);
11227             if(domReplace){
11228                 var d = this.elements[index];
11229                 d.parentNode.insertBefore(replacement, d);
11230                 d.parentNode.removeChild(d);
11231             }
11232             this.elements.splice(index, 1, replacement);
11233         }
11234         return this;
11235     }
11236 });
11237 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11238
11239 /*
11240  * Based on:
11241  * Ext JS Library 1.1.1
11242  * Copyright(c) 2006-2007, Ext JS, LLC.
11243  *
11244  * Originally Released Under LGPL - original licence link has changed is not relivant.
11245  *
11246  * Fork - LGPL
11247  * <script type="text/javascript">
11248  */
11249
11250  
11251
11252 /**
11253  * @class Roo.data.Connection
11254  * @extends Roo.util.Observable
11255  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11256  * either to a configured URL, or to a URL specified at request time.<br><br>
11257  * <p>
11258  * Requests made by this class are asynchronous, and will return immediately. No data from
11259  * the server will be available to the statement immediately following the {@link #request} call.
11260  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11261  * <p>
11262  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11263  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11264  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11265  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11266  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11267  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11268  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11269  * standard DOM methods.
11270  * @constructor
11271  * @param {Object} config a configuration object.
11272  */
11273 Roo.data.Connection = function(config){
11274     Roo.apply(this, config);
11275     this.addEvents({
11276         /**
11277          * @event beforerequest
11278          * Fires before a network request is made to retrieve a data object.
11279          * @param {Connection} conn This Connection object.
11280          * @param {Object} options The options config object passed to the {@link #request} method.
11281          */
11282         "beforerequest" : true,
11283         /**
11284          * @event requestcomplete
11285          * Fires if the request was successfully completed.
11286          * @param {Connection} conn This Connection object.
11287          * @param {Object} response The XHR object containing the response data.
11288          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11289          * @param {Object} options The options config object passed to the {@link #request} method.
11290          */
11291         "requestcomplete" : true,
11292         /**
11293          * @event requestexception
11294          * Fires if an error HTTP status was returned from the server.
11295          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11296          * @param {Connection} conn This Connection object.
11297          * @param {Object} response The XHR object containing the response data.
11298          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11299          * @param {Object} options The options config object passed to the {@link #request} method.
11300          */
11301         "requestexception" : true
11302     });
11303     Roo.data.Connection.superclass.constructor.call(this);
11304 };
11305
11306 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11307     /**
11308      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11309      */
11310     /**
11311      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11312      * extra parameters to each request made by this object. (defaults to undefined)
11313      */
11314     /**
11315      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11316      *  to each request made by this object. (defaults to undefined)
11317      */
11318     /**
11319      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11320      */
11321     /**
11322      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11323      */
11324     timeout : 30000,
11325     /**
11326      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11327      * @type Boolean
11328      */
11329     autoAbort:false,
11330
11331     /**
11332      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11333      * @type Boolean
11334      */
11335     disableCaching: true,
11336
11337     /**
11338      * Sends an HTTP request to a remote server.
11339      * @param {Object} options An object which may contain the following properties:<ul>
11340      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11341      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11342      * request, a url encoded string or a function to call to get either.</li>
11343      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11344      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11345      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11346      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11347      * <li>options {Object} The parameter to the request call.</li>
11348      * <li>success {Boolean} True if the request succeeded.</li>
11349      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11350      * </ul></li>
11351      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11352      * The callback is passed the following parameters:<ul>
11353      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11354      * <li>options {Object} The parameter to the request call.</li>
11355      * </ul></li>
11356      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11357      * The callback is passed the following parameters:<ul>
11358      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11359      * <li>options {Object} The parameter to the request call.</li>
11360      * </ul></li>
11361      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11362      * for the callback function. Defaults to the browser window.</li>
11363      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11364      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11365      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11366      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11367      * params for the post data. Any params will be appended to the URL.</li>
11368      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11369      * </ul>
11370      * @return {Number} transactionId
11371      */
11372     request : function(o){
11373         if(this.fireEvent("beforerequest", this, o) !== false){
11374             var p = o.params;
11375
11376             if(typeof p == "function"){
11377                 p = p.call(o.scope||window, o);
11378             }
11379             if(typeof p == "object"){
11380                 p = Roo.urlEncode(o.params);
11381             }
11382             if(this.extraParams){
11383                 var extras = Roo.urlEncode(this.extraParams);
11384                 p = p ? (p + '&' + extras) : extras;
11385             }
11386
11387             var url = o.url || this.url;
11388             if(typeof url == 'function'){
11389                 url = url.call(o.scope||window, o);
11390             }
11391
11392             if(o.form){
11393                 var form = Roo.getDom(o.form);
11394                 url = url || form.action;
11395
11396                 var enctype = form.getAttribute("enctype");
11397                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11398                     return this.doFormUpload(o, p, url);
11399                 }
11400                 var f = Roo.lib.Ajax.serializeForm(form);
11401                 p = p ? (p + '&' + f) : f;
11402             }
11403
11404             var hs = o.headers;
11405             if(this.defaultHeaders){
11406                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11407                 if(!o.headers){
11408                     o.headers = hs;
11409                 }
11410             }
11411
11412             var cb = {
11413                 success: this.handleResponse,
11414                 failure: this.handleFailure,
11415                 scope: this,
11416                 argument: {options: o},
11417                 timeout : o.timeout || this.timeout
11418             };
11419
11420             var method = o.method||this.method||(p ? "POST" : "GET");
11421
11422             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11423                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11424             }
11425
11426             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11427                 if(o.autoAbort){
11428                     this.abort();
11429                 }
11430             }else if(this.autoAbort !== false){
11431                 this.abort();
11432             }
11433
11434             if((method == 'GET' && p) || o.xmlData){
11435                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11436                 p = '';
11437             }
11438             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11439             return this.transId;
11440         }else{
11441             Roo.callback(o.callback, o.scope, [o, null, null]);
11442             return null;
11443         }
11444     },
11445
11446     /**
11447      * Determine whether this object has a request outstanding.
11448      * @param {Number} transactionId (Optional) defaults to the last transaction
11449      * @return {Boolean} True if there is an outstanding request.
11450      */
11451     isLoading : function(transId){
11452         if(transId){
11453             return Roo.lib.Ajax.isCallInProgress(transId);
11454         }else{
11455             return this.transId ? true : false;
11456         }
11457     },
11458
11459     /**
11460      * Aborts any outstanding request.
11461      * @param {Number} transactionId (Optional) defaults to the last transaction
11462      */
11463     abort : function(transId){
11464         if(transId || this.isLoading()){
11465             Roo.lib.Ajax.abort(transId || this.transId);
11466         }
11467     },
11468
11469     // private
11470     handleResponse : function(response){
11471         this.transId = false;
11472         var options = response.argument.options;
11473         response.argument = options ? options.argument : null;
11474         this.fireEvent("requestcomplete", this, response, options);
11475         Roo.callback(options.success, options.scope, [response, options]);
11476         Roo.callback(options.callback, options.scope, [options, true, response]);
11477     },
11478
11479     // private
11480     handleFailure : function(response, e){
11481         this.transId = false;
11482         var options = response.argument.options;
11483         response.argument = options ? options.argument : null;
11484         this.fireEvent("requestexception", this, response, options, e);
11485         Roo.callback(options.failure, options.scope, [response, options]);
11486         Roo.callback(options.callback, options.scope, [options, false, response]);
11487     },
11488
11489     // private
11490     doFormUpload : function(o, ps, url){
11491         var id = Roo.id();
11492         var frame = document.createElement('iframe');
11493         frame.id = id;
11494         frame.name = id;
11495         frame.className = 'x-hidden';
11496         if(Roo.isIE){
11497             frame.src = Roo.SSL_SECURE_URL;
11498         }
11499         document.body.appendChild(frame);
11500
11501         if(Roo.isIE){
11502            document.frames[id].name = id;
11503         }
11504
11505         var form = Roo.getDom(o.form);
11506         form.target = id;
11507         form.method = 'POST';
11508         form.enctype = form.encoding = 'multipart/form-data';
11509         if(url){
11510             form.action = url;
11511         }
11512
11513         var hiddens, hd;
11514         if(ps){ // add dynamic params
11515             hiddens = [];
11516             ps = Roo.urlDecode(ps, false);
11517             for(var k in ps){
11518                 if(ps.hasOwnProperty(k)){
11519                     hd = document.createElement('input');
11520                     hd.type = 'hidden';
11521                     hd.name = k;
11522                     hd.value = ps[k];
11523                     form.appendChild(hd);
11524                     hiddens.push(hd);
11525                 }
11526             }
11527         }
11528
11529         function cb(){
11530             var r = {  // bogus response object
11531                 responseText : '',
11532                 responseXML : null
11533             };
11534
11535             r.argument = o ? o.argument : null;
11536
11537             try { //
11538                 var doc;
11539                 if(Roo.isIE){
11540                     doc = frame.contentWindow.document;
11541                 }else {
11542                     doc = (frame.contentDocument || window.frames[id].document);
11543                 }
11544                 if(doc && doc.body){
11545                     r.responseText = doc.body.innerHTML;
11546                 }
11547                 if(doc && doc.XMLDocument){
11548                     r.responseXML = doc.XMLDocument;
11549                 }else {
11550                     r.responseXML = doc;
11551                 }
11552             }
11553             catch(e) {
11554                 // ignore
11555             }
11556
11557             Roo.EventManager.removeListener(frame, 'load', cb, this);
11558
11559             this.fireEvent("requestcomplete", this, r, o);
11560             Roo.callback(o.success, o.scope, [r, o]);
11561             Roo.callback(o.callback, o.scope, [o, true, r]);
11562
11563             setTimeout(function(){document.body.removeChild(frame);}, 100);
11564         }
11565
11566         Roo.EventManager.on(frame, 'load', cb, this);
11567         form.submit();
11568
11569         if(hiddens){ // remove dynamic params
11570             for(var i = 0, len = hiddens.length; i < len; i++){
11571                 form.removeChild(hiddens[i]);
11572             }
11573         }
11574     }
11575 });
11576 /*
11577  * Based on:
11578  * Ext JS Library 1.1.1
11579  * Copyright(c) 2006-2007, Ext JS, LLC.
11580  *
11581  * Originally Released Under LGPL - original licence link has changed is not relivant.
11582  *
11583  * Fork - LGPL
11584  * <script type="text/javascript">
11585  */
11586  
11587 /**
11588  * Global Ajax request class.
11589  * 
11590  * @class Roo.Ajax
11591  * @extends Roo.data.Connection
11592  * @static
11593  * 
11594  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11595  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11596  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11597  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11598  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11599  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11600  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11601  */
11602 Roo.Ajax = new Roo.data.Connection({
11603     // fix up the docs
11604     /**
11605      * @scope Roo.Ajax
11606      * @type {Boolear} 
11607      */
11608     autoAbort : false,
11609
11610     /**
11611      * Serialize the passed form into a url encoded string
11612      * @scope Roo.Ajax
11613      * @param {String/HTMLElement} form
11614      * @return {String}
11615      */
11616     serializeForm : function(form){
11617         return Roo.lib.Ajax.serializeForm(form);
11618     }
11619 });/*
11620  * Based on:
11621  * Ext JS Library 1.1.1
11622  * Copyright(c) 2006-2007, Ext JS, LLC.
11623  *
11624  * Originally Released Under LGPL - original licence link has changed is not relivant.
11625  *
11626  * Fork - LGPL
11627  * <script type="text/javascript">
11628  */
11629
11630  
11631 /**
11632  * @class Roo.UpdateManager
11633  * @extends Roo.util.Observable
11634  * Provides AJAX-style update for Element object.<br><br>
11635  * Usage:<br>
11636  * <pre><code>
11637  * // Get it from a Roo.Element object
11638  * var el = Roo.get("foo");
11639  * var mgr = el.getUpdateManager();
11640  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11641  * ...
11642  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11643  * <br>
11644  * // or directly (returns the same UpdateManager instance)
11645  * var mgr = new Roo.UpdateManager("myElementId");
11646  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11647  * mgr.on("update", myFcnNeedsToKnow);
11648  * <br>
11649    // short handed call directly from the element object
11650    Roo.get("foo").load({
11651         url: "bar.php",
11652         scripts:true,
11653         params: "for=bar",
11654         text: "Loading Foo..."
11655    });
11656  * </code></pre>
11657  * @constructor
11658  * Create new UpdateManager directly.
11659  * @param {String/HTMLElement/Roo.Element} el The element to update
11660  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11661  */
11662 Roo.UpdateManager = function(el, forceNew){
11663     el = Roo.get(el);
11664     if(!forceNew && el.updateManager){
11665         return el.updateManager;
11666     }
11667     /**
11668      * The Element object
11669      * @type Roo.Element
11670      */
11671     this.el = el;
11672     /**
11673      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11674      * @type String
11675      */
11676     this.defaultUrl = null;
11677
11678     this.addEvents({
11679         /**
11680          * @event beforeupdate
11681          * Fired before an update is made, return false from your handler and the update is cancelled.
11682          * @param {Roo.Element} el
11683          * @param {String/Object/Function} url
11684          * @param {String/Object} params
11685          */
11686         "beforeupdate": true,
11687         /**
11688          * @event update
11689          * Fired after successful update is made.
11690          * @param {Roo.Element} el
11691          * @param {Object} oResponseObject The response Object
11692          */
11693         "update": true,
11694         /**
11695          * @event failure
11696          * Fired on update failure.
11697          * @param {Roo.Element} el
11698          * @param {Object} oResponseObject The response Object
11699          */
11700         "failure": true
11701     });
11702     var d = Roo.UpdateManager.defaults;
11703     /**
11704      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11705      * @type String
11706      */
11707     this.sslBlankUrl = d.sslBlankUrl;
11708     /**
11709      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11710      * @type Boolean
11711      */
11712     this.disableCaching = d.disableCaching;
11713     /**
11714      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11715      * @type String
11716      */
11717     this.indicatorText = d.indicatorText;
11718     /**
11719      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11720      * @type String
11721      */
11722     this.showLoadIndicator = d.showLoadIndicator;
11723     /**
11724      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11725      * @type Number
11726      */
11727     this.timeout = d.timeout;
11728
11729     /**
11730      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11731      * @type Boolean
11732      */
11733     this.loadScripts = d.loadScripts;
11734
11735     /**
11736      * Transaction object of current executing transaction
11737      */
11738     this.transaction = null;
11739
11740     /**
11741      * @private
11742      */
11743     this.autoRefreshProcId = null;
11744     /**
11745      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.refreshDelegate = this.refresh.createDelegate(this);
11749     /**
11750      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11751      * @type Function
11752      */
11753     this.updateDelegate = this.update.createDelegate(this);
11754     /**
11755      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11756      * @type Function
11757      */
11758     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11759     /**
11760      * @private
11761      */
11762     this.successDelegate = this.processSuccess.createDelegate(this);
11763     /**
11764      * @private
11765      */
11766     this.failureDelegate = this.processFailure.createDelegate(this);
11767
11768     if(!this.renderer){
11769      /**
11770       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11771       */
11772     this.renderer = new Roo.UpdateManager.BasicRenderer();
11773     }
11774     
11775     Roo.UpdateManager.superclass.constructor.call(this);
11776 };
11777
11778 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11779     /**
11780      * Get the Element this UpdateManager is bound to
11781      * @return {Roo.Element} The element
11782      */
11783     getEl : function(){
11784         return this.el;
11785     },
11786     /**
11787      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11788      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11789 <pre><code>
11790 um.update({<br/>
11791     url: "your-url.php",<br/>
11792     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11793     callback: yourFunction,<br/>
11794     scope: yourObject, //(optional scope)  <br/>
11795     discardUrl: false, <br/>
11796     nocache: false,<br/>
11797     text: "Loading...",<br/>
11798     timeout: 30,<br/>
11799     scripts: false<br/>
11800 });
11801 </code></pre>
11802      * The only required property is url. The optional properties nocache, text and scripts
11803      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11804      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11805      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11806      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11807      */
11808     update : function(url, params, callback, discardUrl){
11809         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11810             var method = this.method,
11811                 cfg;
11812             if(typeof url == "object"){ // must be config object
11813                 cfg = url;
11814                 url = cfg.url;
11815                 params = params || cfg.params;
11816                 callback = callback || cfg.callback;
11817                 discardUrl = discardUrl || cfg.discardUrl;
11818                 if(callback && cfg.scope){
11819                     callback = callback.createDelegate(cfg.scope);
11820                 }
11821                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11822                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11823                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11824                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11825                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11826             }
11827             this.showLoading();
11828             if(!discardUrl){
11829                 this.defaultUrl = url;
11830             }
11831             if(typeof url == "function"){
11832                 url = url.call(this);
11833             }
11834
11835             method = method || (params ? "POST" : "GET");
11836             if(method == "GET"){
11837                 url = this.prepareUrl(url);
11838             }
11839
11840             var o = Roo.apply(cfg ||{}, {
11841                 url : url,
11842                 params: params,
11843                 success: this.successDelegate,
11844                 failure: this.failureDelegate,
11845                 callback: undefined,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11848             });
11849             Roo.log("updated manager called with timeout of " + o.timeout);
11850             this.transaction = Roo.Ajax.request(o);
11851         }
11852     },
11853
11854     /**
11855      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11856      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11857      * @param {String/HTMLElement} form The form Id or form element
11858      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11859      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11860      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11861      */
11862     formUpdate : function(form, url, reset, callback){
11863         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11864             if(typeof url == "function"){
11865                 url = url.call(this);
11866             }
11867             form = Roo.getDom(form);
11868             this.transaction = Roo.Ajax.request({
11869                 form: form,
11870                 url:url,
11871                 success: this.successDelegate,
11872                 failure: this.failureDelegate,
11873                 timeout: (this.timeout*1000),
11874                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11875             });
11876             this.showLoading.defer(1, this);
11877         }
11878     },
11879
11880     /**
11881      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11882      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11883      */
11884     refresh : function(callback){
11885         if(this.defaultUrl == null){
11886             return;
11887         }
11888         this.update(this.defaultUrl, null, callback, true);
11889     },
11890
11891     /**
11892      * Set this element to auto refresh.
11893      * @param {Number} interval How often to update (in seconds).
11894      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11895      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11896      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11897      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11898      */
11899     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11900         if(refreshNow){
11901             this.update(url || this.defaultUrl, params, callback, true);
11902         }
11903         if(this.autoRefreshProcId){
11904             clearInterval(this.autoRefreshProcId);
11905         }
11906         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11907     },
11908
11909     /**
11910      * Stop auto refresh on this element.
11911      */
11912      stopAutoRefresh : function(){
11913         if(this.autoRefreshProcId){
11914             clearInterval(this.autoRefreshProcId);
11915             delete this.autoRefreshProcId;
11916         }
11917     },
11918
11919     isAutoRefreshing : function(){
11920        return this.autoRefreshProcId ? true : false;
11921     },
11922     /**
11923      * Called to update the element to "Loading" state. Override to perform custom action.
11924      */
11925     showLoading : function(){
11926         if(this.showLoadIndicator){
11927             this.el.update(this.indicatorText);
11928         }
11929     },
11930
11931     /**
11932      * Adds unique parameter to query string if disableCaching = true
11933      * @private
11934      */
11935     prepareUrl : function(url){
11936         if(this.disableCaching){
11937             var append = "_dc=" + (new Date().getTime());
11938             if(url.indexOf("?") !== -1){
11939                 url += "&" + append;
11940             }else{
11941                 url += "?" + append;
11942             }
11943         }
11944         return url;
11945     },
11946
11947     /**
11948      * @private
11949      */
11950     processSuccess : function(response){
11951         this.transaction = null;
11952         if(response.argument.form && response.argument.reset){
11953             try{ // put in try/catch since some older FF releases had problems with this
11954                 response.argument.form.reset();
11955             }catch(e){}
11956         }
11957         if(this.loadScripts){
11958             this.renderer.render(this.el, response, this,
11959                 this.updateComplete.createDelegate(this, [response]));
11960         }else{
11961             this.renderer.render(this.el, response, this);
11962             this.updateComplete(response);
11963         }
11964     },
11965
11966     updateComplete : function(response){
11967         this.fireEvent("update", this.el, response);
11968         if(typeof response.argument.callback == "function"){
11969             response.argument.callback(this.el, true, response);
11970         }
11971     },
11972
11973     /**
11974      * @private
11975      */
11976     processFailure : function(response){
11977         this.transaction = null;
11978         this.fireEvent("failure", this.el, response);
11979         if(typeof response.argument.callback == "function"){
11980             response.argument.callback(this.el, false, response);
11981         }
11982     },
11983
11984     /**
11985      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11986      * @param {Object} renderer The object implementing the render() method
11987      */
11988     setRenderer : function(renderer){
11989         this.renderer = renderer;
11990     },
11991
11992     getRenderer : function(){
11993        return this.renderer;
11994     },
11995
11996     /**
11997      * Set the defaultUrl used for updates
11998      * @param {String/Function} defaultUrl The url or a function to call to get the url
11999      */
12000     setDefaultUrl : function(defaultUrl){
12001         this.defaultUrl = defaultUrl;
12002     },
12003
12004     /**
12005      * Aborts the executing transaction
12006      */
12007     abort : function(){
12008         if(this.transaction){
12009             Roo.Ajax.abort(this.transaction);
12010         }
12011     },
12012
12013     /**
12014      * Returns true if an update is in progress
12015      * @return {Boolean}
12016      */
12017     isUpdating : function(){
12018         if(this.transaction){
12019             return Roo.Ajax.isLoading(this.transaction);
12020         }
12021         return false;
12022     }
12023 });
12024
12025 /**
12026  * @class Roo.UpdateManager.defaults
12027  * @static (not really - but it helps the doc tool)
12028  * The defaults collection enables customizing the default properties of UpdateManager
12029  */
12030    Roo.UpdateManager.defaults = {
12031        /**
12032          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12033          * @type Number
12034          */
12035          timeout : 30,
12036
12037          /**
12038          * True to process scripts by default (Defaults to false).
12039          * @type Boolean
12040          */
12041         loadScripts : false,
12042
12043         /**
12044         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12045         * @type String
12046         */
12047         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12048         /**
12049          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12050          * @type Boolean
12051          */
12052         disableCaching : false,
12053         /**
12054          * Whether to show indicatorText when loading (Defaults to true).
12055          * @type Boolean
12056          */
12057         showLoadIndicator : true,
12058         /**
12059          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12060          * @type String
12061          */
12062         indicatorText : '<div class="loading-indicator">Loading...</div>'
12063    };
12064
12065 /**
12066  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12067  *Usage:
12068  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12069  * @param {String/HTMLElement/Roo.Element} el The element to update
12070  * @param {String} url The url
12071  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12072  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12073  * @static
12074  * @deprecated
12075  * @member Roo.UpdateManager
12076  */
12077 Roo.UpdateManager.updateElement = function(el, url, params, options){
12078     var um = Roo.get(el, true).getUpdateManager();
12079     Roo.apply(um, options);
12080     um.update(url, params, options ? options.callback : null);
12081 };
12082 // alias for backwards compat
12083 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12084 /**
12085  * @class Roo.UpdateManager.BasicRenderer
12086  * Default Content renderer. Updates the elements innerHTML with the responseText.
12087  */
12088 Roo.UpdateManager.BasicRenderer = function(){};
12089
12090 Roo.UpdateManager.BasicRenderer.prototype = {
12091     /**
12092      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12093      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12094      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12095      * @param {Roo.Element} el The element being rendered
12096      * @param {Object} response The YUI Connect response object
12097      * @param {UpdateManager} updateManager The calling update manager
12098      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12099      */
12100      render : function(el, response, updateManager, callback){
12101         el.update(response.responseText, updateManager.loadScripts, callback);
12102     }
12103 };
12104 /*
12105  * Based on:
12106  * Roo JS
12107  * (c)) Alan Knowles
12108  * Licence : LGPL
12109  */
12110
12111
12112 /**
12113  * @class Roo.DomTemplate
12114  * @extends Roo.Template
12115  * An effort at a dom based template engine..
12116  *
12117  * Similar to XTemplate, except it uses dom parsing to create the template..
12118  *
12119  * Supported features:
12120  *
12121  *  Tags:
12122
12123 <pre><code>
12124       {a_variable} - output encoded.
12125       {a_variable.format:("Y-m-d")} - call a method on the variable
12126       {a_variable:raw} - unencoded output
12127       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12128       {a_variable:this.method_on_template(...)} - call a method on the template object.
12129  
12130 </code></pre>
12131  *  The tpl tag:
12132 <pre><code>
12133         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12134         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12135         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12136         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12137   
12138 </code></pre>
12139  *      
12140  */
12141 Roo.DomTemplate = function()
12142 {
12143      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12144      if (this.html) {
12145         this.compile();
12146      }
12147 };
12148
12149
12150 Roo.extend(Roo.DomTemplate, Roo.Template, {
12151     /**
12152      * id counter for sub templates.
12153      */
12154     id : 0,
12155     /**
12156      * flag to indicate if dom parser is inside a pre,
12157      * it will strip whitespace if not.
12158      */
12159     inPre : false,
12160     
12161     /**
12162      * The various sub templates
12163      */
12164     tpls : false,
12165     
12166     
12167     
12168     /**
12169      *
12170      * basic tag replacing syntax
12171      * WORD:WORD()
12172      *
12173      * // you can fake an object call by doing this
12174      *  x.t:(test,tesT) 
12175      * 
12176      */
12177     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12178     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12179     
12180     iterChild : function (node, method) {
12181         
12182         var oldPre = this.inPre;
12183         if (node.tagName == 'PRE') {
12184             this.inPre = true;
12185         }
12186         for( var i = 0; i < node.childNodes.length; i++) {
12187             method.call(this, node.childNodes[i]);
12188         }
12189         this.inPre = oldPre;
12190     },
12191     
12192     
12193     
12194     /**
12195      * compile the template
12196      *
12197      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12198      *
12199      */
12200     compile: function()
12201     {
12202         var s = this.html;
12203         
12204         // covert the html into DOM...
12205         var doc = false;
12206         var div =false;
12207         try {
12208             doc = document.implementation.createHTMLDocument("");
12209             doc.documentElement.innerHTML =   this.html  ;
12210             div = doc.documentElement;
12211         } catch (e) {
12212             // old IE... - nasty -- it causes all sorts of issues.. with
12213             // images getting pulled from server..
12214             div = document.createElement('div');
12215             div.innerHTML = this.html;
12216         }
12217         //doc.documentElement.innerHTML = htmlBody
12218          
12219         
12220         
12221         this.tpls = [];
12222         var _t = this;
12223         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12224         
12225         var tpls = this.tpls;
12226         
12227         // create a top level template from the snippet..
12228         
12229         //Roo.log(div.innerHTML);
12230         
12231         var tpl = {
12232             uid : 'master',
12233             id : this.id++,
12234             attr : false,
12235             value : false,
12236             body : div.innerHTML,
12237             
12238             forCall : false,
12239             execCall : false,
12240             dom : div,
12241             isTop : true
12242             
12243         };
12244         tpls.unshift(tpl);
12245         
12246         
12247         // compile them...
12248         this.tpls = [];
12249         Roo.each(tpls, function(tp){
12250             this.compileTpl(tp);
12251             this.tpls[tp.id] = tp;
12252         }, this);
12253         
12254         this.master = tpls[0];
12255         return this;
12256         
12257         
12258     },
12259     
12260     compileNode : function(node, istop) {
12261         // test for
12262         //Roo.log(node);
12263         
12264         
12265         // skip anything not a tag..
12266         if (node.nodeType != 1) {
12267             if (node.nodeType == 3 && !this.inPre) {
12268                 // reduce white space..
12269                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12270                 
12271             }
12272             return;
12273         }
12274         
12275         var tpl = {
12276             uid : false,
12277             id : false,
12278             attr : false,
12279             value : false,
12280             body : '',
12281             
12282             forCall : false,
12283             execCall : false,
12284             dom : false,
12285             isTop : istop
12286             
12287             
12288         };
12289         
12290         
12291         switch(true) {
12292             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12293             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12294             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12295             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12296             // no default..
12297         }
12298         
12299         
12300         if (!tpl.attr) {
12301             // just itterate children..
12302             this.iterChild(node,this.compileNode);
12303             return;
12304         }
12305         tpl.uid = this.id++;
12306         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12307         node.removeAttribute('roo-'+ tpl.attr);
12308         if (tpl.attr != 'name') {
12309             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12310             node.parentNode.replaceChild(placeholder,  node);
12311         } else {
12312             
12313             var placeholder =  document.createElement('span');
12314             placeholder.className = 'roo-tpl-' + tpl.value;
12315             node.parentNode.replaceChild(placeholder,  node);
12316         }
12317         
12318         // parent now sees '{domtplXXXX}
12319         this.iterChild(node,this.compileNode);
12320         
12321         // we should now have node body...
12322         var div = document.createElement('div');
12323         div.appendChild(node);
12324         tpl.dom = node;
12325         // this has the unfortunate side effect of converting tagged attributes
12326         // eg. href="{...}" into %7C...%7D
12327         // this has been fixed by searching for those combo's although it's a bit hacky..
12328         
12329         
12330         tpl.body = div.innerHTML;
12331         
12332         
12333          
12334         tpl.id = tpl.uid;
12335         switch(tpl.attr) {
12336             case 'for' :
12337                 switch (tpl.value) {
12338                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12339                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12340                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12341                 }
12342                 break;
12343             
12344             case 'exec':
12345                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12346                 break;
12347             
12348             case 'if':     
12349                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12350                 break;
12351             
12352             case 'name':
12353                 tpl.id  = tpl.value; // replace non characters???
12354                 break;
12355             
12356         }
12357         
12358         
12359         this.tpls.push(tpl);
12360         
12361         
12362         
12363     },
12364     
12365     
12366     
12367     
12368     /**
12369      * Compile a segment of the template into a 'sub-template'
12370      *
12371      * 
12372      * 
12373      *
12374      */
12375     compileTpl : function(tpl)
12376     {
12377         var fm = Roo.util.Format;
12378         var useF = this.disableFormats !== true;
12379         
12380         var sep = Roo.isGecko ? "+\n" : ",\n";
12381         
12382         var undef = function(str) {
12383             Roo.debug && Roo.log("Property not found :"  + str);
12384             return '';
12385         };
12386           
12387         //Roo.log(tpl.body);
12388         
12389         
12390         
12391         var fn = function(m, lbrace, name, format, args)
12392         {
12393             //Roo.log("ARGS");
12394             //Roo.log(arguments);
12395             args = args ? args.replace(/\\'/g,"'") : args;
12396             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12397             if (typeof(format) == 'undefined') {
12398                 format =  'htmlEncode'; 
12399             }
12400             if (format == 'raw' ) {
12401                 format = false;
12402             }
12403             
12404             if(name.substr(0, 6) == 'domtpl'){
12405                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12406             }
12407             
12408             // build an array of options to determine if value is undefined..
12409             
12410             // basically get 'xxxx.yyyy' then do
12411             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12412             //    (function () { Roo.log("Property not found"); return ''; })() :
12413             //    ......
12414             
12415             var udef_ar = [];
12416             var lookfor = '';
12417             Roo.each(name.split('.'), function(st) {
12418                 lookfor += (lookfor.length ? '.': '') + st;
12419                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12420             });
12421             
12422             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12423             
12424             
12425             if(format && useF){
12426                 
12427                 args = args ? ',' + args : "";
12428                  
12429                 if(format.substr(0, 5) != "this."){
12430                     format = "fm." + format + '(';
12431                 }else{
12432                     format = 'this.call("'+ format.substr(5) + '", ';
12433                     args = ", values";
12434                 }
12435                 
12436                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12437             }
12438              
12439             if (args && args.length) {
12440                 // called with xxyx.yuu:(test,test)
12441                 // change to ()
12442                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12443             }
12444             // raw.. - :raw modifier..
12445             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12446             
12447         };
12448         var body;
12449         // branched to use + in gecko and [].join() in others
12450         if(Roo.isGecko){
12451             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12452                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12453                     "';};};";
12454         }else{
12455             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12456             body.push(tpl.body.replace(/(\r\n|\n)/g,
12457                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12458             body.push("'].join('');};};");
12459             body = body.join('');
12460         }
12461         
12462         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12463        
12464         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12465         eval(body);
12466         
12467         return this;
12468     },
12469      
12470     /**
12471      * same as applyTemplate, except it's done to one of the subTemplates
12472      * when using named templates, you can do:
12473      *
12474      * var str = pl.applySubTemplate('your-name', values);
12475      *
12476      * 
12477      * @param {Number} id of the template
12478      * @param {Object} values to apply to template
12479      * @param {Object} parent (normaly the instance of this object)
12480      */
12481     applySubTemplate : function(id, values, parent)
12482     {
12483         
12484         
12485         var t = this.tpls[id];
12486         
12487         
12488         try { 
12489             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12490                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12491                 return '';
12492             }
12493         } catch(e) {
12494             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12495             Roo.log(values);
12496           
12497             return '';
12498         }
12499         try { 
12500             
12501             if(t.execCall && t.execCall.call(this, values, parent)){
12502                 return '';
12503             }
12504         } catch(e) {
12505             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12506             Roo.log(values);
12507             return '';
12508         }
12509         
12510         try {
12511             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12512             parent = t.target ? values : parent;
12513             if(t.forCall && vs instanceof Array){
12514                 var buf = [];
12515                 for(var i = 0, len = vs.length; i < len; i++){
12516                     try {
12517                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12518                     } catch (e) {
12519                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12520                         Roo.log(e.body);
12521                         //Roo.log(t.compiled);
12522                         Roo.log(vs[i]);
12523                     }   
12524                 }
12525                 return buf.join('');
12526             }
12527         } catch (e) {
12528             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12529             Roo.log(values);
12530             return '';
12531         }
12532         try {
12533             return t.compiled.call(this, vs, parent);
12534         } catch (e) {
12535             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12536             Roo.log(e.body);
12537             //Roo.log(t.compiled);
12538             Roo.log(values);
12539             return '';
12540         }
12541     },
12542
12543    
12544
12545     applyTemplate : function(values){
12546         return this.master.compiled.call(this, values, {});
12547         //var s = this.subs;
12548     },
12549
12550     apply : function(){
12551         return this.applyTemplate.apply(this, arguments);
12552     }
12553
12554  });
12555
12556 Roo.DomTemplate.from = function(el){
12557     el = Roo.getDom(el);
12558     return new Roo.Domtemplate(el.value || el.innerHTML);
12559 };/*
12560  * Based on:
12561  * Ext JS Library 1.1.1
12562  * Copyright(c) 2006-2007, Ext JS, LLC.
12563  *
12564  * Originally Released Under LGPL - original licence link has changed is not relivant.
12565  *
12566  * Fork - LGPL
12567  * <script type="text/javascript">
12568  */
12569
12570 /**
12571  * @class Roo.util.DelayedTask
12572  * Provides a convenient method of performing setTimeout where a new
12573  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12574  * You can use this class to buffer
12575  * the keypress events for a certain number of milliseconds, and perform only if they stop
12576  * for that amount of time.
12577  * @constructor The parameters to this constructor serve as defaults and are not required.
12578  * @param {Function} fn (optional) The default function to timeout
12579  * @param {Object} scope (optional) The default scope of that timeout
12580  * @param {Array} args (optional) The default Array of arguments
12581  */
12582 Roo.util.DelayedTask = function(fn, scope, args){
12583     var id = null, d, t;
12584
12585     var call = function(){
12586         var now = new Date().getTime();
12587         if(now - t >= d){
12588             clearInterval(id);
12589             id = null;
12590             fn.apply(scope, args || []);
12591         }
12592     };
12593     /**
12594      * Cancels any pending timeout and queues a new one
12595      * @param {Number} delay The milliseconds to delay
12596      * @param {Function} newFn (optional) Overrides function passed to constructor
12597      * @param {Object} newScope (optional) Overrides scope passed to constructor
12598      * @param {Array} newArgs (optional) Overrides args passed to constructor
12599      */
12600     this.delay = function(delay, newFn, newScope, newArgs){
12601         if(id && delay != d){
12602             this.cancel();
12603         }
12604         d = delay;
12605         t = new Date().getTime();
12606         fn = newFn || fn;
12607         scope = newScope || scope;
12608         args = newArgs || args;
12609         if(!id){
12610             id = setInterval(call, d);
12611         }
12612     };
12613
12614     /**
12615      * Cancel the last queued timeout
12616      */
12617     this.cancel = function(){
12618         if(id){
12619             clearInterval(id);
12620             id = null;
12621         }
12622     };
12623 };/*
12624  * Based on:
12625  * Ext JS Library 1.1.1
12626  * Copyright(c) 2006-2007, Ext JS, LLC.
12627  *
12628  * Originally Released Under LGPL - original licence link has changed is not relivant.
12629  *
12630  * Fork - LGPL
12631  * <script type="text/javascript">
12632  */
12633  
12634  
12635 Roo.util.TaskRunner = function(interval){
12636     interval = interval || 10;
12637     var tasks = [], removeQueue = [];
12638     var id = 0;
12639     var running = false;
12640
12641     var stopThread = function(){
12642         running = false;
12643         clearInterval(id);
12644         id = 0;
12645     };
12646
12647     var startThread = function(){
12648         if(!running){
12649             running = true;
12650             id = setInterval(runTasks, interval);
12651         }
12652     };
12653
12654     var removeTask = function(task){
12655         removeQueue.push(task);
12656         if(task.onStop){
12657             task.onStop();
12658         }
12659     };
12660
12661     var runTasks = function(){
12662         if(removeQueue.length > 0){
12663             for(var i = 0, len = removeQueue.length; i < len; i++){
12664                 tasks.remove(removeQueue[i]);
12665             }
12666             removeQueue = [];
12667             if(tasks.length < 1){
12668                 stopThread();
12669                 return;
12670             }
12671         }
12672         var now = new Date().getTime();
12673         for(var i = 0, len = tasks.length; i < len; ++i){
12674             var t = tasks[i];
12675             var itime = now - t.taskRunTime;
12676             if(t.interval <= itime){
12677                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12678                 t.taskRunTime = now;
12679                 if(rt === false || t.taskRunCount === t.repeat){
12680                     removeTask(t);
12681                     return;
12682                 }
12683             }
12684             if(t.duration && t.duration <= (now - t.taskStartTime)){
12685                 removeTask(t);
12686             }
12687         }
12688     };
12689
12690     /**
12691      * Queues a new task.
12692      * @param {Object} task
12693      */
12694     this.start = function(task){
12695         tasks.push(task);
12696         task.taskStartTime = new Date().getTime();
12697         task.taskRunTime = 0;
12698         task.taskRunCount = 0;
12699         startThread();
12700         return task;
12701     };
12702
12703     this.stop = function(task){
12704         removeTask(task);
12705         return task;
12706     };
12707
12708     this.stopAll = function(){
12709         stopThread();
12710         for(var i = 0, len = tasks.length; i < len; i++){
12711             if(tasks[i].onStop){
12712                 tasks[i].onStop();
12713             }
12714         }
12715         tasks = [];
12716         removeQueue = [];
12717     };
12718 };
12719
12720 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12721  * Based on:
12722  * Ext JS Library 1.1.1
12723  * Copyright(c) 2006-2007, Ext JS, LLC.
12724  *
12725  * Originally Released Under LGPL - original licence link has changed is not relivant.
12726  *
12727  * Fork - LGPL
12728  * <script type="text/javascript">
12729  */
12730
12731  
12732 /**
12733  * @class Roo.util.MixedCollection
12734  * @extends Roo.util.Observable
12735  * A Collection class that maintains both numeric indexes and keys and exposes events.
12736  * @constructor
12737  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12738  * collection (defaults to false)
12739  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12740  * and return the key value for that item.  This is used when available to look up the key on items that
12741  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12742  * equivalent to providing an implementation for the {@link #getKey} method.
12743  */
12744 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12745     this.items = [];
12746     this.map = {};
12747     this.keys = [];
12748     this.length = 0;
12749     this.addEvents({
12750         /**
12751          * @event clear
12752          * Fires when the collection is cleared.
12753          */
12754         "clear" : true,
12755         /**
12756          * @event add
12757          * Fires when an item is added to the collection.
12758          * @param {Number} index The index at which the item was added.
12759          * @param {Object} o The item added.
12760          * @param {String} key The key associated with the added item.
12761          */
12762         "add" : true,
12763         /**
12764          * @event replace
12765          * Fires when an item is replaced in the collection.
12766          * @param {String} key he key associated with the new added.
12767          * @param {Object} old The item being replaced.
12768          * @param {Object} new The new item.
12769          */
12770         "replace" : true,
12771         /**
12772          * @event remove
12773          * Fires when an item is removed from the collection.
12774          * @param {Object} o The item being removed.
12775          * @param {String} key (optional) The key associated with the removed item.
12776          */
12777         "remove" : true,
12778         "sort" : true
12779     });
12780     this.allowFunctions = allowFunctions === true;
12781     if(keyFn){
12782         this.getKey = keyFn;
12783     }
12784     Roo.util.MixedCollection.superclass.constructor.call(this);
12785 };
12786
12787 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12788     allowFunctions : false,
12789     
12790 /**
12791  * Adds an item to the collection.
12792  * @param {String} key The key to associate with the item
12793  * @param {Object} o The item to add.
12794  * @return {Object} The item added.
12795  */
12796     add : function(key, o){
12797         if(arguments.length == 1){
12798             o = arguments[0];
12799             key = this.getKey(o);
12800         }
12801         if(typeof key == "undefined" || key === null){
12802             this.length++;
12803             this.items.push(o);
12804             this.keys.push(null);
12805         }else{
12806             var old = this.map[key];
12807             if(old){
12808                 return this.replace(key, o);
12809             }
12810             this.length++;
12811             this.items.push(o);
12812             this.map[key] = o;
12813             this.keys.push(key);
12814         }
12815         this.fireEvent("add", this.length-1, o, key);
12816         return o;
12817     },
12818        
12819 /**
12820   * MixedCollection has a generic way to fetch keys if you implement getKey.
12821 <pre><code>
12822 // normal way
12823 var mc = new Roo.util.MixedCollection();
12824 mc.add(someEl.dom.id, someEl);
12825 mc.add(otherEl.dom.id, otherEl);
12826 //and so on
12827
12828 // using getKey
12829 var mc = new Roo.util.MixedCollection();
12830 mc.getKey = function(el){
12831    return el.dom.id;
12832 };
12833 mc.add(someEl);
12834 mc.add(otherEl);
12835
12836 // or via the constructor
12837 var mc = new Roo.util.MixedCollection(false, function(el){
12838    return el.dom.id;
12839 });
12840 mc.add(someEl);
12841 mc.add(otherEl);
12842 </code></pre>
12843  * @param o {Object} The item for which to find the key.
12844  * @return {Object} The key for the passed item.
12845  */
12846     getKey : function(o){
12847          return o.id; 
12848     },
12849    
12850 /**
12851  * Replaces an item in the collection.
12852  * @param {String} key The key associated with the item to replace, or the item to replace.
12853  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12854  * @return {Object}  The new item.
12855  */
12856     replace : function(key, o){
12857         if(arguments.length == 1){
12858             o = arguments[0];
12859             key = this.getKey(o);
12860         }
12861         var old = this.item(key);
12862         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12863              return this.add(key, o);
12864         }
12865         var index = this.indexOfKey(key);
12866         this.items[index] = o;
12867         this.map[key] = o;
12868         this.fireEvent("replace", key, old, o);
12869         return o;
12870     },
12871    
12872 /**
12873  * Adds all elements of an Array or an Object to the collection.
12874  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12875  * an Array of values, each of which are added to the collection.
12876  */
12877     addAll : function(objs){
12878         if(arguments.length > 1 || objs instanceof Array){
12879             var args = arguments.length > 1 ? arguments : objs;
12880             for(var i = 0, len = args.length; i < len; i++){
12881                 this.add(args[i]);
12882             }
12883         }else{
12884             for(var key in objs){
12885                 if(this.allowFunctions || typeof objs[key] != "function"){
12886                     this.add(key, objs[key]);
12887                 }
12888             }
12889         }
12890     },
12891    
12892 /**
12893  * Executes the specified function once for every item in the collection, passing each
12894  * item as the first and only parameter. returning false from the function will stop the iteration.
12895  * @param {Function} fn The function to execute for each item.
12896  * @param {Object} scope (optional) The scope in which to execute the function.
12897  */
12898     each : function(fn, scope){
12899         var items = [].concat(this.items); // each safe for removal
12900         for(var i = 0, len = items.length; i < len; i++){
12901             if(fn.call(scope || items[i], items[i], i, len) === false){
12902                 break;
12903             }
12904         }
12905     },
12906    
12907 /**
12908  * Executes the specified function once for every key in the collection, passing each
12909  * key, and its associated item as the first two parameters.
12910  * @param {Function} fn The function to execute for each item.
12911  * @param {Object} scope (optional) The scope in which to execute the function.
12912  */
12913     eachKey : function(fn, scope){
12914         for(var i = 0, len = this.keys.length; i < len; i++){
12915             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12916         }
12917     },
12918    
12919 /**
12920  * Returns the first item in the collection which elicits a true return value from the
12921  * passed selection function.
12922  * @param {Function} fn The selection function to execute for each item.
12923  * @param {Object} scope (optional) The scope in which to execute the function.
12924  * @return {Object} The first item in the collection which returned true from the selection function.
12925  */
12926     find : function(fn, scope){
12927         for(var i = 0, len = this.items.length; i < len; i++){
12928             if(fn.call(scope || window, this.items[i], this.keys[i])){
12929                 return this.items[i];
12930             }
12931         }
12932         return null;
12933     },
12934    
12935 /**
12936  * Inserts an item at the specified index in the collection.
12937  * @param {Number} index The index to insert the item at.
12938  * @param {String} key The key to associate with the new item, or the item itself.
12939  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12940  * @return {Object} The item inserted.
12941  */
12942     insert : function(index, key, o){
12943         if(arguments.length == 2){
12944             o = arguments[1];
12945             key = this.getKey(o);
12946         }
12947         if(index >= this.length){
12948             return this.add(key, o);
12949         }
12950         this.length++;
12951         this.items.splice(index, 0, o);
12952         if(typeof key != "undefined" && key != null){
12953             this.map[key] = o;
12954         }
12955         this.keys.splice(index, 0, key);
12956         this.fireEvent("add", index, o, key);
12957         return o;
12958     },
12959    
12960 /**
12961  * Removed an item from the collection.
12962  * @param {Object} o The item to remove.
12963  * @return {Object} The item removed.
12964  */
12965     remove : function(o){
12966         return this.removeAt(this.indexOf(o));
12967     },
12968    
12969 /**
12970  * Remove an item from a specified index in the collection.
12971  * @param {Number} index The index within the collection of the item to remove.
12972  */
12973     removeAt : function(index){
12974         if(index < this.length && index >= 0){
12975             this.length--;
12976             var o = this.items[index];
12977             this.items.splice(index, 1);
12978             var key = this.keys[index];
12979             if(typeof key != "undefined"){
12980                 delete this.map[key];
12981             }
12982             this.keys.splice(index, 1);
12983             this.fireEvent("remove", o, key);
12984         }
12985     },
12986    
12987 /**
12988  * Removed an item associated with the passed key fom the collection.
12989  * @param {String} key The key of the item to remove.
12990  */
12991     removeKey : function(key){
12992         return this.removeAt(this.indexOfKey(key));
12993     },
12994    
12995 /**
12996  * Returns the number of items in the collection.
12997  * @return {Number} the number of items in the collection.
12998  */
12999     getCount : function(){
13000         return this.length; 
13001     },
13002    
13003 /**
13004  * Returns index within the collection of the passed Object.
13005  * @param {Object} o The item to find the index of.
13006  * @return {Number} index of the item.
13007  */
13008     indexOf : function(o){
13009         if(!this.items.indexOf){
13010             for(var i = 0, len = this.items.length; i < len; i++){
13011                 if(this.items[i] == o) return i;
13012             }
13013             return -1;
13014         }else{
13015             return this.items.indexOf(o);
13016         }
13017     },
13018    
13019 /**
13020  * Returns index within the collection of the passed key.
13021  * @param {String} key The key to find the index of.
13022  * @return {Number} index of the key.
13023  */
13024     indexOfKey : function(key){
13025         if(!this.keys.indexOf){
13026             for(var i = 0, len = this.keys.length; i < len; i++){
13027                 if(this.keys[i] == key) return i;
13028             }
13029             return -1;
13030         }else{
13031             return this.keys.indexOf(key);
13032         }
13033     },
13034    
13035 /**
13036  * Returns the item associated with the passed key OR index. Key has priority over index.
13037  * @param {String/Number} key The key or index of the item.
13038  * @return {Object} The item associated with the passed key.
13039  */
13040     item : function(key){
13041         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13042         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13043     },
13044     
13045 /**
13046  * Returns the item at the specified index.
13047  * @param {Number} index The index of the item.
13048  * @return {Object}
13049  */
13050     itemAt : function(index){
13051         return this.items[index];
13052     },
13053     
13054 /**
13055  * Returns the item associated with the passed key.
13056  * @param {String/Number} key The key of the item.
13057  * @return {Object} The item associated with the passed key.
13058  */
13059     key : function(key){
13060         return this.map[key];
13061     },
13062    
13063 /**
13064  * Returns true if the collection contains the passed Object as an item.
13065  * @param {Object} o  The Object to look for in the collection.
13066  * @return {Boolean} True if the collection contains the Object as an item.
13067  */
13068     contains : function(o){
13069         return this.indexOf(o) != -1;
13070     },
13071    
13072 /**
13073  * Returns true if the collection contains the passed Object as a key.
13074  * @param {String} key The key to look for in the collection.
13075  * @return {Boolean} True if the collection contains the Object as a key.
13076  */
13077     containsKey : function(key){
13078         return typeof this.map[key] != "undefined";
13079     },
13080    
13081 /**
13082  * Removes all items from the collection.
13083  */
13084     clear : function(){
13085         this.length = 0;
13086         this.items = [];
13087         this.keys = [];
13088         this.map = {};
13089         this.fireEvent("clear");
13090     },
13091    
13092 /**
13093  * Returns the first item in the collection.
13094  * @return {Object} the first item in the collection..
13095  */
13096     first : function(){
13097         return this.items[0]; 
13098     },
13099    
13100 /**
13101  * Returns the last item in the collection.
13102  * @return {Object} the last item in the collection..
13103  */
13104     last : function(){
13105         return this.items[this.length-1];   
13106     },
13107     
13108     _sort : function(property, dir, fn){
13109         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13110         fn = fn || function(a, b){
13111             return a-b;
13112         };
13113         var c = [], k = this.keys, items = this.items;
13114         for(var i = 0, len = items.length; i < len; i++){
13115             c[c.length] = {key: k[i], value: items[i], index: i};
13116         }
13117         c.sort(function(a, b){
13118             var v = fn(a[property], b[property]) * dsc;
13119             if(v == 0){
13120                 v = (a.index < b.index ? -1 : 1);
13121             }
13122             return v;
13123         });
13124         for(var i = 0, len = c.length; i < len; i++){
13125             items[i] = c[i].value;
13126             k[i] = c[i].key;
13127         }
13128         this.fireEvent("sort", this);
13129     },
13130     
13131     /**
13132      * Sorts this collection with the passed comparison function
13133      * @param {String} direction (optional) "ASC" or "DESC"
13134      * @param {Function} fn (optional) comparison function
13135      */
13136     sort : function(dir, fn){
13137         this._sort("value", dir, fn);
13138     },
13139     
13140     /**
13141      * Sorts this collection by keys
13142      * @param {String} direction (optional) "ASC" or "DESC"
13143      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13144      */
13145     keySort : function(dir, fn){
13146         this._sort("key", dir, fn || function(a, b){
13147             return String(a).toUpperCase()-String(b).toUpperCase();
13148         });
13149     },
13150     
13151     /**
13152      * Returns a range of items in this collection
13153      * @param {Number} startIndex (optional) defaults to 0
13154      * @param {Number} endIndex (optional) default to the last item
13155      * @return {Array} An array of items
13156      */
13157     getRange : function(start, end){
13158         var items = this.items;
13159         if(items.length < 1){
13160             return [];
13161         }
13162         start = start || 0;
13163         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13164         var r = [];
13165         if(start <= end){
13166             for(var i = start; i <= end; i++) {
13167                     r[r.length] = items[i];
13168             }
13169         }else{
13170             for(var i = start; i >= end; i--) {
13171                     r[r.length] = items[i];
13172             }
13173         }
13174         return r;
13175     },
13176         
13177     /**
13178      * Filter the <i>objects</i> in this collection by a specific property. 
13179      * Returns a new collection that has been filtered.
13180      * @param {String} property A property on your objects
13181      * @param {String/RegExp} value Either string that the property values 
13182      * should start with or a RegExp to test against the property
13183      * @return {MixedCollection} The new filtered collection
13184      */
13185     filter : function(property, value){
13186         if(!value.exec){ // not a regex
13187             value = String(value);
13188             if(value.length == 0){
13189                 return this.clone();
13190             }
13191             value = new RegExp("^" + Roo.escapeRe(value), "i");
13192         }
13193         return this.filterBy(function(o){
13194             return o && value.test(o[property]);
13195         });
13196         },
13197     
13198     /**
13199      * Filter by a function. * Returns a new collection that has been filtered.
13200      * The passed function will be called with each 
13201      * object in the collection. If the function returns true, the value is included 
13202      * otherwise it is filtered.
13203      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13204      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13205      * @return {MixedCollection} The new filtered collection
13206      */
13207     filterBy : function(fn, scope){
13208         var r = new Roo.util.MixedCollection();
13209         r.getKey = this.getKey;
13210         var k = this.keys, it = this.items;
13211         for(var i = 0, len = it.length; i < len; i++){
13212             if(fn.call(scope||this, it[i], k[i])){
13213                                 r.add(k[i], it[i]);
13214                         }
13215         }
13216         return r;
13217     },
13218     
13219     /**
13220      * Creates a duplicate of this collection
13221      * @return {MixedCollection}
13222      */
13223     clone : function(){
13224         var r = new Roo.util.MixedCollection();
13225         var k = this.keys, it = this.items;
13226         for(var i = 0, len = it.length; i < len; i++){
13227             r.add(k[i], it[i]);
13228         }
13229         r.getKey = this.getKey;
13230         return r;
13231     }
13232 });
13233 /**
13234  * Returns the item associated with the passed key or index.
13235  * @method
13236  * @param {String/Number} key The key or index of the item.
13237  * @return {Object} The item associated with the passed key.
13238  */
13239 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13240  * Based on:
13241  * Ext JS Library 1.1.1
13242  * Copyright(c) 2006-2007, Ext JS, LLC.
13243  *
13244  * Originally Released Under LGPL - original licence link has changed is not relivant.
13245  *
13246  * Fork - LGPL
13247  * <script type="text/javascript">
13248  */
13249 /**
13250  * @class Roo.util.JSON
13251  * Modified version of Douglas Crockford"s json.js that doesn"t
13252  * mess with the Object prototype 
13253  * http://www.json.org/js.html
13254  * @singleton
13255  */
13256 Roo.util.JSON = new (function(){
13257     var useHasOwn = {}.hasOwnProperty ? true : false;
13258     
13259     // crashes Safari in some instances
13260     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13261     
13262     var pad = function(n) {
13263         return n < 10 ? "0" + n : n;
13264     };
13265     
13266     var m = {
13267         "\b": '\\b',
13268         "\t": '\\t',
13269         "\n": '\\n',
13270         "\f": '\\f',
13271         "\r": '\\r',
13272         '"' : '\\"',
13273         "\\": '\\\\'
13274     };
13275
13276     var encodeString = function(s){
13277         if (/["\\\x00-\x1f]/.test(s)) {
13278             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13279                 var c = m[b];
13280                 if(c){
13281                     return c;
13282                 }
13283                 c = b.charCodeAt();
13284                 return "\\u00" +
13285                     Math.floor(c / 16).toString(16) +
13286                     (c % 16).toString(16);
13287             }) + '"';
13288         }
13289         return '"' + s + '"';
13290     };
13291     
13292     var encodeArray = function(o){
13293         var a = ["["], b, i, l = o.length, v;
13294             for (i = 0; i < l; i += 1) {
13295                 v = o[i];
13296                 switch (typeof v) {
13297                     case "undefined":
13298                     case "function":
13299                     case "unknown":
13300                         break;
13301                     default:
13302                         if (b) {
13303                             a.push(',');
13304                         }
13305                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13306                         b = true;
13307                 }
13308             }
13309             a.push("]");
13310             return a.join("");
13311     };
13312     
13313     var encodeDate = function(o){
13314         return '"' + o.getFullYear() + "-" +
13315                 pad(o.getMonth() + 1) + "-" +
13316                 pad(o.getDate()) + "T" +
13317                 pad(o.getHours()) + ":" +
13318                 pad(o.getMinutes()) + ":" +
13319                 pad(o.getSeconds()) + '"';
13320     };
13321     
13322     /**
13323      * Encodes an Object, Array or other value
13324      * @param {Mixed} o The variable to encode
13325      * @return {String} The JSON string
13326      */
13327     this.encode = function(o)
13328     {
13329         // should this be extended to fully wrap stringify..
13330         
13331         if(typeof o == "undefined" || o === null){
13332             return "null";
13333         }else if(o instanceof Array){
13334             return encodeArray(o);
13335         }else if(o instanceof Date){
13336             return encodeDate(o);
13337         }else if(typeof o == "string"){
13338             return encodeString(o);
13339         }else if(typeof o == "number"){
13340             return isFinite(o) ? String(o) : "null";
13341         }else if(typeof o == "boolean"){
13342             return String(o);
13343         }else {
13344             var a = ["{"], b, i, v;
13345             for (i in o) {
13346                 if(!useHasOwn || o.hasOwnProperty(i)) {
13347                     v = o[i];
13348                     switch (typeof v) {
13349                     case "undefined":
13350                     case "function":
13351                     case "unknown":
13352                         break;
13353                     default:
13354                         if(b){
13355                             a.push(',');
13356                         }
13357                         a.push(this.encode(i), ":",
13358                                 v === null ? "null" : this.encode(v));
13359                         b = true;
13360                     }
13361                 }
13362             }
13363             a.push("}");
13364             return a.join("");
13365         }
13366     };
13367     
13368     /**
13369      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13370      * @param {String} json The JSON string
13371      * @return {Object} The resulting object
13372      */
13373     this.decode = function(json){
13374         
13375         return  /** eval:var:json */ eval("(" + json + ')');
13376     };
13377 })();
13378 /** 
13379  * Shorthand for {@link Roo.util.JSON#encode}
13380  * @member Roo encode 
13381  * @method */
13382 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13383 /** 
13384  * Shorthand for {@link Roo.util.JSON#decode}
13385  * @member Roo decode 
13386  * @method */
13387 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13388 /*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398  
13399 /**
13400  * @class Roo.util.Format
13401  * Reusable data formatting functions
13402  * @singleton
13403  */
13404 Roo.util.Format = function(){
13405     var trimRe = /^\s+|\s+$/g;
13406     return {
13407         /**
13408          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13409          * @param {String} value The string to truncate
13410          * @param {Number} length The maximum length to allow before truncating
13411          * @return {String} The converted text
13412          */
13413         ellipsis : function(value, len){
13414             if(value && value.length > len){
13415                 return value.substr(0, len-3)+"...";
13416             }
13417             return value;
13418         },
13419
13420         /**
13421          * Checks a reference and converts it to empty string if it is undefined
13422          * @param {Mixed} value Reference to check
13423          * @return {Mixed} Empty string if converted, otherwise the original value
13424          */
13425         undef : function(value){
13426             return typeof value != "undefined" ? value : "";
13427         },
13428
13429         /**
13430          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13431          * @param {String} value The string to encode
13432          * @return {String} The encoded text
13433          */
13434         htmlEncode : function(value){
13435             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13436         },
13437
13438         /**
13439          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13440          * @param {String} value The string to decode
13441          * @return {String} The decoded text
13442          */
13443         htmlDecode : function(value){
13444             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13445         },
13446
13447         /**
13448          * Trims any whitespace from either side of a string
13449          * @param {String} value The text to trim
13450          * @return {String} The trimmed text
13451          */
13452         trim : function(value){
13453             return String(value).replace(trimRe, "");
13454         },
13455
13456         /**
13457          * Returns a substring from within an original string
13458          * @param {String} value The original text
13459          * @param {Number} start The start index of the substring
13460          * @param {Number} length The length of the substring
13461          * @return {String} The substring
13462          */
13463         substr : function(value, start, length){
13464             return String(value).substr(start, length);
13465         },
13466
13467         /**
13468          * Converts a string to all lower case letters
13469          * @param {String} value The text to convert
13470          * @return {String} The converted text
13471          */
13472         lowercase : function(value){
13473             return String(value).toLowerCase();
13474         },
13475
13476         /**
13477          * Converts a string to all upper case letters
13478          * @param {String} value The text to convert
13479          * @return {String} The converted text
13480          */
13481         uppercase : function(value){
13482             return String(value).toUpperCase();
13483         },
13484
13485         /**
13486          * Converts the first character only of a string to upper case
13487          * @param {String} value The text to convert
13488          * @return {String} The converted text
13489          */
13490         capitalize : function(value){
13491             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13492         },
13493
13494         // private
13495         call : function(value, fn){
13496             if(arguments.length > 2){
13497                 var args = Array.prototype.slice.call(arguments, 2);
13498                 args.unshift(value);
13499                  
13500                 return /** eval:var:value */  eval(fn).apply(window, args);
13501             }else{
13502                 /** eval:var:value */
13503                 return /** eval:var:value */ eval(fn).call(window, value);
13504             }
13505         },
13506
13507        
13508         /**
13509          * safer version of Math.toFixed..??/
13510          * @param {Number/String} value The numeric value to format
13511          * @param {Number/String} value Decimal places 
13512          * @return {String} The formatted currency string
13513          */
13514         toFixed : function(v, n)
13515         {
13516             // why not use to fixed - precision is buggered???
13517             if (!n) {
13518                 return Math.round(v-0);
13519             }
13520             var fact = Math.pow(10,n+1);
13521             v = (Math.round((v-0)*fact))/fact;
13522             var z = (''+fact).substring(2);
13523             if (v == Math.floor(v)) {
13524                 return Math.floor(v) + '.' + z;
13525             }
13526             
13527             // now just padd decimals..
13528             var ps = String(v).split('.');
13529             var fd = (ps[1] + z);
13530             var r = fd.substring(0,n); 
13531             var rm = fd.substring(n); 
13532             if (rm < 5) {
13533                 return ps[0] + '.' + r;
13534             }
13535             r*=1; // turn it into a number;
13536             r++;
13537             if (String(r).length != n) {
13538                 ps[0]*=1;
13539                 ps[0]++;
13540                 r = String(r).substring(1); // chop the end off.
13541             }
13542             
13543             return ps[0] + '.' + r;
13544              
13545         },
13546         
13547         /**
13548          * Format a number as US currency
13549          * @param {Number/String} value The numeric value to format
13550          * @return {String} The formatted currency string
13551          */
13552         usMoney : function(v){
13553             return '$' + Roo.util.Format.number(v);
13554         },
13555         
13556         /**
13557          * Format a number
13558          * eventually this should probably emulate php's number_format
13559          * @param {Number/String} value The numeric value to format
13560          * @param {Number} decimals number of decimal places
13561          * @return {String} The formatted currency string
13562          */
13563         number : function(v,decimals)
13564         {
13565             // multiply and round.
13566             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13567             var mul = Math.pow(10, decimals);
13568             var zero = String(mul).substring(1);
13569             v = (Math.round((v-0)*mul))/mul;
13570             
13571             // if it's '0' number.. then
13572             
13573             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13574             v = String(v);
13575             var ps = v.split('.');
13576             var whole = ps[0];
13577             
13578             
13579             var r = /(\d+)(\d{3})/;
13580             // add comma's
13581             while (r.test(whole)) {
13582                 whole = whole.replace(r, '$1' + ',' + '$2');
13583             }
13584             
13585             
13586             var sub = ps[1] ?
13587                     // has decimals..
13588                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13589                     // does not have decimals
13590                     (decimals ? ('.' + zero) : '');
13591             
13592             
13593             return whole + sub ;
13594         },
13595         
13596         /**
13597          * Parse a value into a formatted date using the specified format pattern.
13598          * @param {Mixed} value The value to format
13599          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13600          * @return {String} The formatted date string
13601          */
13602         date : function(v, format){
13603             if(!v){
13604                 return "";
13605             }
13606             if(!(v instanceof Date)){
13607                 v = new Date(Date.parse(v));
13608             }
13609             return v.dateFormat(format || Roo.util.Format.defaults.date);
13610         },
13611
13612         /**
13613          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13614          * @param {String} format Any valid date format string
13615          * @return {Function} The date formatting function
13616          */
13617         dateRenderer : function(format){
13618             return function(v){
13619                 return Roo.util.Format.date(v, format);  
13620             };
13621         },
13622
13623         // private
13624         stripTagsRE : /<\/?[^>]+>/gi,
13625         
13626         /**
13627          * Strips all HTML tags
13628          * @param {Mixed} value The text from which to strip tags
13629          * @return {String} The stripped text
13630          */
13631         stripTags : function(v){
13632             return !v ? v : String(v).replace(this.stripTagsRE, "");
13633         }
13634     };
13635 }();
13636 Roo.util.Format.defaults = {
13637     date : 'd/M/Y'
13638 };/*
13639  * Based on:
13640  * Ext JS Library 1.1.1
13641  * Copyright(c) 2006-2007, Ext JS, LLC.
13642  *
13643  * Originally Released Under LGPL - original licence link has changed is not relivant.
13644  *
13645  * Fork - LGPL
13646  * <script type="text/javascript">
13647  */
13648
13649
13650  
13651
13652 /**
13653  * @class Roo.MasterTemplate
13654  * @extends Roo.Template
13655  * Provides a template that can have child templates. The syntax is:
13656 <pre><code>
13657 var t = new Roo.MasterTemplate(
13658         '&lt;select name="{name}"&gt;',
13659                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13660         '&lt;/select&gt;'
13661 );
13662 t.add('options', {value: 'foo', text: 'bar'});
13663 // or you can add multiple child elements in one shot
13664 t.addAll('options', [
13665     {value: 'foo', text: 'bar'},
13666     {value: 'foo2', text: 'bar2'},
13667     {value: 'foo3', text: 'bar3'}
13668 ]);
13669 // then append, applying the master template values
13670 t.append('my-form', {name: 'my-select'});
13671 </code></pre>
13672 * A name attribute for the child template is not required if you have only one child
13673 * template or you want to refer to them by index.
13674  */
13675 Roo.MasterTemplate = function(){
13676     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13677     this.originalHtml = this.html;
13678     var st = {};
13679     var m, re = this.subTemplateRe;
13680     re.lastIndex = 0;
13681     var subIndex = 0;
13682     while(m = re.exec(this.html)){
13683         var name = m[1], content = m[2];
13684         st[subIndex] = {
13685             name: name,
13686             index: subIndex,
13687             buffer: [],
13688             tpl : new Roo.Template(content)
13689         };
13690         if(name){
13691             st[name] = st[subIndex];
13692         }
13693         st[subIndex].tpl.compile();
13694         st[subIndex].tpl.call = this.call.createDelegate(this);
13695         subIndex++;
13696     }
13697     this.subCount = subIndex;
13698     this.subs = st;
13699 };
13700 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13701     /**
13702     * The regular expression used to match sub templates
13703     * @type RegExp
13704     * @property
13705     */
13706     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13707
13708     /**
13709      * Applies the passed values to a child template.
13710      * @param {String/Number} name (optional) The name or index of the child template
13711      * @param {Array/Object} values The values to be applied to the template
13712      * @return {MasterTemplate} this
13713      */
13714      add : function(name, values){
13715         if(arguments.length == 1){
13716             values = arguments[0];
13717             name = 0;
13718         }
13719         var s = this.subs[name];
13720         s.buffer[s.buffer.length] = s.tpl.apply(values);
13721         return this;
13722     },
13723
13724     /**
13725      * Applies all the passed values to a child template.
13726      * @param {String/Number} name (optional) The name or index of the child template
13727      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13728      * @param {Boolean} reset (optional) True to reset the template first
13729      * @return {MasterTemplate} this
13730      */
13731     fill : function(name, values, reset){
13732         var a = arguments;
13733         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13734             values = a[0];
13735             name = 0;
13736             reset = a[1];
13737         }
13738         if(reset){
13739             this.reset();
13740         }
13741         for(var i = 0, len = values.length; i < len; i++){
13742             this.add(name, values[i]);
13743         }
13744         return this;
13745     },
13746
13747     /**
13748      * Resets the template for reuse
13749      * @return {MasterTemplate} this
13750      */
13751      reset : function(){
13752         var s = this.subs;
13753         for(var i = 0; i < this.subCount; i++){
13754             s[i].buffer = [];
13755         }
13756         return this;
13757     },
13758
13759     applyTemplate : function(values){
13760         var s = this.subs;
13761         var replaceIndex = -1;
13762         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13763             return s[++replaceIndex].buffer.join("");
13764         });
13765         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13766     },
13767
13768     apply : function(){
13769         return this.applyTemplate.apply(this, arguments);
13770     },
13771
13772     compile : function(){return this;}
13773 });
13774
13775 /**
13776  * Alias for fill().
13777  * @method
13778  */
13779 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13780  /**
13781  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13782  * var tpl = Roo.MasterTemplate.from('element-id');
13783  * @param {String/HTMLElement} el
13784  * @param {Object} config
13785  * @static
13786  */
13787 Roo.MasterTemplate.from = function(el, config){
13788     el = Roo.getDom(el);
13789     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13790 };/*
13791  * Based on:
13792  * Ext JS Library 1.1.1
13793  * Copyright(c) 2006-2007, Ext JS, LLC.
13794  *
13795  * Originally Released Under LGPL - original licence link has changed is not relivant.
13796  *
13797  * Fork - LGPL
13798  * <script type="text/javascript">
13799  */
13800
13801  
13802 /**
13803  * @class Roo.util.CSS
13804  * Utility class for manipulating CSS rules
13805  * @singleton
13806  */
13807 Roo.util.CSS = function(){
13808         var rules = null;
13809         var doc = document;
13810
13811     var camelRe = /(-[a-z])/gi;
13812     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13813
13814    return {
13815    /**
13816     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13817     * tag and appended to the HEAD of the document.
13818     * @param {String|Object} cssText The text containing the css rules
13819     * @param {String} id An id to add to the stylesheet for later removal
13820     * @return {StyleSheet}
13821     */
13822     createStyleSheet : function(cssText, id){
13823         var ss;
13824         var head = doc.getElementsByTagName("head")[0];
13825         var nrules = doc.createElement("style");
13826         nrules.setAttribute("type", "text/css");
13827         if(id){
13828             nrules.setAttribute("id", id);
13829         }
13830         if (typeof(cssText) != 'string') {
13831             // support object maps..
13832             // not sure if this a good idea.. 
13833             // perhaps it should be merged with the general css handling
13834             // and handle js style props.
13835             var cssTextNew = [];
13836             for(var n in cssText) {
13837                 var citems = [];
13838                 for(var k in cssText[n]) {
13839                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13840                 }
13841                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13842                 
13843             }
13844             cssText = cssTextNew.join("\n");
13845             
13846         }
13847        
13848        
13849        if(Roo.isIE){
13850            head.appendChild(nrules);
13851            ss = nrules.styleSheet;
13852            ss.cssText = cssText;
13853        }else{
13854            try{
13855                 nrules.appendChild(doc.createTextNode(cssText));
13856            }catch(e){
13857                nrules.cssText = cssText; 
13858            }
13859            head.appendChild(nrules);
13860            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13861        }
13862        this.cacheStyleSheet(ss);
13863        return ss;
13864    },
13865
13866    /**
13867     * Removes a style or link tag by id
13868     * @param {String} id The id of the tag
13869     */
13870    removeStyleSheet : function(id){
13871        var existing = doc.getElementById(id);
13872        if(existing){
13873            existing.parentNode.removeChild(existing);
13874        }
13875    },
13876
13877    /**
13878     * Dynamically swaps an existing stylesheet reference for a new one
13879     * @param {String} id The id of an existing link tag to remove
13880     * @param {String} url The href of the new stylesheet to include
13881     */
13882    swapStyleSheet : function(id, url){
13883        this.removeStyleSheet(id);
13884        var ss = doc.createElement("link");
13885        ss.setAttribute("rel", "stylesheet");
13886        ss.setAttribute("type", "text/css");
13887        ss.setAttribute("id", id);
13888        ss.setAttribute("href", url);
13889        doc.getElementsByTagName("head")[0].appendChild(ss);
13890    },
13891    
13892    /**
13893     * Refresh the rule cache if you have dynamically added stylesheets
13894     * @return {Object} An object (hash) of rules indexed by selector
13895     */
13896    refreshCache : function(){
13897        return this.getRules(true);
13898    },
13899
13900    // private
13901    cacheStyleSheet : function(stylesheet){
13902        if(!rules){
13903            rules = {};
13904        }
13905        try{// try catch for cross domain access issue
13906            var ssRules = stylesheet.cssRules || stylesheet.rules;
13907            for(var j = ssRules.length-1; j >= 0; --j){
13908                rules[ssRules[j].selectorText] = ssRules[j];
13909            }
13910        }catch(e){}
13911    },
13912    
13913    /**
13914     * Gets all css rules for the document
13915     * @param {Boolean} refreshCache true to refresh the internal cache
13916     * @return {Object} An object (hash) of rules indexed by selector
13917     */
13918    getRules : function(refreshCache){
13919                 if(rules == null || refreshCache){
13920                         rules = {};
13921                         var ds = doc.styleSheets;
13922                         for(var i =0, len = ds.length; i < len; i++){
13923                             try{
13924                         this.cacheStyleSheet(ds[i]);
13925                     }catch(e){} 
13926                 }
13927                 }
13928                 return rules;
13929         },
13930         
13931         /**
13932     * Gets an an individual CSS rule by selector(s)
13933     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13934     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13935     * @return {CSSRule} The CSS rule or null if one is not found
13936     */
13937    getRule : function(selector, refreshCache){
13938                 var rs = this.getRules(refreshCache);
13939                 if(!(selector instanceof Array)){
13940                     return rs[selector];
13941                 }
13942                 for(var i = 0; i < selector.length; i++){
13943                         if(rs[selector[i]]){
13944                                 return rs[selector[i]];
13945                         }
13946                 }
13947                 return null;
13948         },
13949         
13950         
13951         /**
13952     * Updates a rule property
13953     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13954     * @param {String} property The css property
13955     * @param {String} value The new value for the property
13956     * @return {Boolean} true If a rule was found and updated
13957     */
13958    updateRule : function(selector, property, value){
13959                 if(!(selector instanceof Array)){
13960                         var rule = this.getRule(selector);
13961                         if(rule){
13962                                 rule.style[property.replace(camelRe, camelFn)] = value;
13963                                 return true;
13964                         }
13965                 }else{
13966                         for(var i = 0; i < selector.length; i++){
13967                                 if(this.updateRule(selector[i], property, value)){
13968                                         return true;
13969                                 }
13970                         }
13971                 }
13972                 return false;
13973         }
13974    };   
13975 }();/*
13976  * Based on:
13977  * Ext JS Library 1.1.1
13978  * Copyright(c) 2006-2007, Ext JS, LLC.
13979  *
13980  * Originally Released Under LGPL - original licence link has changed is not relivant.
13981  *
13982  * Fork - LGPL
13983  * <script type="text/javascript">
13984  */
13985
13986  
13987
13988 /**
13989  * @class Roo.util.ClickRepeater
13990  * @extends Roo.util.Observable
13991  * 
13992  * A wrapper class which can be applied to any element. Fires a "click" event while the
13993  * mouse is pressed. The interval between firings may be specified in the config but
13994  * defaults to 10 milliseconds.
13995  * 
13996  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13997  * 
13998  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13999  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14000  * Similar to an autorepeat key delay.
14001  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14002  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14003  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14004  *           "interval" and "delay" are ignored. "immediate" is honored.
14005  * @cfg {Boolean} preventDefault True to prevent the default click event
14006  * @cfg {Boolean} stopDefault True to stop the default click event
14007  * 
14008  * @history
14009  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14010  *     2007-02-02 jvs Renamed to ClickRepeater
14011  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14012  *
14013  *  @constructor
14014  * @param {String/HTMLElement/Element} el The element to listen on
14015  * @param {Object} config
14016  **/
14017 Roo.util.ClickRepeater = function(el, config)
14018 {
14019     this.el = Roo.get(el);
14020     this.el.unselectable();
14021
14022     Roo.apply(this, config);
14023
14024     this.addEvents({
14025     /**
14026      * @event mousedown
14027      * Fires when the mouse button is depressed.
14028      * @param {Roo.util.ClickRepeater} this
14029      */
14030         "mousedown" : true,
14031     /**
14032      * @event click
14033      * Fires on a specified interval during the time the element is pressed.
14034      * @param {Roo.util.ClickRepeater} this
14035      */
14036         "click" : true,
14037     /**
14038      * @event mouseup
14039      * Fires when the mouse key is released.
14040      * @param {Roo.util.ClickRepeater} this
14041      */
14042         "mouseup" : true
14043     });
14044
14045     this.el.on("mousedown", this.handleMouseDown, this);
14046     if(this.preventDefault || this.stopDefault){
14047         this.el.on("click", function(e){
14048             if(this.preventDefault){
14049                 e.preventDefault();
14050             }
14051             if(this.stopDefault){
14052                 e.stopEvent();
14053             }
14054         }, this);
14055     }
14056
14057     // allow inline handler
14058     if(this.handler){
14059         this.on("click", this.handler,  this.scope || this);
14060     }
14061
14062     Roo.util.ClickRepeater.superclass.constructor.call(this);
14063 };
14064
14065 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14066     interval : 20,
14067     delay: 250,
14068     preventDefault : true,
14069     stopDefault : false,
14070     timer : 0,
14071
14072     // private
14073     handleMouseDown : function(){
14074         clearTimeout(this.timer);
14075         this.el.blur();
14076         if(this.pressClass){
14077             this.el.addClass(this.pressClass);
14078         }
14079         this.mousedownTime = new Date();
14080
14081         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14082         this.el.on("mouseout", this.handleMouseOut, this);
14083
14084         this.fireEvent("mousedown", this);
14085         this.fireEvent("click", this);
14086         
14087         this.timer = this.click.defer(this.delay || this.interval, this);
14088     },
14089
14090     // private
14091     click : function(){
14092         this.fireEvent("click", this);
14093         this.timer = this.click.defer(this.getInterval(), this);
14094     },
14095
14096     // private
14097     getInterval: function(){
14098         if(!this.accelerate){
14099             return this.interval;
14100         }
14101         var pressTime = this.mousedownTime.getElapsed();
14102         if(pressTime < 500){
14103             return 400;
14104         }else if(pressTime < 1700){
14105             return 320;
14106         }else if(pressTime < 2600){
14107             return 250;
14108         }else if(pressTime < 3500){
14109             return 180;
14110         }else if(pressTime < 4400){
14111             return 140;
14112         }else if(pressTime < 5300){
14113             return 80;
14114         }else if(pressTime < 6200){
14115             return 50;
14116         }else{
14117             return 10;
14118         }
14119     },
14120
14121     // private
14122     handleMouseOut : function(){
14123         clearTimeout(this.timer);
14124         if(this.pressClass){
14125             this.el.removeClass(this.pressClass);
14126         }
14127         this.el.on("mouseover", this.handleMouseReturn, this);
14128     },
14129
14130     // private
14131     handleMouseReturn : function(){
14132         this.el.un("mouseover", this.handleMouseReturn);
14133         if(this.pressClass){
14134             this.el.addClass(this.pressClass);
14135         }
14136         this.click();
14137     },
14138
14139     // private
14140     handleMouseUp : function(){
14141         clearTimeout(this.timer);
14142         this.el.un("mouseover", this.handleMouseReturn);
14143         this.el.un("mouseout", this.handleMouseOut);
14144         Roo.get(document).un("mouseup", this.handleMouseUp);
14145         this.el.removeClass(this.pressClass);
14146         this.fireEvent("mouseup", this);
14147     }
14148 });/*
14149  * Based on:
14150  * Ext JS Library 1.1.1
14151  * Copyright(c) 2006-2007, Ext JS, LLC.
14152  *
14153  * Originally Released Under LGPL - original licence link has changed is not relivant.
14154  *
14155  * Fork - LGPL
14156  * <script type="text/javascript">
14157  */
14158
14159  
14160 /**
14161  * @class Roo.KeyNav
14162  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14163  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14164  * way to implement custom navigation schemes for any UI component.</p>
14165  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14166  * pageUp, pageDown, del, home, end.  Usage:</p>
14167  <pre><code>
14168 var nav = new Roo.KeyNav("my-element", {
14169     "left" : function(e){
14170         this.moveLeft(e.ctrlKey);
14171     },
14172     "right" : function(e){
14173         this.moveRight(e.ctrlKey);
14174     },
14175     "enter" : function(e){
14176         this.save();
14177     },
14178     scope : this
14179 });
14180 </code></pre>
14181  * @constructor
14182  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14183  * @param {Object} config The config
14184  */
14185 Roo.KeyNav = function(el, config){
14186     this.el = Roo.get(el);
14187     Roo.apply(this, config);
14188     if(!this.disabled){
14189         this.disabled = true;
14190         this.enable();
14191     }
14192 };
14193
14194 Roo.KeyNav.prototype = {
14195     /**
14196      * @cfg {Boolean} disabled
14197      * True to disable this KeyNav instance (defaults to false)
14198      */
14199     disabled : false,
14200     /**
14201      * @cfg {String} defaultEventAction
14202      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14203      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14204      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14205      */
14206     defaultEventAction: "stopEvent",
14207     /**
14208      * @cfg {Boolean} forceKeyDown
14209      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14210      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14211      * handle keydown instead of keypress.
14212      */
14213     forceKeyDown : false,
14214
14215     // private
14216     prepareEvent : function(e){
14217         var k = e.getKey();
14218         var h = this.keyToHandler[k];
14219         //if(h && this[h]){
14220         //    e.stopPropagation();
14221         //}
14222         if(Roo.isSafari && h && k >= 37 && k <= 40){
14223             e.stopEvent();
14224         }
14225     },
14226
14227     // private
14228     relay : function(e){
14229         var k = e.getKey();
14230         var h = this.keyToHandler[k];
14231         if(h && this[h]){
14232             if(this.doRelay(e, this[h], h) !== true){
14233                 e[this.defaultEventAction]();
14234             }
14235         }
14236     },
14237
14238     // private
14239     doRelay : function(e, h, hname){
14240         return h.call(this.scope || this, e);
14241     },
14242
14243     // possible handlers
14244     enter : false,
14245     left : false,
14246     right : false,
14247     up : false,
14248     down : false,
14249     tab : false,
14250     esc : false,
14251     pageUp : false,
14252     pageDown : false,
14253     del : false,
14254     home : false,
14255     end : false,
14256
14257     // quick lookup hash
14258     keyToHandler : {
14259         37 : "left",
14260         39 : "right",
14261         38 : "up",
14262         40 : "down",
14263         33 : "pageUp",
14264         34 : "pageDown",
14265         46 : "del",
14266         36 : "home",
14267         35 : "end",
14268         13 : "enter",
14269         27 : "esc",
14270         9  : "tab"
14271     },
14272
14273         /**
14274          * Enable this KeyNav
14275          */
14276         enable: function(){
14277                 if(this.disabled){
14278             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14279             // the EventObject will normalize Safari automatically
14280             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14281                 this.el.on("keydown", this.relay,  this);
14282             }else{
14283                 this.el.on("keydown", this.prepareEvent,  this);
14284                 this.el.on("keypress", this.relay,  this);
14285             }
14286                     this.disabled = false;
14287                 }
14288         },
14289
14290         /**
14291          * Disable this KeyNav
14292          */
14293         disable: function(){
14294                 if(!this.disabled){
14295                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14296                 this.el.un("keydown", this.relay);
14297             }else{
14298                 this.el.un("keydown", this.prepareEvent);
14299                 this.el.un("keypress", this.relay);
14300             }
14301                     this.disabled = true;
14302                 }
14303         }
14304 };/*
14305  * Based on:
14306  * Ext JS Library 1.1.1
14307  * Copyright(c) 2006-2007, Ext JS, LLC.
14308  *
14309  * Originally Released Under LGPL - original licence link has changed is not relivant.
14310  *
14311  * Fork - LGPL
14312  * <script type="text/javascript">
14313  */
14314
14315  
14316 /**
14317  * @class Roo.KeyMap
14318  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14319  * The constructor accepts the same config object as defined by {@link #addBinding}.
14320  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14321  * combination it will call the function with this signature (if the match is a multi-key
14322  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14323  * A KeyMap can also handle a string representation of keys.<br />
14324  * Usage:
14325  <pre><code>
14326 // map one key by key code
14327 var map = new Roo.KeyMap("my-element", {
14328     key: 13, // or Roo.EventObject.ENTER
14329     fn: myHandler,
14330     scope: myObject
14331 });
14332
14333 // map multiple keys to one action by string
14334 var map = new Roo.KeyMap("my-element", {
14335     key: "a\r\n\t",
14336     fn: myHandler,
14337     scope: myObject
14338 });
14339
14340 // map multiple keys to multiple actions by strings and array of codes
14341 var map = new Roo.KeyMap("my-element", [
14342     {
14343         key: [10,13],
14344         fn: function(){ alert("Return was pressed"); }
14345     }, {
14346         key: "abc",
14347         fn: function(){ alert('a, b or c was pressed'); }
14348     }, {
14349         key: "\t",
14350         ctrl:true,
14351         shift:true,
14352         fn: function(){ alert('Control + shift + tab was pressed.'); }
14353     }
14354 ]);
14355 </code></pre>
14356  * <b>Note: A KeyMap starts enabled</b>
14357  * @constructor
14358  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14359  * @param {Object} config The config (see {@link #addBinding})
14360  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14361  */
14362 Roo.KeyMap = function(el, config, eventName){
14363     this.el  = Roo.get(el);
14364     this.eventName = eventName || "keydown";
14365     this.bindings = [];
14366     if(config){
14367         this.addBinding(config);
14368     }
14369     this.enable();
14370 };
14371
14372 Roo.KeyMap.prototype = {
14373     /**
14374      * True to stop the event from bubbling and prevent the default browser action if the
14375      * key was handled by the KeyMap (defaults to false)
14376      * @type Boolean
14377      */
14378     stopEvent : false,
14379
14380     /**
14381      * Add a new binding to this KeyMap. The following config object properties are supported:
14382      * <pre>
14383 Property    Type             Description
14384 ----------  ---------------  ----------------------------------------------------------------------
14385 key         String/Array     A single keycode or an array of keycodes to handle
14386 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14387 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14388 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14389 fn          Function         The function to call when KeyMap finds the expected key combination
14390 scope       Object           The scope of the callback function
14391 </pre>
14392      *
14393      * Usage:
14394      * <pre><code>
14395 // Create a KeyMap
14396 var map = new Roo.KeyMap(document, {
14397     key: Roo.EventObject.ENTER,
14398     fn: handleKey,
14399     scope: this
14400 });
14401
14402 //Add a new binding to the existing KeyMap later
14403 map.addBinding({
14404     key: 'abc',
14405     shift: true,
14406     fn: handleKey,
14407     scope: this
14408 });
14409 </code></pre>
14410      * @param {Object/Array} config A single KeyMap config or an array of configs
14411      */
14412         addBinding : function(config){
14413         if(config instanceof Array){
14414             for(var i = 0, len = config.length; i < len; i++){
14415                 this.addBinding(config[i]);
14416             }
14417             return;
14418         }
14419         var keyCode = config.key,
14420             shift = config.shift, 
14421             ctrl = config.ctrl, 
14422             alt = config.alt,
14423             fn = config.fn,
14424             scope = config.scope;
14425         if(typeof keyCode == "string"){
14426             var ks = [];
14427             var keyString = keyCode.toUpperCase();
14428             for(var j = 0, len = keyString.length; j < len; j++){
14429                 ks.push(keyString.charCodeAt(j));
14430             }
14431             keyCode = ks;
14432         }
14433         var keyArray = keyCode instanceof Array;
14434         var handler = function(e){
14435             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14436                 var k = e.getKey();
14437                 if(keyArray){
14438                     for(var i = 0, len = keyCode.length; i < len; i++){
14439                         if(keyCode[i] == k){
14440                           if(this.stopEvent){
14441                               e.stopEvent();
14442                           }
14443                           fn.call(scope || window, k, e);
14444                           return;
14445                         }
14446                     }
14447                 }else{
14448                     if(k == keyCode){
14449                         if(this.stopEvent){
14450                            e.stopEvent();
14451                         }
14452                         fn.call(scope || window, k, e);
14453                     }
14454                 }
14455             }
14456         };
14457         this.bindings.push(handler);  
14458         },
14459
14460     /**
14461      * Shorthand for adding a single key listener
14462      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14463      * following options:
14464      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14465      * @param {Function} fn The function to call
14466      * @param {Object} scope (optional) The scope of the function
14467      */
14468     on : function(key, fn, scope){
14469         var keyCode, shift, ctrl, alt;
14470         if(typeof key == "object" && !(key instanceof Array)){
14471             keyCode = key.key;
14472             shift = key.shift;
14473             ctrl = key.ctrl;
14474             alt = key.alt;
14475         }else{
14476             keyCode = key;
14477         }
14478         this.addBinding({
14479             key: keyCode,
14480             shift: shift,
14481             ctrl: ctrl,
14482             alt: alt,
14483             fn: fn,
14484             scope: scope
14485         })
14486     },
14487
14488     // private
14489     handleKeyDown : function(e){
14490             if(this.enabled){ //just in case
14491             var b = this.bindings;
14492             for(var i = 0, len = b.length; i < len; i++){
14493                 b[i].call(this, e);
14494             }
14495             }
14496         },
14497         
14498         /**
14499          * Returns true if this KeyMap is enabled
14500          * @return {Boolean} 
14501          */
14502         isEnabled : function(){
14503             return this.enabled;  
14504         },
14505         
14506         /**
14507          * Enables this KeyMap
14508          */
14509         enable: function(){
14510                 if(!this.enabled){
14511                     this.el.on(this.eventName, this.handleKeyDown, this);
14512                     this.enabled = true;
14513                 }
14514         },
14515
14516         /**
14517          * Disable this KeyMap
14518          */
14519         disable: function(){
14520                 if(this.enabled){
14521                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14522                     this.enabled = false;
14523                 }
14524         }
14525 };/*
14526  * Based on:
14527  * Ext JS Library 1.1.1
14528  * Copyright(c) 2006-2007, Ext JS, LLC.
14529  *
14530  * Originally Released Under LGPL - original licence link has changed is not relivant.
14531  *
14532  * Fork - LGPL
14533  * <script type="text/javascript">
14534  */
14535
14536  
14537 /**
14538  * @class Roo.util.TextMetrics
14539  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14540  * wide, in pixels, a given block of text will be.
14541  * @singleton
14542  */
14543 Roo.util.TextMetrics = function(){
14544     var shared;
14545     return {
14546         /**
14547          * Measures the size of the specified text
14548          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14549          * that can affect the size of the rendered text
14550          * @param {String} text The text to measure
14551          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14552          * in order to accurately measure the text height
14553          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14554          */
14555         measure : function(el, text, fixedWidth){
14556             if(!shared){
14557                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14558             }
14559             shared.bind(el);
14560             shared.setFixedWidth(fixedWidth || 'auto');
14561             return shared.getSize(text);
14562         },
14563
14564         /**
14565          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14566          * the overhead of multiple calls to initialize the style properties on each measurement.
14567          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14568          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14569          * in order to accurately measure the text height
14570          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14571          */
14572         createInstance : function(el, fixedWidth){
14573             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14574         }
14575     };
14576 }();
14577
14578  
14579
14580 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14581     var ml = new Roo.Element(document.createElement('div'));
14582     document.body.appendChild(ml.dom);
14583     ml.position('absolute');
14584     ml.setLeftTop(-1000, -1000);
14585     ml.hide();
14586
14587     if(fixedWidth){
14588         ml.setWidth(fixedWidth);
14589     }
14590      
14591     var instance = {
14592         /**
14593          * Returns the size of the specified text based on the internal element's style and width properties
14594          * @memberOf Roo.util.TextMetrics.Instance#
14595          * @param {String} text The text to measure
14596          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14597          */
14598         getSize : function(text){
14599             ml.update(text);
14600             var s = ml.getSize();
14601             ml.update('');
14602             return s;
14603         },
14604
14605         /**
14606          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14607          * that can affect the size of the rendered text
14608          * @memberOf Roo.util.TextMetrics.Instance#
14609          * @param {String/HTMLElement} el The element, dom node or id
14610          */
14611         bind : function(el){
14612             ml.setStyle(
14613                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14614             );
14615         },
14616
14617         /**
14618          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14619          * to set a fixed width in order to accurately measure the text height.
14620          * @memberOf Roo.util.TextMetrics.Instance#
14621          * @param {Number} width The width to set on the element
14622          */
14623         setFixedWidth : function(width){
14624             ml.setWidth(width);
14625         },
14626
14627         /**
14628          * Returns the measured width of the specified text
14629          * @memberOf Roo.util.TextMetrics.Instance#
14630          * @param {String} text The text to measure
14631          * @return {Number} width The width in pixels
14632          */
14633         getWidth : function(text){
14634             ml.dom.style.width = 'auto';
14635             return this.getSize(text).width;
14636         },
14637
14638         /**
14639          * Returns the measured height of the specified text.  For multiline text, be sure to call
14640          * {@link #setFixedWidth} if necessary.
14641          * @memberOf Roo.util.TextMetrics.Instance#
14642          * @param {String} text The text to measure
14643          * @return {Number} height The height in pixels
14644          */
14645         getHeight : function(text){
14646             return this.getSize(text).height;
14647         }
14648     };
14649
14650     instance.bind(bindTo);
14651
14652     return instance;
14653 };
14654
14655 // backwards compat
14656 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14657  * Based on:
14658  * Ext JS Library 1.1.1
14659  * Copyright(c) 2006-2007, Ext JS, LLC.
14660  *
14661  * Originally Released Under LGPL - original licence link has changed is not relivant.
14662  *
14663  * Fork - LGPL
14664  * <script type="text/javascript">
14665  */
14666
14667 /**
14668  * @class Roo.state.Provider
14669  * Abstract base class for state provider implementations. This class provides methods
14670  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14671  * Provider interface.
14672  */
14673 Roo.state.Provider = function(){
14674     /**
14675      * @event statechange
14676      * Fires when a state change occurs.
14677      * @param {Provider} this This state provider
14678      * @param {String} key The state key which was changed
14679      * @param {String} value The encoded value for the state
14680      */
14681     this.addEvents({
14682         "statechange": true
14683     });
14684     this.state = {};
14685     Roo.state.Provider.superclass.constructor.call(this);
14686 };
14687 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14688     /**
14689      * Returns the current value for a key
14690      * @param {String} name The key name
14691      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14692      * @return {Mixed} The state data
14693      */
14694     get : function(name, defaultValue){
14695         return typeof this.state[name] == "undefined" ?
14696             defaultValue : this.state[name];
14697     },
14698     
14699     /**
14700      * Clears a value from the state
14701      * @param {String} name The key name
14702      */
14703     clear : function(name){
14704         delete this.state[name];
14705         this.fireEvent("statechange", this, name, null);
14706     },
14707     
14708     /**
14709      * Sets the value for a key
14710      * @param {String} name The key name
14711      * @param {Mixed} value The value to set
14712      */
14713     set : function(name, value){
14714         this.state[name] = value;
14715         this.fireEvent("statechange", this, name, value);
14716     },
14717     
14718     /**
14719      * Decodes a string previously encoded with {@link #encodeValue}.
14720      * @param {String} value The value to decode
14721      * @return {Mixed} The decoded value
14722      */
14723     decodeValue : function(cookie){
14724         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14725         var matches = re.exec(unescape(cookie));
14726         if(!matches || !matches[1]) return; // non state cookie
14727         var type = matches[1];
14728         var v = matches[2];
14729         switch(type){
14730             case "n":
14731                 return parseFloat(v);
14732             case "d":
14733                 return new Date(Date.parse(v));
14734             case "b":
14735                 return (v == "1");
14736             case "a":
14737                 var all = [];
14738                 var values = v.split("^");
14739                 for(var i = 0, len = values.length; i < len; i++){
14740                     all.push(this.decodeValue(values[i]));
14741                 }
14742                 return all;
14743            case "o":
14744                 var all = {};
14745                 var values = v.split("^");
14746                 for(var i = 0, len = values.length; i < len; i++){
14747                     var kv = values[i].split("=");
14748                     all[kv[0]] = this.decodeValue(kv[1]);
14749                 }
14750                 return all;
14751            default:
14752                 return v;
14753         }
14754     },
14755     
14756     /**
14757      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14758      * @param {Mixed} value The value to encode
14759      * @return {String} The encoded value
14760      */
14761     encodeValue : function(v){
14762         var enc;
14763         if(typeof v == "number"){
14764             enc = "n:" + v;
14765         }else if(typeof v == "boolean"){
14766             enc = "b:" + (v ? "1" : "0");
14767         }else if(v instanceof Date){
14768             enc = "d:" + v.toGMTString();
14769         }else if(v instanceof Array){
14770             var flat = "";
14771             for(var i = 0, len = v.length; i < len; i++){
14772                 flat += this.encodeValue(v[i]);
14773                 if(i != len-1) flat += "^";
14774             }
14775             enc = "a:" + flat;
14776         }else if(typeof v == "object"){
14777             var flat = "";
14778             for(var key in v){
14779                 if(typeof v[key] != "function"){
14780                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14781                 }
14782             }
14783             enc = "o:" + flat.substring(0, flat.length-1);
14784         }else{
14785             enc = "s:" + v;
14786         }
14787         return escape(enc);        
14788     }
14789 });
14790
14791 /*
14792  * Based on:
14793  * Ext JS Library 1.1.1
14794  * Copyright(c) 2006-2007, Ext JS, LLC.
14795  *
14796  * Originally Released Under LGPL - original licence link has changed is not relivant.
14797  *
14798  * Fork - LGPL
14799  * <script type="text/javascript">
14800  */
14801 /**
14802  * @class Roo.state.Manager
14803  * This is the global state manager. By default all components that are "state aware" check this class
14804  * for state information if you don't pass them a custom state provider. In order for this class
14805  * to be useful, it must be initialized with a provider when your application initializes.
14806  <pre><code>
14807 // in your initialization function
14808 init : function(){
14809    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14810    ...
14811    // supposed you have a {@link Roo.BorderLayout}
14812    var layout = new Roo.BorderLayout(...);
14813    layout.restoreState();
14814    // or a {Roo.BasicDialog}
14815    var dialog = new Roo.BasicDialog(...);
14816    dialog.restoreState();
14817  </code></pre>
14818  * @singleton
14819  */
14820 Roo.state.Manager = function(){
14821     var provider = new Roo.state.Provider();
14822     
14823     return {
14824         /**
14825          * Configures the default state provider for your application
14826          * @param {Provider} stateProvider The state provider to set
14827          */
14828         setProvider : function(stateProvider){
14829             provider = stateProvider;
14830         },
14831         
14832         /**
14833          * Returns the current value for a key
14834          * @param {String} name The key name
14835          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14836          * @return {Mixed} The state data
14837          */
14838         get : function(key, defaultValue){
14839             return provider.get(key, defaultValue);
14840         },
14841         
14842         /**
14843          * Sets the value for a key
14844          * @param {String} name The key name
14845          * @param {Mixed} value The state data
14846          */
14847          set : function(key, value){
14848             provider.set(key, value);
14849         },
14850         
14851         /**
14852          * Clears a value from the state
14853          * @param {String} name The key name
14854          */
14855         clear : function(key){
14856             provider.clear(key);
14857         },
14858         
14859         /**
14860          * Gets the currently configured state provider
14861          * @return {Provider} The state provider
14862          */
14863         getProvider : function(){
14864             return provider;
14865         }
14866     };
14867 }();
14868 /*
14869  * Based on:
14870  * Ext JS Library 1.1.1
14871  * Copyright(c) 2006-2007, Ext JS, LLC.
14872  *
14873  * Originally Released Under LGPL - original licence link has changed is not relivant.
14874  *
14875  * Fork - LGPL
14876  * <script type="text/javascript">
14877  */
14878 /**
14879  * @class Roo.state.CookieProvider
14880  * @extends Roo.state.Provider
14881  * The default Provider implementation which saves state via cookies.
14882  * <br />Usage:
14883  <pre><code>
14884    var cp = new Roo.state.CookieProvider({
14885        path: "/cgi-bin/",
14886        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14887        domain: "roojs.com"
14888    })
14889    Roo.state.Manager.setProvider(cp);
14890  </code></pre>
14891  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14892  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14893  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14894  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14895  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14896  * domain the page is running on including the 'www' like 'www.roojs.com')
14897  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14898  * @constructor
14899  * Create a new CookieProvider
14900  * @param {Object} config The configuration object
14901  */
14902 Roo.state.CookieProvider = function(config){
14903     Roo.state.CookieProvider.superclass.constructor.call(this);
14904     this.path = "/";
14905     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14906     this.domain = null;
14907     this.secure = false;
14908     Roo.apply(this, config);
14909     this.state = this.readCookies();
14910 };
14911
14912 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14913     // private
14914     set : function(name, value){
14915         if(typeof value == "undefined" || value === null){
14916             this.clear(name);
14917             return;
14918         }
14919         this.setCookie(name, value);
14920         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14921     },
14922
14923     // private
14924     clear : function(name){
14925         this.clearCookie(name);
14926         Roo.state.CookieProvider.superclass.clear.call(this, name);
14927     },
14928
14929     // private
14930     readCookies : function(){
14931         var cookies = {};
14932         var c = document.cookie + ";";
14933         var re = /\s?(.*?)=(.*?);/g;
14934         var matches;
14935         while((matches = re.exec(c)) != null){
14936             var name = matches[1];
14937             var value = matches[2];
14938             if(name && name.substring(0,3) == "ys-"){
14939                 cookies[name.substr(3)] = this.decodeValue(value);
14940             }
14941         }
14942         return cookies;
14943     },
14944
14945     // private
14946     setCookie : function(name, value){
14947         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14948            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14949            ((this.path == null) ? "" : ("; path=" + this.path)) +
14950            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14951            ((this.secure == true) ? "; secure" : "");
14952     },
14953
14954     // private
14955     clearCookie : function(name){
14956         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14957            ((this.path == null) ? "" : ("; path=" + this.path)) +
14958            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14959            ((this.secure == true) ? "; secure" : "");
14960     }
14961 });/*
14962  * Based on:
14963  * Ext JS Library 1.1.1
14964  * Copyright(c) 2006-2007, Ext JS, LLC.
14965  *
14966  * Originally Released Under LGPL - original licence link has changed is not relivant.
14967  *
14968  * Fork - LGPL
14969  * <script type="text/javascript">
14970  */
14971  
14972
14973 /**
14974  * @class Roo.ComponentMgr
14975  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14976  * @singleton
14977  */
14978 Roo.ComponentMgr = function(){
14979     var all = new Roo.util.MixedCollection();
14980
14981     return {
14982         /**
14983          * Registers a component.
14984          * @param {Roo.Component} c The component
14985          */
14986         register : function(c){
14987             all.add(c);
14988         },
14989
14990         /**
14991          * Unregisters a component.
14992          * @param {Roo.Component} c The component
14993          */
14994         unregister : function(c){
14995             all.remove(c);
14996         },
14997
14998         /**
14999          * Returns a component by id
15000          * @param {String} id The component id
15001          */
15002         get : function(id){
15003             return all.get(id);
15004         },
15005
15006         /**
15007          * Registers a function that will be called when a specified component is added to ComponentMgr
15008          * @param {String} id The component id
15009          * @param {Funtction} fn The callback function
15010          * @param {Object} scope The scope of the callback
15011          */
15012         onAvailable : function(id, fn, scope){
15013             all.on("add", function(index, o){
15014                 if(o.id == id){
15015                     fn.call(scope || o, o);
15016                     all.un("add", fn, scope);
15017                 }
15018             });
15019         }
15020     };
15021 }();/*
15022  * Based on:
15023  * Ext JS Library 1.1.1
15024  * Copyright(c) 2006-2007, Ext JS, LLC.
15025  *
15026  * Originally Released Under LGPL - original licence link has changed is not relivant.
15027  *
15028  * Fork - LGPL
15029  * <script type="text/javascript">
15030  */
15031  
15032 /**
15033  * @class Roo.Component
15034  * @extends Roo.util.Observable
15035  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15036  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15037  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15038  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15039  * All visual components (widgets) that require rendering into a layout should subclass Component.
15040  * @constructor
15041  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15042  * 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
15043  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15044  */
15045 Roo.Component = function(config){
15046     config = config || {};
15047     if(config.tagName || config.dom || typeof config == "string"){ // element object
15048         config = {el: config, id: config.id || config};
15049     }
15050     this.initialConfig = config;
15051
15052     Roo.apply(this, config);
15053     this.addEvents({
15054         /**
15055          * @event disable
15056          * Fires after the component is disabled.
15057              * @param {Roo.Component} this
15058              */
15059         disable : true,
15060         /**
15061          * @event enable
15062          * Fires after the component is enabled.
15063              * @param {Roo.Component} this
15064              */
15065         enable : true,
15066         /**
15067          * @event beforeshow
15068          * Fires before the component is shown.  Return false to stop the show.
15069              * @param {Roo.Component} this
15070              */
15071         beforeshow : true,
15072         /**
15073          * @event show
15074          * Fires after the component is shown.
15075              * @param {Roo.Component} this
15076              */
15077         show : true,
15078         /**
15079          * @event beforehide
15080          * Fires before the component is hidden. Return false to stop the hide.
15081              * @param {Roo.Component} this
15082              */
15083         beforehide : true,
15084         /**
15085          * @event hide
15086          * Fires after the component is hidden.
15087              * @param {Roo.Component} this
15088              */
15089         hide : true,
15090         /**
15091          * @event beforerender
15092          * Fires before the component is rendered. Return false to stop the render.
15093              * @param {Roo.Component} this
15094              */
15095         beforerender : true,
15096         /**
15097          * @event render
15098          * Fires after the component is rendered.
15099              * @param {Roo.Component} this
15100              */
15101         render : true,
15102         /**
15103          * @event beforedestroy
15104          * Fires before the component is destroyed. Return false to stop the destroy.
15105              * @param {Roo.Component} this
15106              */
15107         beforedestroy : true,
15108         /**
15109          * @event destroy
15110          * Fires after the component is destroyed.
15111              * @param {Roo.Component} this
15112              */
15113         destroy : true
15114     });
15115     if(!this.id){
15116         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15117     }
15118     Roo.ComponentMgr.register(this);
15119     Roo.Component.superclass.constructor.call(this);
15120     this.initComponent();
15121     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15122         this.render(this.renderTo);
15123         delete this.renderTo;
15124     }
15125 };
15126
15127 /** @private */
15128 Roo.Component.AUTO_ID = 1000;
15129
15130 Roo.extend(Roo.Component, Roo.util.Observable, {
15131     /**
15132      * @scope Roo.Component.prototype
15133      * @type {Boolean}
15134      * true if this component is hidden. Read-only.
15135      */
15136     hidden : false,
15137     /**
15138      * @type {Boolean}
15139      * true if this component is disabled. Read-only.
15140      */
15141     disabled : false,
15142     /**
15143      * @type {Boolean}
15144      * true if this component has been rendered. Read-only.
15145      */
15146     rendered : false,
15147     
15148     /** @cfg {String} disableClass
15149      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15150      */
15151     disabledClass : "x-item-disabled",
15152         /** @cfg {Boolean} allowDomMove
15153          * Whether the component can move the Dom node when rendering (defaults to true).
15154          */
15155     allowDomMove : true,
15156     /** @cfg {String} hideMode
15157      * How this component should hidden. Supported values are
15158      * "visibility" (css visibility), "offsets" (negative offset position) and
15159      * "display" (css display) - defaults to "display".
15160      */
15161     hideMode: 'display',
15162
15163     /** @private */
15164     ctype : "Roo.Component",
15165
15166     /**
15167      * @cfg {String} actionMode 
15168      * which property holds the element that used for  hide() / show() / disable() / enable()
15169      * default is 'el' 
15170      */
15171     actionMode : "el",
15172
15173     /** @private */
15174     getActionEl : function(){
15175         return this[this.actionMode];
15176     },
15177
15178     initComponent : Roo.emptyFn,
15179     /**
15180      * If this is a lazy rendering component, render it to its container element.
15181      * @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.
15182      */
15183     render : function(container, position){
15184         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15185             if(!container && this.el){
15186                 this.el = Roo.get(this.el);
15187                 container = this.el.dom.parentNode;
15188                 this.allowDomMove = false;
15189             }
15190             this.container = Roo.get(container);
15191             this.rendered = true;
15192             if(position !== undefined){
15193                 if(typeof position == 'number'){
15194                     position = this.container.dom.childNodes[position];
15195                 }else{
15196                     position = Roo.getDom(position);
15197                 }
15198             }
15199             this.onRender(this.container, position || null);
15200             if(this.cls){
15201                 this.el.addClass(this.cls);
15202                 delete this.cls;
15203             }
15204             if(this.style){
15205                 this.el.applyStyles(this.style);
15206                 delete this.style;
15207             }
15208             this.fireEvent("render", this);
15209             this.afterRender(this.container);
15210             if(this.hidden){
15211                 this.hide();
15212             }
15213             if(this.disabled){
15214                 this.disable();
15215             }
15216         }
15217         return this;
15218     },
15219
15220     /** @private */
15221     // default function is not really useful
15222     onRender : function(ct, position){
15223         if(this.el){
15224             this.el = Roo.get(this.el);
15225             if(this.allowDomMove !== false){
15226                 ct.dom.insertBefore(this.el.dom, position);
15227             }
15228         }
15229     },
15230
15231     /** @private */
15232     getAutoCreate : function(){
15233         var cfg = typeof this.autoCreate == "object" ?
15234                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15235         if(this.id && !cfg.id){
15236             cfg.id = this.id;
15237         }
15238         return cfg;
15239     },
15240
15241     /** @private */
15242     afterRender : Roo.emptyFn,
15243
15244     /**
15245      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15246      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15247      */
15248     destroy : function(){
15249         if(this.fireEvent("beforedestroy", this) !== false){
15250             this.purgeListeners();
15251             this.beforeDestroy();
15252             if(this.rendered){
15253                 this.el.removeAllListeners();
15254                 this.el.remove();
15255                 if(this.actionMode == "container"){
15256                     this.container.remove();
15257                 }
15258             }
15259             this.onDestroy();
15260             Roo.ComponentMgr.unregister(this);
15261             this.fireEvent("destroy", this);
15262         }
15263     },
15264
15265         /** @private */
15266     beforeDestroy : function(){
15267
15268     },
15269
15270         /** @private */
15271         onDestroy : function(){
15272
15273     },
15274
15275     /**
15276      * Returns the underlying {@link Roo.Element}.
15277      * @return {Roo.Element} The element
15278      */
15279     getEl : function(){
15280         return this.el;
15281     },
15282
15283     /**
15284      * Returns the id of this component.
15285      * @return {String}
15286      */
15287     getId : function(){
15288         return this.id;
15289     },
15290
15291     /**
15292      * Try to focus this component.
15293      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15294      * @return {Roo.Component} this
15295      */
15296     focus : function(selectText){
15297         if(this.rendered){
15298             this.el.focus();
15299             if(selectText === true){
15300                 this.el.dom.select();
15301             }
15302         }
15303         return this;
15304     },
15305
15306     /** @private */
15307     blur : function(){
15308         if(this.rendered){
15309             this.el.blur();
15310         }
15311         return this;
15312     },
15313
15314     /**
15315      * Disable this component.
15316      * @return {Roo.Component} this
15317      */
15318     disable : function(){
15319         if(this.rendered){
15320             this.onDisable();
15321         }
15322         this.disabled = true;
15323         this.fireEvent("disable", this);
15324         return this;
15325     },
15326
15327         // private
15328     onDisable : function(){
15329         this.getActionEl().addClass(this.disabledClass);
15330         this.el.dom.disabled = true;
15331     },
15332
15333     /**
15334      * Enable this component.
15335      * @return {Roo.Component} this
15336      */
15337     enable : function(){
15338         if(this.rendered){
15339             this.onEnable();
15340         }
15341         this.disabled = false;
15342         this.fireEvent("enable", this);
15343         return this;
15344     },
15345
15346         // private
15347     onEnable : function(){
15348         this.getActionEl().removeClass(this.disabledClass);
15349         this.el.dom.disabled = false;
15350     },
15351
15352     /**
15353      * Convenience function for setting disabled/enabled by boolean.
15354      * @param {Boolean} disabled
15355      */
15356     setDisabled : function(disabled){
15357         this[disabled ? "disable" : "enable"]();
15358     },
15359
15360     /**
15361      * Show this component.
15362      * @return {Roo.Component} this
15363      */
15364     show: function(){
15365         if(this.fireEvent("beforeshow", this) !== false){
15366             this.hidden = false;
15367             if(this.rendered){
15368                 this.onShow();
15369             }
15370             this.fireEvent("show", this);
15371         }
15372         return this;
15373     },
15374
15375     // private
15376     onShow : function(){
15377         var ae = this.getActionEl();
15378         if(this.hideMode == 'visibility'){
15379             ae.dom.style.visibility = "visible";
15380         }else if(this.hideMode == 'offsets'){
15381             ae.removeClass('x-hidden');
15382         }else{
15383             ae.dom.style.display = "";
15384         }
15385     },
15386
15387     /**
15388      * Hide this component.
15389      * @return {Roo.Component} this
15390      */
15391     hide: function(){
15392         if(this.fireEvent("beforehide", this) !== false){
15393             this.hidden = true;
15394             if(this.rendered){
15395                 this.onHide();
15396             }
15397             this.fireEvent("hide", this);
15398         }
15399         return this;
15400     },
15401
15402     // private
15403     onHide : function(){
15404         var ae = this.getActionEl();
15405         if(this.hideMode == 'visibility'){
15406             ae.dom.style.visibility = "hidden";
15407         }else if(this.hideMode == 'offsets'){
15408             ae.addClass('x-hidden');
15409         }else{
15410             ae.dom.style.display = "none";
15411         }
15412     },
15413
15414     /**
15415      * Convenience function to hide or show this component by boolean.
15416      * @param {Boolean} visible True to show, false to hide
15417      * @return {Roo.Component} this
15418      */
15419     setVisible: function(visible){
15420         if(visible) {
15421             this.show();
15422         }else{
15423             this.hide();
15424         }
15425         return this;
15426     },
15427
15428     /**
15429      * Returns true if this component is visible.
15430      */
15431     isVisible : function(){
15432         return this.getActionEl().isVisible();
15433     },
15434
15435     cloneConfig : function(overrides){
15436         overrides = overrides || {};
15437         var id = overrides.id || Roo.id();
15438         var cfg = Roo.applyIf(overrides, this.initialConfig);
15439         cfg.id = id; // prevent dup id
15440         return new this.constructor(cfg);
15441     }
15442 });/*
15443  * Based on:
15444  * Ext JS Library 1.1.1
15445  * Copyright(c) 2006-2007, Ext JS, LLC.
15446  *
15447  * Originally Released Under LGPL - original licence link has changed is not relivant.
15448  *
15449  * Fork - LGPL
15450  * <script type="text/javascript">
15451  */
15452
15453 /**
15454  * @class Roo.BoxComponent
15455  * @extends Roo.Component
15456  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15457  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15458  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15459  * layout containers.
15460  * @constructor
15461  * @param {Roo.Element/String/Object} config The configuration options.
15462  */
15463 Roo.BoxComponent = function(config){
15464     Roo.Component.call(this, config);
15465     this.addEvents({
15466         /**
15467          * @event resize
15468          * Fires after the component is resized.
15469              * @param {Roo.Component} this
15470              * @param {Number} adjWidth The box-adjusted width that was set
15471              * @param {Number} adjHeight The box-adjusted height that was set
15472              * @param {Number} rawWidth The width that was originally specified
15473              * @param {Number} rawHeight The height that was originally specified
15474              */
15475         resize : true,
15476         /**
15477          * @event move
15478          * Fires after the component is moved.
15479              * @param {Roo.Component} this
15480              * @param {Number} x The new x position
15481              * @param {Number} y The new y position
15482              */
15483         move : true
15484     });
15485 };
15486
15487 Roo.extend(Roo.BoxComponent, Roo.Component, {
15488     // private, set in afterRender to signify that the component has been rendered
15489     boxReady : false,
15490     // private, used to defer height settings to subclasses
15491     deferHeight: false,
15492     /** @cfg {Number} width
15493      * width (optional) size of component
15494      */
15495      /** @cfg {Number} height
15496      * height (optional) size of component
15497      */
15498      
15499     /**
15500      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15501      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15502      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15503      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15504      * @return {Roo.BoxComponent} this
15505      */
15506     setSize : function(w, h){
15507         // support for standard size objects
15508         if(typeof w == 'object'){
15509             h = w.height;
15510             w = w.width;
15511         }
15512         // not rendered
15513         if(!this.boxReady){
15514             this.width = w;
15515             this.height = h;
15516             return this;
15517         }
15518
15519         // prevent recalcs when not needed
15520         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15521             return this;
15522         }
15523         this.lastSize = {width: w, height: h};
15524
15525         var adj = this.adjustSize(w, h);
15526         var aw = adj.width, ah = adj.height;
15527         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15528             var rz = this.getResizeEl();
15529             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15530                 rz.setSize(aw, ah);
15531             }else if(!this.deferHeight && ah !== undefined){
15532                 rz.setHeight(ah);
15533             }else if(aw !== undefined){
15534                 rz.setWidth(aw);
15535             }
15536             this.onResize(aw, ah, w, h);
15537             this.fireEvent('resize', this, aw, ah, w, h);
15538         }
15539         return this;
15540     },
15541
15542     /**
15543      * Gets the current size of the component's underlying element.
15544      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15545      */
15546     getSize : function(){
15547         return this.el.getSize();
15548     },
15549
15550     /**
15551      * Gets the current XY position of the component's underlying element.
15552      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15553      * @return {Array} The XY position of the element (e.g., [100, 200])
15554      */
15555     getPosition : function(local){
15556         if(local === true){
15557             return [this.el.getLeft(true), this.el.getTop(true)];
15558         }
15559         return this.xy || this.el.getXY();
15560     },
15561
15562     /**
15563      * Gets the current box measurements of the component's underlying element.
15564      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15565      * @returns {Object} box An object in the format {x, y, width, height}
15566      */
15567     getBox : function(local){
15568         var s = this.el.getSize();
15569         if(local){
15570             s.x = this.el.getLeft(true);
15571             s.y = this.el.getTop(true);
15572         }else{
15573             var xy = this.xy || this.el.getXY();
15574             s.x = xy[0];
15575             s.y = xy[1];
15576         }
15577         return s;
15578     },
15579
15580     /**
15581      * Sets the current box measurements of the component's underlying element.
15582      * @param {Object} box An object in the format {x, y, width, height}
15583      * @returns {Roo.BoxComponent} this
15584      */
15585     updateBox : function(box){
15586         this.setSize(box.width, box.height);
15587         this.setPagePosition(box.x, box.y);
15588         return this;
15589     },
15590
15591     // protected
15592     getResizeEl : function(){
15593         return this.resizeEl || this.el;
15594     },
15595
15596     // protected
15597     getPositionEl : function(){
15598         return this.positionEl || this.el;
15599     },
15600
15601     /**
15602      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15603      * This method fires the move event.
15604      * @param {Number} left The new left
15605      * @param {Number} top The new top
15606      * @returns {Roo.BoxComponent} this
15607      */
15608     setPosition : function(x, y){
15609         this.x = x;
15610         this.y = y;
15611         if(!this.boxReady){
15612             return this;
15613         }
15614         var adj = this.adjustPosition(x, y);
15615         var ax = adj.x, ay = adj.y;
15616
15617         var el = this.getPositionEl();
15618         if(ax !== undefined || ay !== undefined){
15619             if(ax !== undefined && ay !== undefined){
15620                 el.setLeftTop(ax, ay);
15621             }else if(ax !== undefined){
15622                 el.setLeft(ax);
15623             }else if(ay !== undefined){
15624                 el.setTop(ay);
15625             }
15626             this.onPosition(ax, ay);
15627             this.fireEvent('move', this, ax, ay);
15628         }
15629         return this;
15630     },
15631
15632     /**
15633      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15634      * This method fires the move event.
15635      * @param {Number} x The new x position
15636      * @param {Number} y The new y position
15637      * @returns {Roo.BoxComponent} this
15638      */
15639     setPagePosition : function(x, y){
15640         this.pageX = x;
15641         this.pageY = y;
15642         if(!this.boxReady){
15643             return;
15644         }
15645         if(x === undefined || y === undefined){ // cannot translate undefined points
15646             return;
15647         }
15648         var p = this.el.translatePoints(x, y);
15649         this.setPosition(p.left, p.top);
15650         return this;
15651     },
15652
15653     // private
15654     onRender : function(ct, position){
15655         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15656         if(this.resizeEl){
15657             this.resizeEl = Roo.get(this.resizeEl);
15658         }
15659         if(this.positionEl){
15660             this.positionEl = Roo.get(this.positionEl);
15661         }
15662     },
15663
15664     // private
15665     afterRender : function(){
15666         Roo.BoxComponent.superclass.afterRender.call(this);
15667         this.boxReady = true;
15668         this.setSize(this.width, this.height);
15669         if(this.x || this.y){
15670             this.setPosition(this.x, this.y);
15671         }
15672         if(this.pageX || this.pageY){
15673             this.setPagePosition(this.pageX, this.pageY);
15674         }
15675     },
15676
15677     /**
15678      * Force the component's size to recalculate based on the underlying element's current height and width.
15679      * @returns {Roo.BoxComponent} this
15680      */
15681     syncSize : function(){
15682         delete this.lastSize;
15683         this.setSize(this.el.getWidth(), this.el.getHeight());
15684         return this;
15685     },
15686
15687     /**
15688      * Called after the component is resized, this method is empty by default but can be implemented by any
15689      * subclass that needs to perform custom logic after a resize occurs.
15690      * @param {Number} adjWidth The box-adjusted width that was set
15691      * @param {Number} adjHeight The box-adjusted height that was set
15692      * @param {Number} rawWidth The width that was originally specified
15693      * @param {Number} rawHeight The height that was originally specified
15694      */
15695     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15696
15697     },
15698
15699     /**
15700      * Called after the component is moved, this method is empty by default but can be implemented by any
15701      * subclass that needs to perform custom logic after a move occurs.
15702      * @param {Number} x The new x position
15703      * @param {Number} y The new y position
15704      */
15705     onPosition : function(x, y){
15706
15707     },
15708
15709     // private
15710     adjustSize : function(w, h){
15711         if(this.autoWidth){
15712             w = 'auto';
15713         }
15714         if(this.autoHeight){
15715             h = 'auto';
15716         }
15717         return {width : w, height: h};
15718     },
15719
15720     // private
15721     adjustPosition : function(x, y){
15722         return {x : x, y: y};
15723     }
15724 });/*
15725  * Original code for Roojs - LGPL
15726  * <script type="text/javascript">
15727  */
15728  
15729 /**
15730  * @class Roo.XComponent
15731  * A delayed Element creator...
15732  * Or a way to group chunks of interface together.
15733  * 
15734  * Mypart.xyx = new Roo.XComponent({
15735
15736     parent : 'Mypart.xyz', // empty == document.element.!!
15737     order : '001',
15738     name : 'xxxx'
15739     region : 'xxxx'
15740     disabled : function() {} 
15741      
15742     tree : function() { // return an tree of xtype declared components
15743         var MODULE = this;
15744         return 
15745         {
15746             xtype : 'NestedLayoutPanel',
15747             // technicall
15748         }
15749      ]
15750  *})
15751  *
15752  *
15753  * It can be used to build a big heiracy, with parent etc.
15754  * or you can just use this to render a single compoent to a dom element
15755  * MYPART.render(Roo.Element | String(id) | dom_element )
15756  * 
15757  * @extends Roo.util.Observable
15758  * @constructor
15759  * @param cfg {Object} configuration of component
15760  * 
15761  */
15762 Roo.XComponent = function(cfg) {
15763     Roo.apply(this, cfg);
15764     this.addEvents({ 
15765         /**
15766              * @event built
15767              * Fires when this the componnt is built
15768              * @param {Roo.XComponent} c the component
15769              */
15770         'built' : true
15771         
15772     });
15773     this.region = this.region || 'center'; // default..
15774     Roo.XComponent.register(this);
15775     this.modules = false;
15776     this.el = false; // where the layout goes..
15777     
15778     
15779 }
15780 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15781     /**
15782      * @property el
15783      * The created element (with Roo.factory())
15784      * @type {Roo.Layout}
15785      */
15786     el  : false,
15787     
15788     /**
15789      * @property el
15790      * for BC  - use el in new code
15791      * @type {Roo.Layout}
15792      */
15793     panel : false,
15794     
15795     /**
15796      * @property layout
15797      * for BC  - use el in new code
15798      * @type {Roo.Layout}
15799      */
15800     layout : false,
15801     
15802      /**
15803      * @cfg {Function|boolean} disabled
15804      * If this module is disabled by some rule, return true from the funtion
15805      */
15806     disabled : false,
15807     
15808     /**
15809      * @cfg {String} parent 
15810      * Name of parent element which it get xtype added to..
15811      */
15812     parent: false,
15813     
15814     /**
15815      * @cfg {String} order
15816      * Used to set the order in which elements are created (usefull for multiple tabs)
15817      */
15818     
15819     order : false,
15820     /**
15821      * @cfg {String} name
15822      * String to display while loading.
15823      */
15824     name : false,
15825     /**
15826      * @cfg {String} region
15827      * Region to render component to (defaults to center)
15828      */
15829     region : 'center',
15830     
15831     /**
15832      * @cfg {Array} items
15833      * A single item array - the first element is the root of the tree..
15834      * It's done this way to stay compatible with the Xtype system...
15835      */
15836     items : false,
15837     
15838     /**
15839      * @property _tree
15840      * The method that retuns the tree of parts that make up this compoennt 
15841      * @type {function}
15842      */
15843     _tree  : false,
15844     
15845      /**
15846      * render
15847      * render element to dom or tree
15848      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15849      */
15850     
15851     render : function(el)
15852     {
15853         
15854         el = el || false;
15855         var hp = this.parent ? 1 : 0;
15856         
15857         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15858             // if parent is a '#.....' string, then let's use that..
15859             var ename = this.parent.substr(1)
15860             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15861             el = Roo.get(ename);
15862             if (!el && !this.parent) {
15863                 Roo.log("Warning - element can not be found :#" + ename );
15864                 return;
15865             }
15866         }
15867         
15868         
15869         if (!this.parent) {
15870             
15871             el = el ? Roo.get(el) : false;      
15872             
15873             // it's a top level one..
15874             this.parent =  {
15875                 el : new Roo.BorderLayout(el || document.body, {
15876                 
15877                      center: {
15878                          titlebar: false,
15879                          autoScroll:false,
15880                          closeOnTab: true,
15881                          tabPosition: 'top',
15882                           //resizeTabs: true,
15883                          alwaysShowTabs: el && hp? false :  true,
15884                          hideTabs: el || !hp ? true :  false,
15885                          minTabWidth: 140
15886                      }
15887                  })
15888             }
15889         }
15890         
15891                 if (!this.parent.el) {
15892                         // probably an old style ctor, which has been disabled.
15893                         return;
15894                         
15895                 }
15896                 // The 'tree' method is  '_tree now' 
15897             
15898         var tree = this._tree ? this._tree() : this.tree();
15899         tree.region = tree.region || this.region;
15900         
15901         if (this.parent.el === true) {
15902             // bootstrap... - body..
15903             this.parent.el = Roo.factory(tree);
15904         }
15905         
15906         this.el = this.parent.el.addxtype(tree);
15907         this.fireEvent('built', this);
15908         
15909         this.panel = this.el;
15910         this.layout = this.panel.layout;
15911                 this.parentLayout = this.parent.layout  || false;  
15912          
15913     }
15914     
15915 });
15916
15917 Roo.apply(Roo.XComponent, {
15918     /**
15919      * @property  hideProgress
15920      * true to disable the building progress bar.. usefull on single page renders.
15921      * @type Boolean
15922      */
15923     hideProgress : false,
15924     /**
15925      * @property  buildCompleted
15926      * True when the builder has completed building the interface.
15927      * @type Boolean
15928      */
15929     buildCompleted : false,
15930      
15931     /**
15932      * @property  topModule
15933      * the upper most module - uses document.element as it's constructor.
15934      * @type Object
15935      */
15936      
15937     topModule  : false,
15938       
15939     /**
15940      * @property  modules
15941      * array of modules to be created by registration system.
15942      * @type {Array} of Roo.XComponent
15943      */
15944     
15945     modules : [],
15946     /**
15947      * @property  elmodules
15948      * array of modules to be created by which use #ID 
15949      * @type {Array} of Roo.XComponent
15950      */
15951      
15952     elmodules : [],
15953
15954      /**
15955      * @property  build_from_html
15956      * Build elements from html - used by bootstrap HTML stuff 
15957      *    - this is cleared after build is completed
15958      * @type {boolean} true  (default false)
15959      */
15960      
15961     build_from_html : false,
15962
15963     /**
15964      * Register components to be built later.
15965      *
15966      * This solves the following issues
15967      * - Building is not done on page load, but after an authentication process has occured.
15968      * - Interface elements are registered on page load
15969      * - Parent Interface elements may not be loaded before child, so this handles that..
15970      * 
15971      *
15972      * example:
15973      * 
15974      * MyApp.register({
15975           order : '000001',
15976           module : 'Pman.Tab.projectMgr',
15977           region : 'center',
15978           parent : 'Pman.layout',
15979           disabled : false,  // or use a function..
15980         })
15981      
15982      * * @param {Object} details about module
15983      */
15984     register : function(obj) {
15985                 
15986         Roo.XComponent.event.fireEvent('register', obj);
15987         switch(typeof(obj.disabled) ) {
15988                 
15989             case 'undefined':
15990                 break;
15991             
15992             case 'function':
15993                 if ( obj.disabled() ) {
15994                         return;
15995                 }
15996                 break;
15997             
15998             default:
15999                 if (obj.disabled) {
16000                         return;
16001                 }
16002                 break;
16003         }
16004                 
16005         this.modules.push(obj);
16006          
16007     },
16008     /**
16009      * convert a string to an object..
16010      * eg. 'AAA.BBB' -> finds AAA.BBB
16011
16012      */
16013     
16014     toObject : function(str)
16015     {
16016         if (!str || typeof(str) == 'object') {
16017             return str;
16018         }
16019         if (str.substring(0,1) == '#') {
16020             return str;
16021         }
16022
16023         var ar = str.split('.');
16024         var rt, o;
16025         rt = ar.shift();
16026             /** eval:var:o */
16027         try {
16028             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16029         } catch (e) {
16030             throw "Module not found : " + str;
16031         }
16032         
16033         if (o === false) {
16034             throw "Module not found : " + str;
16035         }
16036         Roo.each(ar, function(e) {
16037             if (typeof(o[e]) == 'undefined') {
16038                 throw "Module not found : " + str;
16039             }
16040             o = o[e];
16041         });
16042         
16043         return o;
16044         
16045     },
16046     
16047     
16048     /**
16049      * move modules into their correct place in the tree..
16050      * 
16051      */
16052     preBuild : function ()
16053     {
16054         var _t = this;
16055         Roo.each(this.modules , function (obj)
16056         {
16057             Roo.XComponent.event.fireEvent('beforebuild', obj);
16058             
16059             var opar = obj.parent;
16060             try { 
16061                 obj.parent = this.toObject(opar);
16062             } catch(e) {
16063                 Roo.log("parent:toObject failed: " + e.toString());
16064                 return;
16065             }
16066             
16067             if (!obj.parent) {
16068                 Roo.debug && Roo.log("GOT top level module");
16069                 Roo.debug && Roo.log(obj);
16070                 obj.modules = new Roo.util.MixedCollection(false, 
16071                     function(o) { return o.order + '' }
16072                 );
16073                 this.topModule = obj;
16074                 return;
16075             }
16076                         // parent is a string (usually a dom element name..)
16077             if (typeof(obj.parent) == 'string') {
16078                 this.elmodules.push(obj);
16079                 return;
16080             }
16081             if (obj.parent.constructor != Roo.XComponent) {
16082                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16083             }
16084             if (!obj.parent.modules) {
16085                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16086                     function(o) { return o.order + '' }
16087                 );
16088             }
16089             if (obj.parent.disabled) {
16090                 obj.disabled = true;
16091             }
16092             obj.parent.modules.add(obj);
16093         }, this);
16094     },
16095     
16096      /**
16097      * make a list of modules to build.
16098      * @return {Array} list of modules. 
16099      */ 
16100     
16101     buildOrder : function()
16102     {
16103         var _this = this;
16104         var cmp = function(a,b) {   
16105             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16106         };
16107         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16108             throw "No top level modules to build";
16109         }
16110         
16111         // make a flat list in order of modules to build.
16112         var mods = this.topModule ? [ this.topModule ] : [];
16113                 
16114         
16115         // elmodules (is a list of DOM based modules )
16116         Roo.each(this.elmodules, function(e) {
16117             mods.push(e);
16118             if (!this.topModule &&
16119                 typeof(e.parent) == 'string' &&
16120                 e.parent.substring(0,1) == '#' &&
16121                 Roo.get(e.parent.substr(1))
16122                ) {
16123                 
16124                 _this.topModule = e;
16125             }
16126             
16127         });
16128
16129         
16130         // add modules to their parents..
16131         var addMod = function(m) {
16132             Roo.debug && Roo.log("build Order: add: " + m.name);
16133                 
16134             mods.push(m);
16135             if (m.modules && !m.disabled) {
16136                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16137                 m.modules.keySort('ASC',  cmp );
16138                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16139     
16140                 m.modules.each(addMod);
16141             } else {
16142                 Roo.debug && Roo.log("build Order: no child modules");
16143             }
16144             // not sure if this is used any more..
16145             if (m.finalize) {
16146                 m.finalize.name = m.name + " (clean up) ";
16147                 mods.push(m.finalize);
16148             }
16149             
16150         }
16151         if (this.topModule && this.topModule.modules) { 
16152             this.topModule.modules.keySort('ASC',  cmp );
16153             this.topModule.modules.each(addMod);
16154         } 
16155         return mods;
16156     },
16157     
16158      /**
16159      * Build the registered modules.
16160      * @param {Object} parent element.
16161      * @param {Function} optional method to call after module has been added.
16162      * 
16163      */ 
16164    
16165     build : function(opts) 
16166     {
16167         
16168         if (typeof(opts) != 'undefined') {
16169             Roo.apply(this,opts);
16170         }
16171         
16172         this.preBuild();
16173         var mods = this.buildOrder();
16174       
16175         //this.allmods = mods;
16176         //Roo.debug && Roo.log(mods);
16177         //return;
16178         if (!mods.length) { // should not happen
16179             throw "NO modules!!!";
16180         }
16181         
16182         
16183         var msg = "Building Interface...";
16184         // flash it up as modal - so we store the mask!?
16185         if (!this.hideProgress && Roo.MessageBox) {
16186             Roo.MessageBox.show({ title: 'loading' });
16187             Roo.MessageBox.show({
16188                title: "Please wait...",
16189                msg: msg,
16190                width:450,
16191                progress:true,
16192                closable:false,
16193                modal: false
16194               
16195             });
16196         }
16197         var total = mods.length;
16198         
16199         var _this = this;
16200         var progressRun = function() {
16201             if (!mods.length) {
16202                 Roo.debug && Roo.log('hide?');
16203                 if (!this.hideProgress && Roo.MessageBox) {
16204                     Roo.MessageBox.hide();
16205                 }
16206                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16207                 
16208                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16209                 
16210                 // THE END...
16211                 return false;   
16212             }
16213             
16214             var m = mods.shift();
16215             
16216             
16217             Roo.debug && Roo.log(m);
16218             // not sure if this is supported any more.. - modules that are are just function
16219             if (typeof(m) == 'function') { 
16220                 m.call(this);
16221                 return progressRun.defer(10, _this);
16222             } 
16223             
16224             
16225             msg = "Building Interface " + (total  - mods.length) + 
16226                     " of " + total + 
16227                     (m.name ? (' - ' + m.name) : '');
16228                         Roo.debug && Roo.log(msg);
16229             if (!this.hideProgress &&  Roo.MessageBox) { 
16230                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16231             }
16232             
16233          
16234             // is the module disabled?
16235             var disabled = (typeof(m.disabled) == 'function') ?
16236                 m.disabled.call(m.module.disabled) : m.disabled;    
16237             
16238             
16239             if (disabled) {
16240                 return progressRun(); // we do not update the display!
16241             }
16242             
16243             // now build 
16244             
16245                         
16246                         
16247             m.render();
16248             // it's 10 on top level, and 1 on others??? why...
16249             return progressRun.defer(10, _this);
16250              
16251         }
16252         progressRun.defer(1, _this);
16253      
16254         
16255         
16256     },
16257         
16258         
16259         /**
16260          * Event Object.
16261          *
16262          *
16263          */
16264         event: false, 
16265     /**
16266          * wrapper for event.on - aliased later..  
16267          * Typically use to register a event handler for register:
16268          *
16269          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16270          *
16271          */
16272     on : false
16273    
16274     
16275     
16276 });
16277
16278 Roo.XComponent.event = new Roo.util.Observable({
16279                 events : { 
16280                         /**
16281                          * @event register
16282                          * Fires when an Component is registered,
16283                          * set the disable property on the Component to stop registration.
16284                          * @param {Roo.XComponent} c the component being registerd.
16285                          * 
16286                          */
16287                         'register' : true,
16288             /**
16289                          * @event beforebuild
16290                          * Fires before each Component is built
16291                          * can be used to apply permissions.
16292                          * @param {Roo.XComponent} c the component being registerd.
16293                          * 
16294                          */
16295                         'beforebuild' : true,
16296                         /**
16297                          * @event buildcomplete
16298                          * Fires on the top level element when all elements have been built
16299                          * @param {Roo.XComponent} the top level component.
16300                          */
16301                         'buildcomplete' : true
16302                         
16303                 }
16304 });
16305
16306 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16307  /*
16308  * Based on:
16309  * Ext JS Library 1.1.1
16310  * Copyright(c) 2006-2007, Ext JS, LLC.
16311  *
16312  * Originally Released Under LGPL - original licence link has changed is not relivant.
16313  *
16314  * Fork - LGPL
16315  * <script type="text/javascript">
16316  */
16317
16318
16319
16320 /*
16321  * These classes are derivatives of the similarly named classes in the YUI Library.
16322  * The original license:
16323  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16324  * Code licensed under the BSD License:
16325  * http://developer.yahoo.net/yui/license.txt
16326  */
16327
16328 (function() {
16329
16330 var Event=Roo.EventManager;
16331 var Dom=Roo.lib.Dom;
16332
16333 /**
16334  * @class Roo.dd.DragDrop
16335  * @extends Roo.util.Observable
16336  * Defines the interface and base operation of items that that can be
16337  * dragged or can be drop targets.  It was designed to be extended, overriding
16338  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16339  * Up to three html elements can be associated with a DragDrop instance:
16340  * <ul>
16341  * <li>linked element: the element that is passed into the constructor.
16342  * This is the element which defines the boundaries for interaction with
16343  * other DragDrop objects.</li>
16344  * <li>handle element(s): The drag operation only occurs if the element that
16345  * was clicked matches a handle element.  By default this is the linked
16346  * element, but there are times that you will want only a portion of the
16347  * linked element to initiate the drag operation, and the setHandleElId()
16348  * method provides a way to define this.</li>
16349  * <li>drag element: this represents the element that would be moved along
16350  * with the cursor during a drag operation.  By default, this is the linked
16351  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16352  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16353  * </li>
16354  * </ul>
16355  * This class should not be instantiated until the onload event to ensure that
16356  * the associated elements are available.
16357  * The following would define a DragDrop obj that would interact with any
16358  * other DragDrop obj in the "group1" group:
16359  * <pre>
16360  *  dd = new Roo.dd.DragDrop("div1", "group1");
16361  * </pre>
16362  * Since none of the event handlers have been implemented, nothing would
16363  * actually happen if you were to run the code above.  Normally you would
16364  * override this class or one of the default implementations, but you can
16365  * also override the methods you want on an instance of the class...
16366  * <pre>
16367  *  dd.onDragDrop = function(e, id) {
16368  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16369  *  }
16370  * </pre>
16371  * @constructor
16372  * @param {String} id of the element that is linked to this instance
16373  * @param {String} sGroup the group of related DragDrop objects
16374  * @param {object} config an object containing configurable attributes
16375  *                Valid properties for DragDrop:
16376  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16377  */
16378 Roo.dd.DragDrop = function(id, sGroup, config) {
16379     if (id) {
16380         this.init(id, sGroup, config);
16381     }
16382     
16383 };
16384
16385 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16386
16387     /**
16388      * The id of the element associated with this object.  This is what we
16389      * refer to as the "linked element" because the size and position of
16390      * this element is used to determine when the drag and drop objects have
16391      * interacted.
16392      * @property id
16393      * @type String
16394      */
16395     id: null,
16396
16397     /**
16398      * Configuration attributes passed into the constructor
16399      * @property config
16400      * @type object
16401      */
16402     config: null,
16403
16404     /**
16405      * The id of the element that will be dragged.  By default this is same
16406      * as the linked element , but could be changed to another element. Ex:
16407      * Roo.dd.DDProxy
16408      * @property dragElId
16409      * @type String
16410      * @private
16411      */
16412     dragElId: null,
16413
16414     /**
16415      * the id of the element that initiates the drag operation.  By default
16416      * this is the linked element, but could be changed to be a child of this
16417      * element.  This lets us do things like only starting the drag when the
16418      * header element within the linked html element is clicked.
16419      * @property handleElId
16420      * @type String
16421      * @private
16422      */
16423     handleElId: null,
16424
16425     /**
16426      * An associative array of HTML tags that will be ignored if clicked.
16427      * @property invalidHandleTypes
16428      * @type {string: string}
16429      */
16430     invalidHandleTypes: null,
16431
16432     /**
16433      * An associative array of ids for elements that will be ignored if clicked
16434      * @property invalidHandleIds
16435      * @type {string: string}
16436      */
16437     invalidHandleIds: null,
16438
16439     /**
16440      * An indexted array of css class names for elements that will be ignored
16441      * if clicked.
16442      * @property invalidHandleClasses
16443      * @type string[]
16444      */
16445     invalidHandleClasses: null,
16446
16447     /**
16448      * The linked element's absolute X position at the time the drag was
16449      * started
16450      * @property startPageX
16451      * @type int
16452      * @private
16453      */
16454     startPageX: 0,
16455
16456     /**
16457      * The linked element's absolute X position at the time the drag was
16458      * started
16459      * @property startPageY
16460      * @type int
16461      * @private
16462      */
16463     startPageY: 0,
16464
16465     /**
16466      * The group defines a logical collection of DragDrop objects that are
16467      * related.  Instances only get events when interacting with other
16468      * DragDrop object in the same group.  This lets us define multiple
16469      * groups using a single DragDrop subclass if we want.
16470      * @property groups
16471      * @type {string: string}
16472      */
16473     groups: null,
16474
16475     /**
16476      * Individual drag/drop instances can be locked.  This will prevent
16477      * onmousedown start drag.
16478      * @property locked
16479      * @type boolean
16480      * @private
16481      */
16482     locked: false,
16483
16484     /**
16485      * Lock this instance
16486      * @method lock
16487      */
16488     lock: function() { this.locked = true; },
16489
16490     /**
16491      * Unlock this instace
16492      * @method unlock
16493      */
16494     unlock: function() { this.locked = false; },
16495
16496     /**
16497      * By default, all insances can be a drop target.  This can be disabled by
16498      * setting isTarget to false.
16499      * @method isTarget
16500      * @type boolean
16501      */
16502     isTarget: true,
16503
16504     /**
16505      * The padding configured for this drag and drop object for calculating
16506      * the drop zone intersection with this object.
16507      * @method padding
16508      * @type int[]
16509      */
16510     padding: null,
16511
16512     /**
16513      * Cached reference to the linked element
16514      * @property _domRef
16515      * @private
16516      */
16517     _domRef: null,
16518
16519     /**
16520      * Internal typeof flag
16521      * @property __ygDragDrop
16522      * @private
16523      */
16524     __ygDragDrop: true,
16525
16526     /**
16527      * Set to true when horizontal contraints are applied
16528      * @property constrainX
16529      * @type boolean
16530      * @private
16531      */
16532     constrainX: false,
16533
16534     /**
16535      * Set to true when vertical contraints are applied
16536      * @property constrainY
16537      * @type boolean
16538      * @private
16539      */
16540     constrainY: false,
16541
16542     /**
16543      * The left constraint
16544      * @property minX
16545      * @type int
16546      * @private
16547      */
16548     minX: 0,
16549
16550     /**
16551      * The right constraint
16552      * @property maxX
16553      * @type int
16554      * @private
16555      */
16556     maxX: 0,
16557
16558     /**
16559      * The up constraint
16560      * @property minY
16561      * @type int
16562      * @type int
16563      * @private
16564      */
16565     minY: 0,
16566
16567     /**
16568      * The down constraint
16569      * @property maxY
16570      * @type int
16571      * @private
16572      */
16573     maxY: 0,
16574
16575     /**
16576      * Maintain offsets when we resetconstraints.  Set to true when you want
16577      * the position of the element relative to its parent to stay the same
16578      * when the page changes
16579      *
16580      * @property maintainOffset
16581      * @type boolean
16582      */
16583     maintainOffset: false,
16584
16585     /**
16586      * Array of pixel locations the element will snap to if we specified a
16587      * horizontal graduation/interval.  This array is generated automatically
16588      * when you define a tick interval.
16589      * @property xTicks
16590      * @type int[]
16591      */
16592     xTicks: null,
16593
16594     /**
16595      * Array of pixel locations the element will snap to if we specified a
16596      * vertical graduation/interval.  This array is generated automatically
16597      * when you define a tick interval.
16598      * @property yTicks
16599      * @type int[]
16600      */
16601     yTicks: null,
16602
16603     /**
16604      * By default the drag and drop instance will only respond to the primary
16605      * button click (left button for a right-handed mouse).  Set to true to
16606      * allow drag and drop to start with any mouse click that is propogated
16607      * by the browser
16608      * @property primaryButtonOnly
16609      * @type boolean
16610      */
16611     primaryButtonOnly: true,
16612
16613     /**
16614      * The availabe property is false until the linked dom element is accessible.
16615      * @property available
16616      * @type boolean
16617      */
16618     available: false,
16619
16620     /**
16621      * By default, drags can only be initiated if the mousedown occurs in the
16622      * region the linked element is.  This is done in part to work around a
16623      * bug in some browsers that mis-report the mousedown if the previous
16624      * mouseup happened outside of the window.  This property is set to true
16625      * if outer handles are defined.
16626      *
16627      * @property hasOuterHandles
16628      * @type boolean
16629      * @default false
16630      */
16631     hasOuterHandles: false,
16632
16633     /**
16634      * Code that executes immediately before the startDrag event
16635      * @method b4StartDrag
16636      * @private
16637      */
16638     b4StartDrag: function(x, y) { },
16639
16640     /**
16641      * Abstract method called after a drag/drop object is clicked
16642      * and the drag or mousedown time thresholds have beeen met.
16643      * @method startDrag
16644      * @param {int} X click location
16645      * @param {int} Y click location
16646      */
16647     startDrag: function(x, y) { /* override this */ },
16648
16649     /**
16650      * Code that executes immediately before the onDrag event
16651      * @method b4Drag
16652      * @private
16653      */
16654     b4Drag: function(e) { },
16655
16656     /**
16657      * Abstract method called during the onMouseMove event while dragging an
16658      * object.
16659      * @method onDrag
16660      * @param {Event} e the mousemove event
16661      */
16662     onDrag: function(e) { /* override this */ },
16663
16664     /**
16665      * Abstract method called when this element fist begins hovering over
16666      * another DragDrop obj
16667      * @method onDragEnter
16668      * @param {Event} e the mousemove event
16669      * @param {String|DragDrop[]} id In POINT mode, the element
16670      * id this is hovering over.  In INTERSECT mode, an array of one or more
16671      * dragdrop items being hovered over.
16672      */
16673     onDragEnter: function(e, id) { /* override this */ },
16674
16675     /**
16676      * Code that executes immediately before the onDragOver event
16677      * @method b4DragOver
16678      * @private
16679      */
16680     b4DragOver: function(e) { },
16681
16682     /**
16683      * Abstract method called when this element is hovering over another
16684      * DragDrop obj
16685      * @method onDragOver
16686      * @param {Event} e the mousemove event
16687      * @param {String|DragDrop[]} id In POINT mode, the element
16688      * id this is hovering over.  In INTERSECT mode, an array of dd items
16689      * being hovered over.
16690      */
16691     onDragOver: function(e, id) { /* override this */ },
16692
16693     /**
16694      * Code that executes immediately before the onDragOut event
16695      * @method b4DragOut
16696      * @private
16697      */
16698     b4DragOut: function(e) { },
16699
16700     /**
16701      * Abstract method called when we are no longer hovering over an element
16702      * @method onDragOut
16703      * @param {Event} e the mousemove event
16704      * @param {String|DragDrop[]} id In POINT mode, the element
16705      * id this was hovering over.  In INTERSECT mode, an array of dd items
16706      * that the mouse is no longer over.
16707      */
16708     onDragOut: function(e, id) { /* override this */ },
16709
16710     /**
16711      * Code that executes immediately before the onDragDrop event
16712      * @method b4DragDrop
16713      * @private
16714      */
16715     b4DragDrop: function(e) { },
16716
16717     /**
16718      * Abstract method called when this item is dropped on another DragDrop
16719      * obj
16720      * @method onDragDrop
16721      * @param {Event} e the mouseup event
16722      * @param {String|DragDrop[]} id In POINT mode, the element
16723      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16724      * was dropped on.
16725      */
16726     onDragDrop: function(e, id) { /* override this */ },
16727
16728     /**
16729      * Abstract method called when this item is dropped on an area with no
16730      * drop target
16731      * @method onInvalidDrop
16732      * @param {Event} e the mouseup event
16733      */
16734     onInvalidDrop: function(e) { /* override this */ },
16735
16736     /**
16737      * Code that executes immediately before the endDrag event
16738      * @method b4EndDrag
16739      * @private
16740      */
16741     b4EndDrag: function(e) { },
16742
16743     /**
16744      * Fired when we are done dragging the object
16745      * @method endDrag
16746      * @param {Event} e the mouseup event
16747      */
16748     endDrag: function(e) { /* override this */ },
16749
16750     /**
16751      * Code executed immediately before the onMouseDown event
16752      * @method b4MouseDown
16753      * @param {Event} e the mousedown event
16754      * @private
16755      */
16756     b4MouseDown: function(e) {  },
16757
16758     /**
16759      * Event handler that fires when a drag/drop obj gets a mousedown
16760      * @method onMouseDown
16761      * @param {Event} e the mousedown event
16762      */
16763     onMouseDown: function(e) { /* override this */ },
16764
16765     /**
16766      * Event handler that fires when a drag/drop obj gets a mouseup
16767      * @method onMouseUp
16768      * @param {Event} e the mouseup event
16769      */
16770     onMouseUp: function(e) { /* override this */ },
16771
16772     /**
16773      * Override the onAvailable method to do what is needed after the initial
16774      * position was determined.
16775      * @method onAvailable
16776      */
16777     onAvailable: function () {
16778     },
16779
16780     /*
16781      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16782      * @type Object
16783      */
16784     defaultPadding : {left:0, right:0, top:0, bottom:0},
16785
16786     /*
16787      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16788  *
16789  * Usage:
16790  <pre><code>
16791  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16792                 { dragElId: "existingProxyDiv" });
16793  dd.startDrag = function(){
16794      this.constrainTo("parent-id");
16795  };
16796  </code></pre>
16797  * Or you can initalize it using the {@link Roo.Element} object:
16798  <pre><code>
16799  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16800      startDrag : function(){
16801          this.constrainTo("parent-id");
16802      }
16803  });
16804  </code></pre>
16805      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16806      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16807      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16808      * an object containing the sides to pad. For example: {right:10, bottom:10}
16809      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16810      */
16811     constrainTo : function(constrainTo, pad, inContent){
16812         if(typeof pad == "number"){
16813             pad = {left: pad, right:pad, top:pad, bottom:pad};
16814         }
16815         pad = pad || this.defaultPadding;
16816         var b = Roo.get(this.getEl()).getBox();
16817         var ce = Roo.get(constrainTo);
16818         var s = ce.getScroll();
16819         var c, cd = ce.dom;
16820         if(cd == document.body){
16821             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16822         }else{
16823             xy = ce.getXY();
16824             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16825         }
16826
16827
16828         var topSpace = b.y - c.y;
16829         var leftSpace = b.x - c.x;
16830
16831         this.resetConstraints();
16832         this.setXConstraint(leftSpace - (pad.left||0), // left
16833                 c.width - leftSpace - b.width - (pad.right||0) //right
16834         );
16835         this.setYConstraint(topSpace - (pad.top||0), //top
16836                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16837         );
16838     },
16839
16840     /**
16841      * Returns a reference to the linked element
16842      * @method getEl
16843      * @return {HTMLElement} the html element
16844      */
16845     getEl: function() {
16846         if (!this._domRef) {
16847             this._domRef = Roo.getDom(this.id);
16848         }
16849
16850         return this._domRef;
16851     },
16852
16853     /**
16854      * Returns a reference to the actual element to drag.  By default this is
16855      * the same as the html element, but it can be assigned to another
16856      * element. An example of this can be found in Roo.dd.DDProxy
16857      * @method getDragEl
16858      * @return {HTMLElement} the html element
16859      */
16860     getDragEl: function() {
16861         return Roo.getDom(this.dragElId);
16862     },
16863
16864     /**
16865      * Sets up the DragDrop object.  Must be called in the constructor of any
16866      * Roo.dd.DragDrop subclass
16867      * @method init
16868      * @param id the id of the linked element
16869      * @param {String} sGroup the group of related items
16870      * @param {object} config configuration attributes
16871      */
16872     init: function(id, sGroup, config) {
16873         this.initTarget(id, sGroup, config);
16874         if (!Roo.isTouch) {
16875             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16876         }
16877         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16878         // Event.on(this.id, "selectstart", Event.preventDefault);
16879     },
16880
16881     /**
16882      * Initializes Targeting functionality only... the object does not
16883      * get a mousedown handler.
16884      * @method initTarget
16885      * @param id the id of the linked element
16886      * @param {String} sGroup the group of related items
16887      * @param {object} config configuration attributes
16888      */
16889     initTarget: function(id, sGroup, config) {
16890
16891         // configuration attributes
16892         this.config = config || {};
16893
16894         // create a local reference to the drag and drop manager
16895         this.DDM = Roo.dd.DDM;
16896         // initialize the groups array
16897         this.groups = {};
16898
16899         // assume that we have an element reference instead of an id if the
16900         // parameter is not a string
16901         if (typeof id !== "string") {
16902             id = Roo.id(id);
16903         }
16904
16905         // set the id
16906         this.id = id;
16907
16908         // add to an interaction group
16909         this.addToGroup((sGroup) ? sGroup : "default");
16910
16911         // We don't want to register this as the handle with the manager
16912         // so we just set the id rather than calling the setter.
16913         this.handleElId = id;
16914
16915         // the linked element is the element that gets dragged by default
16916         this.setDragElId(id);
16917
16918         // by default, clicked anchors will not start drag operations.
16919         this.invalidHandleTypes = { A: "A" };
16920         this.invalidHandleIds = {};
16921         this.invalidHandleClasses = [];
16922
16923         this.applyConfig();
16924
16925         this.handleOnAvailable();
16926     },
16927
16928     /**
16929      * Applies the configuration parameters that were passed into the constructor.
16930      * This is supposed to happen at each level through the inheritance chain.  So
16931      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16932      * DragDrop in order to get all of the parameters that are available in
16933      * each object.
16934      * @method applyConfig
16935      */
16936     applyConfig: function() {
16937
16938         // configurable properties:
16939         //    padding, isTarget, maintainOffset, primaryButtonOnly
16940         this.padding           = this.config.padding || [0, 0, 0, 0];
16941         this.isTarget          = (this.config.isTarget !== false);
16942         this.maintainOffset    = (this.config.maintainOffset);
16943         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16944
16945     },
16946
16947     /**
16948      * Executed when the linked element is available
16949      * @method handleOnAvailable
16950      * @private
16951      */
16952     handleOnAvailable: function() {
16953         this.available = true;
16954         this.resetConstraints();
16955         this.onAvailable();
16956     },
16957
16958      /**
16959      * Configures the padding for the target zone in px.  Effectively expands
16960      * (or reduces) the virtual object size for targeting calculations.
16961      * Supports css-style shorthand; if only one parameter is passed, all sides
16962      * will have that padding, and if only two are passed, the top and bottom
16963      * will have the first param, the left and right the second.
16964      * @method setPadding
16965      * @param {int} iTop    Top pad
16966      * @param {int} iRight  Right pad
16967      * @param {int} iBot    Bot pad
16968      * @param {int} iLeft   Left pad
16969      */
16970     setPadding: function(iTop, iRight, iBot, iLeft) {
16971         // this.padding = [iLeft, iRight, iTop, iBot];
16972         if (!iRight && 0 !== iRight) {
16973             this.padding = [iTop, iTop, iTop, iTop];
16974         } else if (!iBot && 0 !== iBot) {
16975             this.padding = [iTop, iRight, iTop, iRight];
16976         } else {
16977             this.padding = [iTop, iRight, iBot, iLeft];
16978         }
16979     },
16980
16981     /**
16982      * Stores the initial placement of the linked element.
16983      * @method setInitialPosition
16984      * @param {int} diffX   the X offset, default 0
16985      * @param {int} diffY   the Y offset, default 0
16986      */
16987     setInitPosition: function(diffX, diffY) {
16988         var el = this.getEl();
16989
16990         if (!this.DDM.verifyEl(el)) {
16991             return;
16992         }
16993
16994         var dx = diffX || 0;
16995         var dy = diffY || 0;
16996
16997         var p = Dom.getXY( el );
16998
16999         this.initPageX = p[0] - dx;
17000         this.initPageY = p[1] - dy;
17001
17002         this.lastPageX = p[0];
17003         this.lastPageY = p[1];
17004
17005
17006         this.setStartPosition(p);
17007     },
17008
17009     /**
17010      * Sets the start position of the element.  This is set when the obj
17011      * is initialized, the reset when a drag is started.
17012      * @method setStartPosition
17013      * @param pos current position (from previous lookup)
17014      * @private
17015      */
17016     setStartPosition: function(pos) {
17017         var p = pos || Dom.getXY( this.getEl() );
17018         this.deltaSetXY = null;
17019
17020         this.startPageX = p[0];
17021         this.startPageY = p[1];
17022     },
17023
17024     /**
17025      * Add this instance to a group of related drag/drop objects.  All
17026      * instances belong to at least one group, and can belong to as many
17027      * groups as needed.
17028      * @method addToGroup
17029      * @param sGroup {string} the name of the group
17030      */
17031     addToGroup: function(sGroup) {
17032         this.groups[sGroup] = true;
17033         this.DDM.regDragDrop(this, sGroup);
17034     },
17035
17036     /**
17037      * Remove's this instance from the supplied interaction group
17038      * @method removeFromGroup
17039      * @param {string}  sGroup  The group to drop
17040      */
17041     removeFromGroup: function(sGroup) {
17042         if (this.groups[sGroup]) {
17043             delete this.groups[sGroup];
17044         }
17045
17046         this.DDM.removeDDFromGroup(this, sGroup);
17047     },
17048
17049     /**
17050      * Allows you to specify that an element other than the linked element
17051      * will be moved with the cursor during a drag
17052      * @method setDragElId
17053      * @param id {string} the id of the element that will be used to initiate the drag
17054      */
17055     setDragElId: function(id) {
17056         this.dragElId = id;
17057     },
17058
17059     /**
17060      * Allows you to specify a child of the linked element that should be
17061      * used to initiate the drag operation.  An example of this would be if
17062      * you have a content div with text and links.  Clicking anywhere in the
17063      * content area would normally start the drag operation.  Use this method
17064      * to specify that an element inside of the content div is the element
17065      * that starts the drag operation.
17066      * @method setHandleElId
17067      * @param id {string} the id of the element that will be used to
17068      * initiate the drag.
17069      */
17070     setHandleElId: function(id) {
17071         if (typeof id !== "string") {
17072             id = Roo.id(id);
17073         }
17074         this.handleElId = id;
17075         this.DDM.regHandle(this.id, id);
17076     },
17077
17078     /**
17079      * Allows you to set an element outside of the linked element as a drag
17080      * handle
17081      * @method setOuterHandleElId
17082      * @param id the id of the element that will be used to initiate the drag
17083      */
17084     setOuterHandleElId: function(id) {
17085         if (typeof id !== "string") {
17086             id = Roo.id(id);
17087         }
17088         Event.on(id, "mousedown",
17089                 this.handleMouseDown, this);
17090         this.setHandleElId(id);
17091
17092         this.hasOuterHandles = true;
17093     },
17094
17095     /**
17096      * Remove all drag and drop hooks for this element
17097      * @method unreg
17098      */
17099     unreg: function() {
17100         Event.un(this.id, "mousedown",
17101                 this.handleMouseDown);
17102         Event.un(this.id, "touchstart",
17103                 this.handleMouseDown);
17104         this._domRef = null;
17105         this.DDM._remove(this);
17106     },
17107
17108     destroy : function(){
17109         this.unreg();
17110     },
17111
17112     /**
17113      * Returns true if this instance is locked, or the drag drop mgr is locked
17114      * (meaning that all drag/drop is disabled on the page.)
17115      * @method isLocked
17116      * @return {boolean} true if this obj or all drag/drop is locked, else
17117      * false
17118      */
17119     isLocked: function() {
17120         return (this.DDM.isLocked() || this.locked);
17121     },
17122
17123     /**
17124      * Fired when this object is clicked
17125      * @method handleMouseDown
17126      * @param {Event} e
17127      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17128      * @private
17129      */
17130     handleMouseDown: function(e, oDD){
17131      
17132         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17133             //Roo.log('not touch/ button !=0');
17134             return;
17135         }
17136         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17137             return; // double touch..
17138         }
17139         
17140
17141         if (this.isLocked()) {
17142             //Roo.log('locked');
17143             return;
17144         }
17145
17146         this.DDM.refreshCache(this.groups);
17147 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17148         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17149         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17150             //Roo.log('no outer handes or not over target');
17151                 // do nothing.
17152         } else {
17153 //            Roo.log('check validator');
17154             if (this.clickValidator(e)) {
17155 //                Roo.log('validate success');
17156                 // set the initial element position
17157                 this.setStartPosition();
17158
17159
17160                 this.b4MouseDown(e);
17161                 this.onMouseDown(e);
17162
17163                 this.DDM.handleMouseDown(e, this);
17164
17165                 this.DDM.stopEvent(e);
17166             } else {
17167
17168
17169             }
17170         }
17171     },
17172
17173     clickValidator: function(e) {
17174         var target = e.getTarget();
17175         return ( this.isValidHandleChild(target) &&
17176                     (this.id == this.handleElId ||
17177                         this.DDM.handleWasClicked(target, this.id)) );
17178     },
17179
17180     /**
17181      * Allows you to specify a tag name that should not start a drag operation
17182      * when clicked.  This is designed to facilitate embedding links within a
17183      * drag handle that do something other than start the drag.
17184      * @method addInvalidHandleType
17185      * @param {string} tagName the type of element to exclude
17186      */
17187     addInvalidHandleType: function(tagName) {
17188         var type = tagName.toUpperCase();
17189         this.invalidHandleTypes[type] = type;
17190     },
17191
17192     /**
17193      * Lets you to specify an element id for a child of a drag handle
17194      * that should not initiate a drag
17195      * @method addInvalidHandleId
17196      * @param {string} id the element id of the element you wish to ignore
17197      */
17198     addInvalidHandleId: function(id) {
17199         if (typeof id !== "string") {
17200             id = Roo.id(id);
17201         }
17202         this.invalidHandleIds[id] = id;
17203     },
17204
17205     /**
17206      * Lets you specify a css class of elements that will not initiate a drag
17207      * @method addInvalidHandleClass
17208      * @param {string} cssClass the class of the elements you wish to ignore
17209      */
17210     addInvalidHandleClass: function(cssClass) {
17211         this.invalidHandleClasses.push(cssClass);
17212     },
17213
17214     /**
17215      * Unsets an excluded tag name set by addInvalidHandleType
17216      * @method removeInvalidHandleType
17217      * @param {string} tagName the type of element to unexclude
17218      */
17219     removeInvalidHandleType: function(tagName) {
17220         var type = tagName.toUpperCase();
17221         // this.invalidHandleTypes[type] = null;
17222         delete this.invalidHandleTypes[type];
17223     },
17224
17225     /**
17226      * Unsets an invalid handle id
17227      * @method removeInvalidHandleId
17228      * @param {string} id the id of the element to re-enable
17229      */
17230     removeInvalidHandleId: function(id) {
17231         if (typeof id !== "string") {
17232             id = Roo.id(id);
17233         }
17234         delete this.invalidHandleIds[id];
17235     },
17236
17237     /**
17238      * Unsets an invalid css class
17239      * @method removeInvalidHandleClass
17240      * @param {string} cssClass the class of the element(s) you wish to
17241      * re-enable
17242      */
17243     removeInvalidHandleClass: function(cssClass) {
17244         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17245             if (this.invalidHandleClasses[i] == cssClass) {
17246                 delete this.invalidHandleClasses[i];
17247             }
17248         }
17249     },
17250
17251     /**
17252      * Checks the tag exclusion list to see if this click should be ignored
17253      * @method isValidHandleChild
17254      * @param {HTMLElement} node the HTMLElement to evaluate
17255      * @return {boolean} true if this is a valid tag type, false if not
17256      */
17257     isValidHandleChild: function(node) {
17258
17259         var valid = true;
17260         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17261         var nodeName;
17262         try {
17263             nodeName = node.nodeName.toUpperCase();
17264         } catch(e) {
17265             nodeName = node.nodeName;
17266         }
17267         valid = valid && !this.invalidHandleTypes[nodeName];
17268         valid = valid && !this.invalidHandleIds[node.id];
17269
17270         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17271             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17272         }
17273
17274
17275         return valid;
17276
17277     },
17278
17279     /**
17280      * Create the array of horizontal tick marks if an interval was specified
17281      * in setXConstraint().
17282      * @method setXTicks
17283      * @private
17284      */
17285     setXTicks: function(iStartX, iTickSize) {
17286         this.xTicks = [];
17287         this.xTickSize = iTickSize;
17288
17289         var tickMap = {};
17290
17291         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17292             if (!tickMap[i]) {
17293                 this.xTicks[this.xTicks.length] = i;
17294                 tickMap[i] = true;
17295             }
17296         }
17297
17298         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17299             if (!tickMap[i]) {
17300                 this.xTicks[this.xTicks.length] = i;
17301                 tickMap[i] = true;
17302             }
17303         }
17304
17305         this.xTicks.sort(this.DDM.numericSort) ;
17306     },
17307
17308     /**
17309      * Create the array of vertical tick marks if an interval was specified in
17310      * setYConstraint().
17311      * @method setYTicks
17312      * @private
17313      */
17314     setYTicks: function(iStartY, iTickSize) {
17315         this.yTicks = [];
17316         this.yTickSize = iTickSize;
17317
17318         var tickMap = {};
17319
17320         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17321             if (!tickMap[i]) {
17322                 this.yTicks[this.yTicks.length] = i;
17323                 tickMap[i] = true;
17324             }
17325         }
17326
17327         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17328             if (!tickMap[i]) {
17329                 this.yTicks[this.yTicks.length] = i;
17330                 tickMap[i] = true;
17331             }
17332         }
17333
17334         this.yTicks.sort(this.DDM.numericSort) ;
17335     },
17336
17337     /**
17338      * By default, the element can be dragged any place on the screen.  Use
17339      * this method to limit the horizontal travel of the element.  Pass in
17340      * 0,0 for the parameters if you want to lock the drag to the y axis.
17341      * @method setXConstraint
17342      * @param {int} iLeft the number of pixels the element can move to the left
17343      * @param {int} iRight the number of pixels the element can move to the
17344      * right
17345      * @param {int} iTickSize optional parameter for specifying that the
17346      * element
17347      * should move iTickSize pixels at a time.
17348      */
17349     setXConstraint: function(iLeft, iRight, iTickSize) {
17350         this.leftConstraint = iLeft;
17351         this.rightConstraint = iRight;
17352
17353         this.minX = this.initPageX - iLeft;
17354         this.maxX = this.initPageX + iRight;
17355         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17356
17357         this.constrainX = true;
17358     },
17359
17360     /**
17361      * Clears any constraints applied to this instance.  Also clears ticks
17362      * since they can't exist independent of a constraint at this time.
17363      * @method clearConstraints
17364      */
17365     clearConstraints: function() {
17366         this.constrainX = false;
17367         this.constrainY = false;
17368         this.clearTicks();
17369     },
17370
17371     /**
17372      * Clears any tick interval defined for this instance
17373      * @method clearTicks
17374      */
17375     clearTicks: function() {
17376         this.xTicks = null;
17377         this.yTicks = null;
17378         this.xTickSize = 0;
17379         this.yTickSize = 0;
17380     },
17381
17382     /**
17383      * By default, the element can be dragged any place on the screen.  Set
17384      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17385      * parameters if you want to lock the drag to the x axis.
17386      * @method setYConstraint
17387      * @param {int} iUp the number of pixels the element can move up
17388      * @param {int} iDown the number of pixels the element can move down
17389      * @param {int} iTickSize optional parameter for specifying that the
17390      * element should move iTickSize pixels at a time.
17391      */
17392     setYConstraint: function(iUp, iDown, iTickSize) {
17393         this.topConstraint = iUp;
17394         this.bottomConstraint = iDown;
17395
17396         this.minY = this.initPageY - iUp;
17397         this.maxY = this.initPageY + iDown;
17398         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17399
17400         this.constrainY = true;
17401
17402     },
17403
17404     /**
17405      * resetConstraints must be called if you manually reposition a dd element.
17406      * @method resetConstraints
17407      * @param {boolean} maintainOffset
17408      */
17409     resetConstraints: function() {
17410
17411
17412         // Maintain offsets if necessary
17413         if (this.initPageX || this.initPageX === 0) {
17414             // figure out how much this thing has moved
17415             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17416             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17417
17418             this.setInitPosition(dx, dy);
17419
17420         // This is the first time we have detected the element's position
17421         } else {
17422             this.setInitPosition();
17423         }
17424
17425         if (this.constrainX) {
17426             this.setXConstraint( this.leftConstraint,
17427                                  this.rightConstraint,
17428                                  this.xTickSize        );
17429         }
17430
17431         if (this.constrainY) {
17432             this.setYConstraint( this.topConstraint,
17433                                  this.bottomConstraint,
17434                                  this.yTickSize         );
17435         }
17436     },
17437
17438     /**
17439      * Normally the drag element is moved pixel by pixel, but we can specify
17440      * that it move a number of pixels at a time.  This method resolves the
17441      * location when we have it set up like this.
17442      * @method getTick
17443      * @param {int} val where we want to place the object
17444      * @param {int[]} tickArray sorted array of valid points
17445      * @return {int} the closest tick
17446      * @private
17447      */
17448     getTick: function(val, tickArray) {
17449
17450         if (!tickArray) {
17451             // If tick interval is not defined, it is effectively 1 pixel,
17452             // so we return the value passed to us.
17453             return val;
17454         } else if (tickArray[0] >= val) {
17455             // The value is lower than the first tick, so we return the first
17456             // tick.
17457             return tickArray[0];
17458         } else {
17459             for (var i=0, len=tickArray.length; i<len; ++i) {
17460                 var next = i + 1;
17461                 if (tickArray[next] && tickArray[next] >= val) {
17462                     var diff1 = val - tickArray[i];
17463                     var diff2 = tickArray[next] - val;
17464                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17465                 }
17466             }
17467
17468             // The value is larger than the last tick, so we return the last
17469             // tick.
17470             return tickArray[tickArray.length - 1];
17471         }
17472     },
17473
17474     /**
17475      * toString method
17476      * @method toString
17477      * @return {string} string representation of the dd obj
17478      */
17479     toString: function() {
17480         return ("DragDrop " + this.id);
17481     }
17482
17483 });
17484
17485 })();
17486 /*
17487  * Based on:
17488  * Ext JS Library 1.1.1
17489  * Copyright(c) 2006-2007, Ext JS, LLC.
17490  *
17491  * Originally Released Under LGPL - original licence link has changed is not relivant.
17492  *
17493  * Fork - LGPL
17494  * <script type="text/javascript">
17495  */
17496
17497
17498 /**
17499  * The drag and drop utility provides a framework for building drag and drop
17500  * applications.  In addition to enabling drag and drop for specific elements,
17501  * the drag and drop elements are tracked by the manager class, and the
17502  * interactions between the various elements are tracked during the drag and
17503  * the implementing code is notified about these important moments.
17504  */
17505
17506 // Only load the library once.  Rewriting the manager class would orphan
17507 // existing drag and drop instances.
17508 if (!Roo.dd.DragDropMgr) {
17509
17510 /**
17511  * @class Roo.dd.DragDropMgr
17512  * DragDropMgr is a singleton that tracks the element interaction for
17513  * all DragDrop items in the window.  Generally, you will not call
17514  * this class directly, but it does have helper methods that could
17515  * be useful in your DragDrop implementations.
17516  * @singleton
17517  */
17518 Roo.dd.DragDropMgr = function() {
17519
17520     var Event = Roo.EventManager;
17521
17522     return {
17523
17524         /**
17525          * Two dimensional Array of registered DragDrop objects.  The first
17526          * dimension is the DragDrop item group, the second the DragDrop
17527          * object.
17528          * @property ids
17529          * @type {string: string}
17530          * @private
17531          * @static
17532          */
17533         ids: {},
17534
17535         /**
17536          * Array of element ids defined as drag handles.  Used to determine
17537          * if the element that generated the mousedown event is actually the
17538          * handle and not the html element itself.
17539          * @property handleIds
17540          * @type {string: string}
17541          * @private
17542          * @static
17543          */
17544         handleIds: {},
17545
17546         /**
17547          * the DragDrop object that is currently being dragged
17548          * @property dragCurrent
17549          * @type DragDrop
17550          * @private
17551          * @static
17552          **/
17553         dragCurrent: null,
17554
17555         /**
17556          * the DragDrop object(s) that are being hovered over
17557          * @property dragOvers
17558          * @type Array
17559          * @private
17560          * @static
17561          */
17562         dragOvers: {},
17563
17564         /**
17565          * the X distance between the cursor and the object being dragged
17566          * @property deltaX
17567          * @type int
17568          * @private
17569          * @static
17570          */
17571         deltaX: 0,
17572
17573         /**
17574          * the Y distance between the cursor and the object being dragged
17575          * @property deltaY
17576          * @type int
17577          * @private
17578          * @static
17579          */
17580         deltaY: 0,
17581
17582         /**
17583          * Flag to determine if we should prevent the default behavior of the
17584          * events we define. By default this is true, but this can be set to
17585          * false if you need the default behavior (not recommended)
17586          * @property preventDefault
17587          * @type boolean
17588          * @static
17589          */
17590         preventDefault: true,
17591
17592         /**
17593          * Flag to determine if we should stop the propagation of the events
17594          * we generate. This is true by default but you may want to set it to
17595          * false if the html element contains other features that require the
17596          * mouse click.
17597          * @property stopPropagation
17598          * @type boolean
17599          * @static
17600          */
17601         stopPropagation: true,
17602
17603         /**
17604          * Internal flag that is set to true when drag and drop has been
17605          * intialized
17606          * @property initialized
17607          * @private
17608          * @static
17609          */
17610         initalized: false,
17611
17612         /**
17613          * All drag and drop can be disabled.
17614          * @property locked
17615          * @private
17616          * @static
17617          */
17618         locked: false,
17619
17620         /**
17621          * Called the first time an element is registered.
17622          * @method init
17623          * @private
17624          * @static
17625          */
17626         init: function() {
17627             this.initialized = true;
17628         },
17629
17630         /**
17631          * In point mode, drag and drop interaction is defined by the
17632          * location of the cursor during the drag/drop
17633          * @property POINT
17634          * @type int
17635          * @static
17636          */
17637         POINT: 0,
17638
17639         /**
17640          * In intersect mode, drag and drop interactio nis defined by the
17641          * overlap of two or more drag and drop objects.
17642          * @property INTERSECT
17643          * @type int
17644          * @static
17645          */
17646         INTERSECT: 1,
17647
17648         /**
17649          * The current drag and drop mode.  Default: POINT
17650          * @property mode
17651          * @type int
17652          * @static
17653          */
17654         mode: 0,
17655
17656         /**
17657          * Runs method on all drag and drop objects
17658          * @method _execOnAll
17659          * @private
17660          * @static
17661          */
17662         _execOnAll: function(sMethod, args) {
17663             for (var i in this.ids) {
17664                 for (var j in this.ids[i]) {
17665                     var oDD = this.ids[i][j];
17666                     if (! this.isTypeOfDD(oDD)) {
17667                         continue;
17668                     }
17669                     oDD[sMethod].apply(oDD, args);
17670                 }
17671             }
17672         },
17673
17674         /**
17675          * Drag and drop initialization.  Sets up the global event handlers
17676          * @method _onLoad
17677          * @private
17678          * @static
17679          */
17680         _onLoad: function() {
17681
17682             this.init();
17683
17684             if (!Roo.isTouch) {
17685                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17686                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17687             }
17688             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17689             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17690             
17691             Event.on(window,   "unload",    this._onUnload, this, true);
17692             Event.on(window,   "resize",    this._onResize, this, true);
17693             // Event.on(window,   "mouseout",    this._test);
17694
17695         },
17696
17697         /**
17698          * Reset constraints on all drag and drop objs
17699          * @method _onResize
17700          * @private
17701          * @static
17702          */
17703         _onResize: function(e) {
17704             this._execOnAll("resetConstraints", []);
17705         },
17706
17707         /**
17708          * Lock all drag and drop functionality
17709          * @method lock
17710          * @static
17711          */
17712         lock: function() { this.locked = true; },
17713
17714         /**
17715          * Unlock all drag and drop functionality
17716          * @method unlock
17717          * @static
17718          */
17719         unlock: function() { this.locked = false; },
17720
17721         /**
17722          * Is drag and drop locked?
17723          * @method isLocked
17724          * @return {boolean} True if drag and drop is locked, false otherwise.
17725          * @static
17726          */
17727         isLocked: function() { return this.locked; },
17728
17729         /**
17730          * Location cache that is set for all drag drop objects when a drag is
17731          * initiated, cleared when the drag is finished.
17732          * @property locationCache
17733          * @private
17734          * @static
17735          */
17736         locationCache: {},
17737
17738         /**
17739          * Set useCache to false if you want to force object the lookup of each
17740          * drag and drop linked element constantly during a drag.
17741          * @property useCache
17742          * @type boolean
17743          * @static
17744          */
17745         useCache: true,
17746
17747         /**
17748          * The number of pixels that the mouse needs to move after the
17749          * mousedown before the drag is initiated.  Default=3;
17750          * @property clickPixelThresh
17751          * @type int
17752          * @static
17753          */
17754         clickPixelThresh: 3,
17755
17756         /**
17757          * The number of milliseconds after the mousedown event to initiate the
17758          * drag if we don't get a mouseup event. Default=1000
17759          * @property clickTimeThresh
17760          * @type int
17761          * @static
17762          */
17763         clickTimeThresh: 350,
17764
17765         /**
17766          * Flag that indicates that either the drag pixel threshold or the
17767          * mousdown time threshold has been met
17768          * @property dragThreshMet
17769          * @type boolean
17770          * @private
17771          * @static
17772          */
17773         dragThreshMet: false,
17774
17775         /**
17776          * Timeout used for the click time threshold
17777          * @property clickTimeout
17778          * @type Object
17779          * @private
17780          * @static
17781          */
17782         clickTimeout: null,
17783
17784         /**
17785          * The X position of the mousedown event stored for later use when a
17786          * drag threshold is met.
17787          * @property startX
17788          * @type int
17789          * @private
17790          * @static
17791          */
17792         startX: 0,
17793
17794         /**
17795          * The Y position of the mousedown event stored for later use when a
17796          * drag threshold is met.
17797          * @property startY
17798          * @type int
17799          * @private
17800          * @static
17801          */
17802         startY: 0,
17803
17804         /**
17805          * Each DragDrop instance must be registered with the DragDropMgr.
17806          * This is executed in DragDrop.init()
17807          * @method regDragDrop
17808          * @param {DragDrop} oDD the DragDrop object to register
17809          * @param {String} sGroup the name of the group this element belongs to
17810          * @static
17811          */
17812         regDragDrop: function(oDD, sGroup) {
17813             if (!this.initialized) { this.init(); }
17814
17815             if (!this.ids[sGroup]) {
17816                 this.ids[sGroup] = {};
17817             }
17818             this.ids[sGroup][oDD.id] = oDD;
17819         },
17820
17821         /**
17822          * Removes the supplied dd instance from the supplied group. Executed
17823          * by DragDrop.removeFromGroup, so don't call this function directly.
17824          * @method removeDDFromGroup
17825          * @private
17826          * @static
17827          */
17828         removeDDFromGroup: function(oDD, sGroup) {
17829             if (!this.ids[sGroup]) {
17830                 this.ids[sGroup] = {};
17831             }
17832
17833             var obj = this.ids[sGroup];
17834             if (obj && obj[oDD.id]) {
17835                 delete obj[oDD.id];
17836             }
17837         },
17838
17839         /**
17840          * Unregisters a drag and drop item.  This is executed in
17841          * DragDrop.unreg, use that method instead of calling this directly.
17842          * @method _remove
17843          * @private
17844          * @static
17845          */
17846         _remove: function(oDD) {
17847             for (var g in oDD.groups) {
17848                 if (g && this.ids[g][oDD.id]) {
17849                     delete this.ids[g][oDD.id];
17850                 }
17851             }
17852             delete this.handleIds[oDD.id];
17853         },
17854
17855         /**
17856          * Each DragDrop handle element must be registered.  This is done
17857          * automatically when executing DragDrop.setHandleElId()
17858          * @method regHandle
17859          * @param {String} sDDId the DragDrop id this element is a handle for
17860          * @param {String} sHandleId the id of the element that is the drag
17861          * handle
17862          * @static
17863          */
17864         regHandle: function(sDDId, sHandleId) {
17865             if (!this.handleIds[sDDId]) {
17866                 this.handleIds[sDDId] = {};
17867             }
17868             this.handleIds[sDDId][sHandleId] = sHandleId;
17869         },
17870
17871         /**
17872          * Utility function to determine if a given element has been
17873          * registered as a drag drop item.
17874          * @method isDragDrop
17875          * @param {String} id the element id to check
17876          * @return {boolean} true if this element is a DragDrop item,
17877          * false otherwise
17878          * @static
17879          */
17880         isDragDrop: function(id) {
17881             return ( this.getDDById(id) ) ? true : false;
17882         },
17883
17884         /**
17885          * Returns the drag and drop instances that are in all groups the
17886          * passed in instance belongs to.
17887          * @method getRelated
17888          * @param {DragDrop} p_oDD the obj to get related data for
17889          * @param {boolean} bTargetsOnly if true, only return targetable objs
17890          * @return {DragDrop[]} the related instances
17891          * @static
17892          */
17893         getRelated: function(p_oDD, bTargetsOnly) {
17894             var oDDs = [];
17895             for (var i in p_oDD.groups) {
17896                 for (j in this.ids[i]) {
17897                     var dd = this.ids[i][j];
17898                     if (! this.isTypeOfDD(dd)) {
17899                         continue;
17900                     }
17901                     if (!bTargetsOnly || dd.isTarget) {
17902                         oDDs[oDDs.length] = dd;
17903                     }
17904                 }
17905             }
17906
17907             return oDDs;
17908         },
17909
17910         /**
17911          * Returns true if the specified dd target is a legal target for
17912          * the specifice drag obj
17913          * @method isLegalTarget
17914          * @param {DragDrop} the drag obj
17915          * @param {DragDrop} the target
17916          * @return {boolean} true if the target is a legal target for the
17917          * dd obj
17918          * @static
17919          */
17920         isLegalTarget: function (oDD, oTargetDD) {
17921             var targets = this.getRelated(oDD, true);
17922             for (var i=0, len=targets.length;i<len;++i) {
17923                 if (targets[i].id == oTargetDD.id) {
17924                     return true;
17925                 }
17926             }
17927
17928             return false;
17929         },
17930
17931         /**
17932          * My goal is to be able to transparently determine if an object is
17933          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17934          * returns "object", oDD.constructor.toString() always returns
17935          * "DragDrop" and not the name of the subclass.  So for now it just
17936          * evaluates a well-known variable in DragDrop.
17937          * @method isTypeOfDD
17938          * @param {Object} the object to evaluate
17939          * @return {boolean} true if typeof oDD = DragDrop
17940          * @static
17941          */
17942         isTypeOfDD: function (oDD) {
17943             return (oDD && oDD.__ygDragDrop);
17944         },
17945
17946         /**
17947          * Utility function to determine if a given element has been
17948          * registered as a drag drop handle for the given Drag Drop object.
17949          * @method isHandle
17950          * @param {String} id the element id to check
17951          * @return {boolean} true if this element is a DragDrop handle, false
17952          * otherwise
17953          * @static
17954          */
17955         isHandle: function(sDDId, sHandleId) {
17956             return ( this.handleIds[sDDId] &&
17957                             this.handleIds[sDDId][sHandleId] );
17958         },
17959
17960         /**
17961          * Returns the DragDrop instance for a given id
17962          * @method getDDById
17963          * @param {String} id the id of the DragDrop object
17964          * @return {DragDrop} the drag drop object, null if it is not found
17965          * @static
17966          */
17967         getDDById: function(id) {
17968             for (var i in this.ids) {
17969                 if (this.ids[i][id]) {
17970                     return this.ids[i][id];
17971                 }
17972             }
17973             return null;
17974         },
17975
17976         /**
17977          * Fired after a registered DragDrop object gets the mousedown event.
17978          * Sets up the events required to track the object being dragged
17979          * @method handleMouseDown
17980          * @param {Event} e the event
17981          * @param oDD the DragDrop object being dragged
17982          * @private
17983          * @static
17984          */
17985         handleMouseDown: function(e, oDD) {
17986             if(Roo.QuickTips){
17987                 Roo.QuickTips.disable();
17988             }
17989             this.currentTarget = e.getTarget();
17990
17991             this.dragCurrent = oDD;
17992
17993             var el = oDD.getEl();
17994
17995             // track start position
17996             this.startX = e.getPageX();
17997             this.startY = e.getPageY();
17998
17999             this.deltaX = this.startX - el.offsetLeft;
18000             this.deltaY = this.startY - el.offsetTop;
18001
18002             this.dragThreshMet = false;
18003
18004             this.clickTimeout = setTimeout(
18005                     function() {
18006                         var DDM = Roo.dd.DDM;
18007                         DDM.startDrag(DDM.startX, DDM.startY);
18008                     },
18009                     this.clickTimeThresh );
18010         },
18011
18012         /**
18013          * Fired when either the drag pixel threshol or the mousedown hold
18014          * time threshold has been met.
18015          * @method startDrag
18016          * @param x {int} the X position of the original mousedown
18017          * @param y {int} the Y position of the original mousedown
18018          * @static
18019          */
18020         startDrag: function(x, y) {
18021             clearTimeout(this.clickTimeout);
18022             if (this.dragCurrent) {
18023                 this.dragCurrent.b4StartDrag(x, y);
18024                 this.dragCurrent.startDrag(x, y);
18025             }
18026             this.dragThreshMet = true;
18027         },
18028
18029         /**
18030          * Internal function to handle the mouseup event.  Will be invoked
18031          * from the context of the document.
18032          * @method handleMouseUp
18033          * @param {Event} e the event
18034          * @private
18035          * @static
18036          */
18037         handleMouseUp: function(e) {
18038
18039             if(Roo.QuickTips){
18040                 Roo.QuickTips.enable();
18041             }
18042             if (! this.dragCurrent) {
18043                 return;
18044             }
18045
18046             clearTimeout(this.clickTimeout);
18047
18048             if (this.dragThreshMet) {
18049                 this.fireEvents(e, true);
18050             } else {
18051             }
18052
18053             this.stopDrag(e);
18054
18055             this.stopEvent(e);
18056         },
18057
18058         /**
18059          * Utility to stop event propagation and event default, if these
18060          * features are turned on.
18061          * @method stopEvent
18062          * @param {Event} e the event as returned by this.getEvent()
18063          * @static
18064          */
18065         stopEvent: function(e){
18066             if(this.stopPropagation) {
18067                 e.stopPropagation();
18068             }
18069
18070             if (this.preventDefault) {
18071                 e.preventDefault();
18072             }
18073         },
18074
18075         /**
18076          * Internal function to clean up event handlers after the drag
18077          * operation is complete
18078          * @method stopDrag
18079          * @param {Event} e the event
18080          * @private
18081          * @static
18082          */
18083         stopDrag: function(e) {
18084             // Fire the drag end event for the item that was dragged
18085             if (this.dragCurrent) {
18086                 if (this.dragThreshMet) {
18087                     this.dragCurrent.b4EndDrag(e);
18088                     this.dragCurrent.endDrag(e);
18089                 }
18090
18091                 this.dragCurrent.onMouseUp(e);
18092             }
18093
18094             this.dragCurrent = null;
18095             this.dragOvers = {};
18096         },
18097
18098         /**
18099          * Internal function to handle the mousemove event.  Will be invoked
18100          * from the context of the html element.
18101          *
18102          * @TODO figure out what we can do about mouse events lost when the
18103          * user drags objects beyond the window boundary.  Currently we can
18104          * detect this in internet explorer by verifying that the mouse is
18105          * down during the mousemove event.  Firefox doesn't give us the
18106          * button state on the mousemove event.
18107          * @method handleMouseMove
18108          * @param {Event} e the event
18109          * @private
18110          * @static
18111          */
18112         handleMouseMove: function(e) {
18113             if (! this.dragCurrent) {
18114                 return true;
18115             }
18116
18117             // var button = e.which || e.button;
18118
18119             // check for IE mouseup outside of page boundary
18120             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18121                 this.stopEvent(e);
18122                 return this.handleMouseUp(e);
18123             }
18124
18125             if (!this.dragThreshMet) {
18126                 var diffX = Math.abs(this.startX - e.getPageX());
18127                 var diffY = Math.abs(this.startY - e.getPageY());
18128                 if (diffX > this.clickPixelThresh ||
18129                             diffY > this.clickPixelThresh) {
18130                     this.startDrag(this.startX, this.startY);
18131                 }
18132             }
18133
18134             if (this.dragThreshMet) {
18135                 this.dragCurrent.b4Drag(e);
18136                 this.dragCurrent.onDrag(e);
18137                 if(!this.dragCurrent.moveOnly){
18138                     this.fireEvents(e, false);
18139                 }
18140             }
18141
18142             this.stopEvent(e);
18143
18144             return true;
18145         },
18146
18147         /**
18148          * Iterates over all of the DragDrop elements to find ones we are
18149          * hovering over or dropping on
18150          * @method fireEvents
18151          * @param {Event} e the event
18152          * @param {boolean} isDrop is this a drop op or a mouseover op?
18153          * @private
18154          * @static
18155          */
18156         fireEvents: function(e, isDrop) {
18157             var dc = this.dragCurrent;
18158
18159             // If the user did the mouse up outside of the window, we could
18160             // get here even though we have ended the drag.
18161             if (!dc || dc.isLocked()) {
18162                 return;
18163             }
18164
18165             var pt = e.getPoint();
18166
18167             // cache the previous dragOver array
18168             var oldOvers = [];
18169
18170             var outEvts   = [];
18171             var overEvts  = [];
18172             var dropEvts  = [];
18173             var enterEvts = [];
18174
18175             // Check to see if the object(s) we were hovering over is no longer
18176             // being hovered over so we can fire the onDragOut event
18177             for (var i in this.dragOvers) {
18178
18179                 var ddo = this.dragOvers[i];
18180
18181                 if (! this.isTypeOfDD(ddo)) {
18182                     continue;
18183                 }
18184
18185                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18186                     outEvts.push( ddo );
18187                 }
18188
18189                 oldOvers[i] = true;
18190                 delete this.dragOvers[i];
18191             }
18192
18193             for (var sGroup in dc.groups) {
18194
18195                 if ("string" != typeof sGroup) {
18196                     continue;
18197                 }
18198
18199                 for (i in this.ids[sGroup]) {
18200                     var oDD = this.ids[sGroup][i];
18201                     if (! this.isTypeOfDD(oDD)) {
18202                         continue;
18203                     }
18204
18205                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18206                         if (this.isOverTarget(pt, oDD, this.mode)) {
18207                             // look for drop interactions
18208                             if (isDrop) {
18209                                 dropEvts.push( oDD );
18210                             // look for drag enter and drag over interactions
18211                             } else {
18212
18213                                 // initial drag over: dragEnter fires
18214                                 if (!oldOvers[oDD.id]) {
18215                                     enterEvts.push( oDD );
18216                                 // subsequent drag overs: dragOver fires
18217                                 } else {
18218                                     overEvts.push( oDD );
18219                                 }
18220
18221                                 this.dragOvers[oDD.id] = oDD;
18222                             }
18223                         }
18224                     }
18225                 }
18226             }
18227
18228             if (this.mode) {
18229                 if (outEvts.length) {
18230                     dc.b4DragOut(e, outEvts);
18231                     dc.onDragOut(e, outEvts);
18232                 }
18233
18234                 if (enterEvts.length) {
18235                     dc.onDragEnter(e, enterEvts);
18236                 }
18237
18238                 if (overEvts.length) {
18239                     dc.b4DragOver(e, overEvts);
18240                     dc.onDragOver(e, overEvts);
18241                 }
18242
18243                 if (dropEvts.length) {
18244                     dc.b4DragDrop(e, dropEvts);
18245                     dc.onDragDrop(e, dropEvts);
18246                 }
18247
18248             } else {
18249                 // fire dragout events
18250                 var len = 0;
18251                 for (i=0, len=outEvts.length; i<len; ++i) {
18252                     dc.b4DragOut(e, outEvts[i].id);
18253                     dc.onDragOut(e, outEvts[i].id);
18254                 }
18255
18256                 // fire enter events
18257                 for (i=0,len=enterEvts.length; i<len; ++i) {
18258                     // dc.b4DragEnter(e, oDD.id);
18259                     dc.onDragEnter(e, enterEvts[i].id);
18260                 }
18261
18262                 // fire over events
18263                 for (i=0,len=overEvts.length; i<len; ++i) {
18264                     dc.b4DragOver(e, overEvts[i].id);
18265                     dc.onDragOver(e, overEvts[i].id);
18266                 }
18267
18268                 // fire drop events
18269                 for (i=0, len=dropEvts.length; i<len; ++i) {
18270                     dc.b4DragDrop(e, dropEvts[i].id);
18271                     dc.onDragDrop(e, dropEvts[i].id);
18272                 }
18273
18274             }
18275
18276             // notify about a drop that did not find a target
18277             if (isDrop && !dropEvts.length) {
18278                 dc.onInvalidDrop(e);
18279             }
18280
18281         },
18282
18283         /**
18284          * Helper function for getting the best match from the list of drag
18285          * and drop objects returned by the drag and drop events when we are
18286          * in INTERSECT mode.  It returns either the first object that the
18287          * cursor is over, or the object that has the greatest overlap with
18288          * the dragged element.
18289          * @method getBestMatch
18290          * @param  {DragDrop[]} dds The array of drag and drop objects
18291          * targeted
18292          * @return {DragDrop}       The best single match
18293          * @static
18294          */
18295         getBestMatch: function(dds) {
18296             var winner = null;
18297             // Return null if the input is not what we expect
18298             //if (!dds || !dds.length || dds.length == 0) {
18299                // winner = null;
18300             // If there is only one item, it wins
18301             //} else if (dds.length == 1) {
18302
18303             var len = dds.length;
18304
18305             if (len == 1) {
18306                 winner = dds[0];
18307             } else {
18308                 // Loop through the targeted items
18309                 for (var i=0; i<len; ++i) {
18310                     var dd = dds[i];
18311                     // If the cursor is over the object, it wins.  If the
18312                     // cursor is over multiple matches, the first one we come
18313                     // to wins.
18314                     if (dd.cursorIsOver) {
18315                         winner = dd;
18316                         break;
18317                     // Otherwise the object with the most overlap wins
18318                     } else {
18319                         if (!winner ||
18320                             winner.overlap.getArea() < dd.overlap.getArea()) {
18321                             winner = dd;
18322                         }
18323                     }
18324                 }
18325             }
18326
18327             return winner;
18328         },
18329
18330         /**
18331          * Refreshes the cache of the top-left and bottom-right points of the
18332          * drag and drop objects in the specified group(s).  This is in the
18333          * format that is stored in the drag and drop instance, so typical
18334          * usage is:
18335          * <code>
18336          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18337          * </code>
18338          * Alternatively:
18339          * <code>
18340          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18341          * </code>
18342          * @TODO this really should be an indexed array.  Alternatively this
18343          * method could accept both.
18344          * @method refreshCache
18345          * @param {Object} groups an associative array of groups to refresh
18346          * @static
18347          */
18348         refreshCache: function(groups) {
18349             for (var sGroup in groups) {
18350                 if ("string" != typeof sGroup) {
18351                     continue;
18352                 }
18353                 for (var i in this.ids[sGroup]) {
18354                     var oDD = this.ids[sGroup][i];
18355
18356                     if (this.isTypeOfDD(oDD)) {
18357                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18358                         var loc = this.getLocation(oDD);
18359                         if (loc) {
18360                             this.locationCache[oDD.id] = loc;
18361                         } else {
18362                             delete this.locationCache[oDD.id];
18363                             // this will unregister the drag and drop object if
18364                             // the element is not in a usable state
18365                             // oDD.unreg();
18366                         }
18367                     }
18368                 }
18369             }
18370         },
18371
18372         /**
18373          * This checks to make sure an element exists and is in the DOM.  The
18374          * main purpose is to handle cases where innerHTML is used to remove
18375          * drag and drop objects from the DOM.  IE provides an 'unspecified
18376          * error' when trying to access the offsetParent of such an element
18377          * @method verifyEl
18378          * @param {HTMLElement} el the element to check
18379          * @return {boolean} true if the element looks usable
18380          * @static
18381          */
18382         verifyEl: function(el) {
18383             if (el) {
18384                 var parent;
18385                 if(Roo.isIE){
18386                     try{
18387                         parent = el.offsetParent;
18388                     }catch(e){}
18389                 }else{
18390                     parent = el.offsetParent;
18391                 }
18392                 if (parent) {
18393                     return true;
18394                 }
18395             }
18396
18397             return false;
18398         },
18399
18400         /**
18401          * Returns a Region object containing the drag and drop element's position
18402          * and size, including the padding configured for it
18403          * @method getLocation
18404          * @param {DragDrop} oDD the drag and drop object to get the
18405          *                       location for
18406          * @return {Roo.lib.Region} a Region object representing the total area
18407          *                             the element occupies, including any padding
18408          *                             the instance is configured for.
18409          * @static
18410          */
18411         getLocation: function(oDD) {
18412             if (! this.isTypeOfDD(oDD)) {
18413                 return null;
18414             }
18415
18416             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18417
18418             try {
18419                 pos= Roo.lib.Dom.getXY(el);
18420             } catch (e) { }
18421
18422             if (!pos) {
18423                 return null;
18424             }
18425
18426             x1 = pos[0];
18427             x2 = x1 + el.offsetWidth;
18428             y1 = pos[1];
18429             y2 = y1 + el.offsetHeight;
18430
18431             t = y1 - oDD.padding[0];
18432             r = x2 + oDD.padding[1];
18433             b = y2 + oDD.padding[2];
18434             l = x1 - oDD.padding[3];
18435
18436             return new Roo.lib.Region( t, r, b, l );
18437         },
18438
18439         /**
18440          * Checks the cursor location to see if it over the target
18441          * @method isOverTarget
18442          * @param {Roo.lib.Point} pt The point to evaluate
18443          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18444          * @return {boolean} true if the mouse is over the target
18445          * @private
18446          * @static
18447          */
18448         isOverTarget: function(pt, oTarget, intersect) {
18449             // use cache if available
18450             var loc = this.locationCache[oTarget.id];
18451             if (!loc || !this.useCache) {
18452                 loc = this.getLocation(oTarget);
18453                 this.locationCache[oTarget.id] = loc;
18454
18455             }
18456
18457             if (!loc) {
18458                 return false;
18459             }
18460
18461             oTarget.cursorIsOver = loc.contains( pt );
18462
18463             // DragDrop is using this as a sanity check for the initial mousedown
18464             // in this case we are done.  In POINT mode, if the drag obj has no
18465             // contraints, we are also done. Otherwise we need to evaluate the
18466             // location of the target as related to the actual location of the
18467             // dragged element.
18468             var dc = this.dragCurrent;
18469             if (!dc || !dc.getTargetCoord ||
18470                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18471                 return oTarget.cursorIsOver;
18472             }
18473
18474             oTarget.overlap = null;
18475
18476             // Get the current location of the drag element, this is the
18477             // location of the mouse event less the delta that represents
18478             // where the original mousedown happened on the element.  We
18479             // need to consider constraints and ticks as well.
18480             var pos = dc.getTargetCoord(pt.x, pt.y);
18481
18482             var el = dc.getDragEl();
18483             var curRegion = new Roo.lib.Region( pos.y,
18484                                                    pos.x + el.offsetWidth,
18485                                                    pos.y + el.offsetHeight,
18486                                                    pos.x );
18487
18488             var overlap = curRegion.intersect(loc);
18489
18490             if (overlap) {
18491                 oTarget.overlap = overlap;
18492                 return (intersect) ? true : oTarget.cursorIsOver;
18493             } else {
18494                 return false;
18495             }
18496         },
18497
18498         /**
18499          * unload event handler
18500          * @method _onUnload
18501          * @private
18502          * @static
18503          */
18504         _onUnload: function(e, me) {
18505             Roo.dd.DragDropMgr.unregAll();
18506         },
18507
18508         /**
18509          * Cleans up the drag and drop events and objects.
18510          * @method unregAll
18511          * @private
18512          * @static
18513          */
18514         unregAll: function() {
18515
18516             if (this.dragCurrent) {
18517                 this.stopDrag();
18518                 this.dragCurrent = null;
18519             }
18520
18521             this._execOnAll("unreg", []);
18522
18523             for (i in this.elementCache) {
18524                 delete this.elementCache[i];
18525             }
18526
18527             this.elementCache = {};
18528             this.ids = {};
18529         },
18530
18531         /**
18532          * A cache of DOM elements
18533          * @property elementCache
18534          * @private
18535          * @static
18536          */
18537         elementCache: {},
18538
18539         /**
18540          * Get the wrapper for the DOM element specified
18541          * @method getElWrapper
18542          * @param {String} id the id of the element to get
18543          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18544          * @private
18545          * @deprecated This wrapper isn't that useful
18546          * @static
18547          */
18548         getElWrapper: function(id) {
18549             var oWrapper = this.elementCache[id];
18550             if (!oWrapper || !oWrapper.el) {
18551                 oWrapper = this.elementCache[id] =
18552                     new this.ElementWrapper(Roo.getDom(id));
18553             }
18554             return oWrapper;
18555         },
18556
18557         /**
18558          * Returns the actual DOM element
18559          * @method getElement
18560          * @param {String} id the id of the elment to get
18561          * @return {Object} The element
18562          * @deprecated use Roo.getDom instead
18563          * @static
18564          */
18565         getElement: function(id) {
18566             return Roo.getDom(id);
18567         },
18568
18569         /**
18570          * Returns the style property for the DOM element (i.e.,
18571          * document.getElById(id).style)
18572          * @method getCss
18573          * @param {String} id the id of the elment to get
18574          * @return {Object} The style property of the element
18575          * @deprecated use Roo.getDom instead
18576          * @static
18577          */
18578         getCss: function(id) {
18579             var el = Roo.getDom(id);
18580             return (el) ? el.style : null;
18581         },
18582
18583         /**
18584          * Inner class for cached elements
18585          * @class DragDropMgr.ElementWrapper
18586          * @for DragDropMgr
18587          * @private
18588          * @deprecated
18589          */
18590         ElementWrapper: function(el) {
18591                 /**
18592                  * The element
18593                  * @property el
18594                  */
18595                 this.el = el || null;
18596                 /**
18597                  * The element id
18598                  * @property id
18599                  */
18600                 this.id = this.el && el.id;
18601                 /**
18602                  * A reference to the style property
18603                  * @property css
18604                  */
18605                 this.css = this.el && el.style;
18606             },
18607
18608         /**
18609          * Returns the X position of an html element
18610          * @method getPosX
18611          * @param el the element for which to get the position
18612          * @return {int} the X coordinate
18613          * @for DragDropMgr
18614          * @deprecated use Roo.lib.Dom.getX instead
18615          * @static
18616          */
18617         getPosX: function(el) {
18618             return Roo.lib.Dom.getX(el);
18619         },
18620
18621         /**
18622          * Returns the Y position of an html element
18623          * @method getPosY
18624          * @param el the element for which to get the position
18625          * @return {int} the Y coordinate
18626          * @deprecated use Roo.lib.Dom.getY instead
18627          * @static
18628          */
18629         getPosY: function(el) {
18630             return Roo.lib.Dom.getY(el);
18631         },
18632
18633         /**
18634          * Swap two nodes.  In IE, we use the native method, for others we
18635          * emulate the IE behavior
18636          * @method swapNode
18637          * @param n1 the first node to swap
18638          * @param n2 the other node to swap
18639          * @static
18640          */
18641         swapNode: function(n1, n2) {
18642             if (n1.swapNode) {
18643                 n1.swapNode(n2);
18644             } else {
18645                 var p = n2.parentNode;
18646                 var s = n2.nextSibling;
18647
18648                 if (s == n1) {
18649                     p.insertBefore(n1, n2);
18650                 } else if (n2 == n1.nextSibling) {
18651                     p.insertBefore(n2, n1);
18652                 } else {
18653                     n1.parentNode.replaceChild(n2, n1);
18654                     p.insertBefore(n1, s);
18655                 }
18656             }
18657         },
18658
18659         /**
18660          * Returns the current scroll position
18661          * @method getScroll
18662          * @private
18663          * @static
18664          */
18665         getScroll: function () {
18666             var t, l, dde=document.documentElement, db=document.body;
18667             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18668                 t = dde.scrollTop;
18669                 l = dde.scrollLeft;
18670             } else if (db) {
18671                 t = db.scrollTop;
18672                 l = db.scrollLeft;
18673             } else {
18674
18675             }
18676             return { top: t, left: l };
18677         },
18678
18679         /**
18680          * Returns the specified element style property
18681          * @method getStyle
18682          * @param {HTMLElement} el          the element
18683          * @param {string}      styleProp   the style property
18684          * @return {string} The value of the style property
18685          * @deprecated use Roo.lib.Dom.getStyle
18686          * @static
18687          */
18688         getStyle: function(el, styleProp) {
18689             return Roo.fly(el).getStyle(styleProp);
18690         },
18691
18692         /**
18693          * Gets the scrollTop
18694          * @method getScrollTop
18695          * @return {int} the document's scrollTop
18696          * @static
18697          */
18698         getScrollTop: function () { return this.getScroll().top; },
18699
18700         /**
18701          * Gets the scrollLeft
18702          * @method getScrollLeft
18703          * @return {int} the document's scrollTop
18704          * @static
18705          */
18706         getScrollLeft: function () { return this.getScroll().left; },
18707
18708         /**
18709          * Sets the x/y position of an element to the location of the
18710          * target element.
18711          * @method moveToEl
18712          * @param {HTMLElement} moveEl      The element to move
18713          * @param {HTMLElement} targetEl    The position reference element
18714          * @static
18715          */
18716         moveToEl: function (moveEl, targetEl) {
18717             var aCoord = Roo.lib.Dom.getXY(targetEl);
18718             Roo.lib.Dom.setXY(moveEl, aCoord);
18719         },
18720
18721         /**
18722          * Numeric array sort function
18723          * @method numericSort
18724          * @static
18725          */
18726         numericSort: function(a, b) { return (a - b); },
18727
18728         /**
18729          * Internal counter
18730          * @property _timeoutCount
18731          * @private
18732          * @static
18733          */
18734         _timeoutCount: 0,
18735
18736         /**
18737          * Trying to make the load order less important.  Without this we get
18738          * an error if this file is loaded before the Event Utility.
18739          * @method _addListeners
18740          * @private
18741          * @static
18742          */
18743         _addListeners: function() {
18744             var DDM = Roo.dd.DDM;
18745             if ( Roo.lib.Event && document ) {
18746                 DDM._onLoad();
18747             } else {
18748                 if (DDM._timeoutCount > 2000) {
18749                 } else {
18750                     setTimeout(DDM._addListeners, 10);
18751                     if (document && document.body) {
18752                         DDM._timeoutCount += 1;
18753                     }
18754                 }
18755             }
18756         },
18757
18758         /**
18759          * Recursively searches the immediate parent and all child nodes for
18760          * the handle element in order to determine wheter or not it was
18761          * clicked.
18762          * @method handleWasClicked
18763          * @param node the html element to inspect
18764          * @static
18765          */
18766         handleWasClicked: function(node, id) {
18767             if (this.isHandle(id, node.id)) {
18768                 return true;
18769             } else {
18770                 // check to see if this is a text node child of the one we want
18771                 var p = node.parentNode;
18772
18773                 while (p) {
18774                     if (this.isHandle(id, p.id)) {
18775                         return true;
18776                     } else {
18777                         p = p.parentNode;
18778                     }
18779                 }
18780             }
18781
18782             return false;
18783         }
18784
18785     };
18786
18787 }();
18788
18789 // shorter alias, save a few bytes
18790 Roo.dd.DDM = Roo.dd.DragDropMgr;
18791 Roo.dd.DDM._addListeners();
18792
18793 }/*
18794  * Based on:
18795  * Ext JS Library 1.1.1
18796  * Copyright(c) 2006-2007, Ext JS, LLC.
18797  *
18798  * Originally Released Under LGPL - original licence link has changed is not relivant.
18799  *
18800  * Fork - LGPL
18801  * <script type="text/javascript">
18802  */
18803
18804 /**
18805  * @class Roo.dd.DD
18806  * A DragDrop implementation where the linked element follows the
18807  * mouse cursor during a drag.
18808  * @extends Roo.dd.DragDrop
18809  * @constructor
18810  * @param {String} id the id of the linked element
18811  * @param {String} sGroup the group of related DragDrop items
18812  * @param {object} config an object containing configurable attributes
18813  *                Valid properties for DD:
18814  *                    scroll
18815  */
18816 Roo.dd.DD = function(id, sGroup, config) {
18817     if (id) {
18818         this.init(id, sGroup, config);
18819     }
18820 };
18821
18822 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18823
18824     /**
18825      * When set to true, the utility automatically tries to scroll the browser
18826      * window wehn a drag and drop element is dragged near the viewport boundary.
18827      * Defaults to true.
18828      * @property scroll
18829      * @type boolean
18830      */
18831     scroll: true,
18832
18833     /**
18834      * Sets the pointer offset to the distance between the linked element's top
18835      * left corner and the location the element was clicked
18836      * @method autoOffset
18837      * @param {int} iPageX the X coordinate of the click
18838      * @param {int} iPageY the Y coordinate of the click
18839      */
18840     autoOffset: function(iPageX, iPageY) {
18841         var x = iPageX - this.startPageX;
18842         var y = iPageY - this.startPageY;
18843         this.setDelta(x, y);
18844     },
18845
18846     /**
18847      * Sets the pointer offset.  You can call this directly to force the
18848      * offset to be in a particular location (e.g., pass in 0,0 to set it
18849      * to the center of the object)
18850      * @method setDelta
18851      * @param {int} iDeltaX the distance from the left
18852      * @param {int} iDeltaY the distance from the top
18853      */
18854     setDelta: function(iDeltaX, iDeltaY) {
18855         this.deltaX = iDeltaX;
18856         this.deltaY = iDeltaY;
18857     },
18858
18859     /**
18860      * Sets the drag element to the location of the mousedown or click event,
18861      * maintaining the cursor location relative to the location on the element
18862      * that was clicked.  Override this if you want to place the element in a
18863      * location other than where the cursor is.
18864      * @method setDragElPos
18865      * @param {int} iPageX the X coordinate of the mousedown or drag event
18866      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18867      */
18868     setDragElPos: function(iPageX, iPageY) {
18869         // the first time we do this, we are going to check to make sure
18870         // the element has css positioning
18871
18872         var el = this.getDragEl();
18873         this.alignElWithMouse(el, iPageX, iPageY);
18874     },
18875
18876     /**
18877      * Sets the element to the location of the mousedown or click event,
18878      * maintaining the cursor location relative to the location on the element
18879      * that was clicked.  Override this if you want to place the element in a
18880      * location other than where the cursor is.
18881      * @method alignElWithMouse
18882      * @param {HTMLElement} el the element to move
18883      * @param {int} iPageX the X coordinate of the mousedown or drag event
18884      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18885      */
18886     alignElWithMouse: function(el, iPageX, iPageY) {
18887         var oCoord = this.getTargetCoord(iPageX, iPageY);
18888         var fly = el.dom ? el : Roo.fly(el);
18889         if (!this.deltaSetXY) {
18890             var aCoord = [oCoord.x, oCoord.y];
18891             fly.setXY(aCoord);
18892             var newLeft = fly.getLeft(true);
18893             var newTop  = fly.getTop(true);
18894             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18895         } else {
18896             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18897         }
18898
18899         this.cachePosition(oCoord.x, oCoord.y);
18900         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18901         return oCoord;
18902     },
18903
18904     /**
18905      * Saves the most recent position so that we can reset the constraints and
18906      * tick marks on-demand.  We need to know this so that we can calculate the
18907      * number of pixels the element is offset from its original position.
18908      * @method cachePosition
18909      * @param iPageX the current x position (optional, this just makes it so we
18910      * don't have to look it up again)
18911      * @param iPageY the current y position (optional, this just makes it so we
18912      * don't have to look it up again)
18913      */
18914     cachePosition: function(iPageX, iPageY) {
18915         if (iPageX) {
18916             this.lastPageX = iPageX;
18917             this.lastPageY = iPageY;
18918         } else {
18919             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18920             this.lastPageX = aCoord[0];
18921             this.lastPageY = aCoord[1];
18922         }
18923     },
18924
18925     /**
18926      * Auto-scroll the window if the dragged object has been moved beyond the
18927      * visible window boundary.
18928      * @method autoScroll
18929      * @param {int} x the drag element's x position
18930      * @param {int} y the drag element's y position
18931      * @param {int} h the height of the drag element
18932      * @param {int} w the width of the drag element
18933      * @private
18934      */
18935     autoScroll: function(x, y, h, w) {
18936
18937         if (this.scroll) {
18938             // The client height
18939             var clientH = Roo.lib.Dom.getViewWidth();
18940
18941             // The client width
18942             var clientW = Roo.lib.Dom.getViewHeight();
18943
18944             // The amt scrolled down
18945             var st = this.DDM.getScrollTop();
18946
18947             // The amt scrolled right
18948             var sl = this.DDM.getScrollLeft();
18949
18950             // Location of the bottom of the element
18951             var bot = h + y;
18952
18953             // Location of the right of the element
18954             var right = w + x;
18955
18956             // The distance from the cursor to the bottom of the visible area,
18957             // adjusted so that we don't scroll if the cursor is beyond the
18958             // element drag constraints
18959             var toBot = (clientH + st - y - this.deltaY);
18960
18961             // The distance from the cursor to the right of the visible area
18962             var toRight = (clientW + sl - x - this.deltaX);
18963
18964
18965             // How close to the edge the cursor must be before we scroll
18966             // var thresh = (document.all) ? 100 : 40;
18967             var thresh = 40;
18968
18969             // How many pixels to scroll per autoscroll op.  This helps to reduce
18970             // clunky scrolling. IE is more sensitive about this ... it needs this
18971             // value to be higher.
18972             var scrAmt = (document.all) ? 80 : 30;
18973
18974             // Scroll down if we are near the bottom of the visible page and the
18975             // obj extends below the crease
18976             if ( bot > clientH && toBot < thresh ) {
18977                 window.scrollTo(sl, st + scrAmt);
18978             }
18979
18980             // Scroll up if the window is scrolled down and the top of the object
18981             // goes above the top border
18982             if ( y < st && st > 0 && y - st < thresh ) {
18983                 window.scrollTo(sl, st - scrAmt);
18984             }
18985
18986             // Scroll right if the obj is beyond the right border and the cursor is
18987             // near the border.
18988             if ( right > clientW && toRight < thresh ) {
18989                 window.scrollTo(sl + scrAmt, st);
18990             }
18991
18992             // Scroll left if the window has been scrolled to the right and the obj
18993             // extends past the left border
18994             if ( x < sl && sl > 0 && x - sl < thresh ) {
18995                 window.scrollTo(sl - scrAmt, st);
18996             }
18997         }
18998     },
18999
19000     /**
19001      * Finds the location the element should be placed if we want to move
19002      * it to where the mouse location less the click offset would place us.
19003      * @method getTargetCoord
19004      * @param {int} iPageX the X coordinate of the click
19005      * @param {int} iPageY the Y coordinate of the click
19006      * @return an object that contains the coordinates (Object.x and Object.y)
19007      * @private
19008      */
19009     getTargetCoord: function(iPageX, iPageY) {
19010
19011
19012         var x = iPageX - this.deltaX;
19013         var y = iPageY - this.deltaY;
19014
19015         if (this.constrainX) {
19016             if (x < this.minX) { x = this.minX; }
19017             if (x > this.maxX) { x = this.maxX; }
19018         }
19019
19020         if (this.constrainY) {
19021             if (y < this.minY) { y = this.minY; }
19022             if (y > this.maxY) { y = this.maxY; }
19023         }
19024
19025         x = this.getTick(x, this.xTicks);
19026         y = this.getTick(y, this.yTicks);
19027
19028
19029         return {x:x, y:y};
19030     },
19031
19032     /*
19033      * Sets up config options specific to this class. Overrides
19034      * Roo.dd.DragDrop, but all versions of this method through the
19035      * inheritance chain are called
19036      */
19037     applyConfig: function() {
19038         Roo.dd.DD.superclass.applyConfig.call(this);
19039         this.scroll = (this.config.scroll !== false);
19040     },
19041
19042     /*
19043      * Event that fires prior to the onMouseDown event.  Overrides
19044      * Roo.dd.DragDrop.
19045      */
19046     b4MouseDown: function(e) {
19047         // this.resetConstraints();
19048         this.autoOffset(e.getPageX(),
19049                             e.getPageY());
19050     },
19051
19052     /*
19053      * Event that fires prior to the onDrag event.  Overrides
19054      * Roo.dd.DragDrop.
19055      */
19056     b4Drag: function(e) {
19057         this.setDragElPos(e.getPageX(),
19058                             e.getPageY());
19059     },
19060
19061     toString: function() {
19062         return ("DD " + this.id);
19063     }
19064
19065     //////////////////////////////////////////////////////////////////////////
19066     // Debugging ygDragDrop events that can be overridden
19067     //////////////////////////////////////////////////////////////////////////
19068     /*
19069     startDrag: function(x, y) {
19070     },
19071
19072     onDrag: function(e) {
19073     },
19074
19075     onDragEnter: function(e, id) {
19076     },
19077
19078     onDragOver: function(e, id) {
19079     },
19080
19081     onDragOut: function(e, id) {
19082     },
19083
19084     onDragDrop: function(e, id) {
19085     },
19086
19087     endDrag: function(e) {
19088     }
19089
19090     */
19091
19092 });/*
19093  * Based on:
19094  * Ext JS Library 1.1.1
19095  * Copyright(c) 2006-2007, Ext JS, LLC.
19096  *
19097  * Originally Released Under LGPL - original licence link has changed is not relivant.
19098  *
19099  * Fork - LGPL
19100  * <script type="text/javascript">
19101  */
19102
19103 /**
19104  * @class Roo.dd.DDProxy
19105  * A DragDrop implementation that inserts an empty, bordered div into
19106  * the document that follows the cursor during drag operations.  At the time of
19107  * the click, the frame div is resized to the dimensions of the linked html
19108  * element, and moved to the exact location of the linked element.
19109  *
19110  * References to the "frame" element refer to the single proxy element that
19111  * was created to be dragged in place of all DDProxy elements on the
19112  * page.
19113  *
19114  * @extends Roo.dd.DD
19115  * @constructor
19116  * @param {String} id the id of the linked html element
19117  * @param {String} sGroup the group of related DragDrop objects
19118  * @param {object} config an object containing configurable attributes
19119  *                Valid properties for DDProxy in addition to those in DragDrop:
19120  *                   resizeFrame, centerFrame, dragElId
19121  */
19122 Roo.dd.DDProxy = function(id, sGroup, config) {
19123     if (id) {
19124         this.init(id, sGroup, config);
19125         this.initFrame();
19126     }
19127 };
19128
19129 /**
19130  * The default drag frame div id
19131  * @property Roo.dd.DDProxy.dragElId
19132  * @type String
19133  * @static
19134  */
19135 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19136
19137 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19138
19139     /**
19140      * By default we resize the drag frame to be the same size as the element
19141      * we want to drag (this is to get the frame effect).  We can turn it off
19142      * if we want a different behavior.
19143      * @property resizeFrame
19144      * @type boolean
19145      */
19146     resizeFrame: true,
19147
19148     /**
19149      * By default the frame is positioned exactly where the drag element is, so
19150      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19151      * you do not have constraints on the obj is to have the drag frame centered
19152      * around the cursor.  Set centerFrame to true for this effect.
19153      * @property centerFrame
19154      * @type boolean
19155      */
19156     centerFrame: false,
19157
19158     /**
19159      * Creates the proxy element if it does not yet exist
19160      * @method createFrame
19161      */
19162     createFrame: function() {
19163         var self = this;
19164         var body = document.body;
19165
19166         if (!body || !body.firstChild) {
19167             setTimeout( function() { self.createFrame(); }, 50 );
19168             return;
19169         }
19170
19171         var div = this.getDragEl();
19172
19173         if (!div) {
19174             div    = document.createElement("div");
19175             div.id = this.dragElId;
19176             var s  = div.style;
19177
19178             s.position   = "absolute";
19179             s.visibility = "hidden";
19180             s.cursor     = "move";
19181             s.border     = "2px solid #aaa";
19182             s.zIndex     = 999;
19183
19184             // appendChild can blow up IE if invoked prior to the window load event
19185             // while rendering a table.  It is possible there are other scenarios
19186             // that would cause this to happen as well.
19187             body.insertBefore(div, body.firstChild);
19188         }
19189     },
19190
19191     /**
19192      * Initialization for the drag frame element.  Must be called in the
19193      * constructor of all subclasses
19194      * @method initFrame
19195      */
19196     initFrame: function() {
19197         this.createFrame();
19198     },
19199
19200     applyConfig: function() {
19201         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19202
19203         this.resizeFrame = (this.config.resizeFrame !== false);
19204         this.centerFrame = (this.config.centerFrame);
19205         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19206     },
19207
19208     /**
19209      * Resizes the drag frame to the dimensions of the clicked object, positions
19210      * it over the object, and finally displays it
19211      * @method showFrame
19212      * @param {int} iPageX X click position
19213      * @param {int} iPageY Y click position
19214      * @private
19215      */
19216     showFrame: function(iPageX, iPageY) {
19217         var el = this.getEl();
19218         var dragEl = this.getDragEl();
19219         var s = dragEl.style;
19220
19221         this._resizeProxy();
19222
19223         if (this.centerFrame) {
19224             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19225                            Math.round(parseInt(s.height, 10)/2) );
19226         }
19227
19228         this.setDragElPos(iPageX, iPageY);
19229
19230         Roo.fly(dragEl).show();
19231     },
19232
19233     /**
19234      * The proxy is automatically resized to the dimensions of the linked
19235      * element when a drag is initiated, unless resizeFrame is set to false
19236      * @method _resizeProxy
19237      * @private
19238      */
19239     _resizeProxy: function() {
19240         if (this.resizeFrame) {
19241             var el = this.getEl();
19242             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19243         }
19244     },
19245
19246     // overrides Roo.dd.DragDrop
19247     b4MouseDown: function(e) {
19248         var x = e.getPageX();
19249         var y = e.getPageY();
19250         this.autoOffset(x, y);
19251         this.setDragElPos(x, y);
19252     },
19253
19254     // overrides Roo.dd.DragDrop
19255     b4StartDrag: function(x, y) {
19256         // show the drag frame
19257         this.showFrame(x, y);
19258     },
19259
19260     // overrides Roo.dd.DragDrop
19261     b4EndDrag: function(e) {
19262         Roo.fly(this.getDragEl()).hide();
19263     },
19264
19265     // overrides Roo.dd.DragDrop
19266     // By default we try to move the element to the last location of the frame.
19267     // This is so that the default behavior mirrors that of Roo.dd.DD.
19268     endDrag: function(e) {
19269
19270         var lel = this.getEl();
19271         var del = this.getDragEl();
19272
19273         // Show the drag frame briefly so we can get its position
19274         del.style.visibility = "";
19275
19276         this.beforeMove();
19277         // Hide the linked element before the move to get around a Safari
19278         // rendering bug.
19279         lel.style.visibility = "hidden";
19280         Roo.dd.DDM.moveToEl(lel, del);
19281         del.style.visibility = "hidden";
19282         lel.style.visibility = "";
19283
19284         this.afterDrag();
19285     },
19286
19287     beforeMove : function(){
19288
19289     },
19290
19291     afterDrag : function(){
19292
19293     },
19294
19295     toString: function() {
19296         return ("DDProxy " + this.id);
19297     }
19298
19299 });
19300 /*
19301  * Based on:
19302  * Ext JS Library 1.1.1
19303  * Copyright(c) 2006-2007, Ext JS, LLC.
19304  *
19305  * Originally Released Under LGPL - original licence link has changed is not relivant.
19306  *
19307  * Fork - LGPL
19308  * <script type="text/javascript">
19309  */
19310
19311  /**
19312  * @class Roo.dd.DDTarget
19313  * A DragDrop implementation that does not move, but can be a drop
19314  * target.  You would get the same result by simply omitting implementation
19315  * for the event callbacks, but this way we reduce the processing cost of the
19316  * event listener and the callbacks.
19317  * @extends Roo.dd.DragDrop
19318  * @constructor
19319  * @param {String} id the id of the element that is a drop target
19320  * @param {String} sGroup the group of related DragDrop objects
19321  * @param {object} config an object containing configurable attributes
19322  *                 Valid properties for DDTarget in addition to those in
19323  *                 DragDrop:
19324  *                    none
19325  */
19326 Roo.dd.DDTarget = function(id, sGroup, config) {
19327     if (id) {
19328         this.initTarget(id, sGroup, config);
19329     }
19330     if (config.listeners || config.events) { 
19331        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19332             listeners : config.listeners || {}, 
19333             events : config.events || {} 
19334         });    
19335     }
19336 };
19337
19338 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19339 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19340     toString: function() {
19341         return ("DDTarget " + this.id);
19342     }
19343 });
19344 /*
19345  * Based on:
19346  * Ext JS Library 1.1.1
19347  * Copyright(c) 2006-2007, Ext JS, LLC.
19348  *
19349  * Originally Released Under LGPL - original licence link has changed is not relivant.
19350  *
19351  * Fork - LGPL
19352  * <script type="text/javascript">
19353  */
19354  
19355
19356 /**
19357  * @class Roo.dd.ScrollManager
19358  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19359  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19360  * @singleton
19361  */
19362 Roo.dd.ScrollManager = function(){
19363     var ddm = Roo.dd.DragDropMgr;
19364     var els = {};
19365     var dragEl = null;
19366     var proc = {};
19367     
19368     
19369     
19370     var onStop = function(e){
19371         dragEl = null;
19372         clearProc();
19373     };
19374     
19375     var triggerRefresh = function(){
19376         if(ddm.dragCurrent){
19377              ddm.refreshCache(ddm.dragCurrent.groups);
19378         }
19379     };
19380     
19381     var doScroll = function(){
19382         if(ddm.dragCurrent){
19383             var dds = Roo.dd.ScrollManager;
19384             if(!dds.animate){
19385                 if(proc.el.scroll(proc.dir, dds.increment)){
19386                     triggerRefresh();
19387                 }
19388             }else{
19389                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19390             }
19391         }
19392     };
19393     
19394     var clearProc = function(){
19395         if(proc.id){
19396             clearInterval(proc.id);
19397         }
19398         proc.id = 0;
19399         proc.el = null;
19400         proc.dir = "";
19401     };
19402     
19403     var startProc = function(el, dir){
19404          Roo.log('scroll startproc');
19405         clearProc();
19406         proc.el = el;
19407         proc.dir = dir;
19408         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19409     };
19410     
19411     var onFire = function(e, isDrop){
19412        
19413         if(isDrop || !ddm.dragCurrent){ return; }
19414         var dds = Roo.dd.ScrollManager;
19415         if(!dragEl || dragEl != ddm.dragCurrent){
19416             dragEl = ddm.dragCurrent;
19417             // refresh regions on drag start
19418             dds.refreshCache();
19419         }
19420         
19421         var xy = Roo.lib.Event.getXY(e);
19422         var pt = new Roo.lib.Point(xy[0], xy[1]);
19423         for(var id in els){
19424             var el = els[id], r = el._region;
19425             if(r && r.contains(pt) && el.isScrollable()){
19426                 if(r.bottom - pt.y <= dds.thresh){
19427                     if(proc.el != el){
19428                         startProc(el, "down");
19429                     }
19430                     return;
19431                 }else if(r.right - pt.x <= dds.thresh){
19432                     if(proc.el != el){
19433                         startProc(el, "left");
19434                     }
19435                     return;
19436                 }else if(pt.y - r.top <= dds.thresh){
19437                     if(proc.el != el){
19438                         startProc(el, "up");
19439                     }
19440                     return;
19441                 }else if(pt.x - r.left <= dds.thresh){
19442                     if(proc.el != el){
19443                         startProc(el, "right");
19444                     }
19445                     return;
19446                 }
19447             }
19448         }
19449         clearProc();
19450     };
19451     
19452     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19453     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19454     
19455     return {
19456         /**
19457          * Registers new overflow element(s) to auto scroll
19458          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19459          */
19460         register : function(el){
19461             if(el instanceof Array){
19462                 for(var i = 0, len = el.length; i < len; i++) {
19463                         this.register(el[i]);
19464                 }
19465             }else{
19466                 el = Roo.get(el);
19467                 els[el.id] = el;
19468             }
19469             Roo.dd.ScrollManager.els = els;
19470         },
19471         
19472         /**
19473          * Unregisters overflow element(s) so they are no longer scrolled
19474          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19475          */
19476         unregister : function(el){
19477             if(el instanceof Array){
19478                 for(var i = 0, len = el.length; i < len; i++) {
19479                         this.unregister(el[i]);
19480                 }
19481             }else{
19482                 el = Roo.get(el);
19483                 delete els[el.id];
19484             }
19485         },
19486         
19487         /**
19488          * The number of pixels from the edge of a container the pointer needs to be to 
19489          * trigger scrolling (defaults to 25)
19490          * @type Number
19491          */
19492         thresh : 25,
19493         
19494         /**
19495          * The number of pixels to scroll in each scroll increment (defaults to 50)
19496          * @type Number
19497          */
19498         increment : 100,
19499         
19500         /**
19501          * The frequency of scrolls in milliseconds (defaults to 500)
19502          * @type Number
19503          */
19504         frequency : 500,
19505         
19506         /**
19507          * True to animate the scroll (defaults to true)
19508          * @type Boolean
19509          */
19510         animate: true,
19511         
19512         /**
19513          * The animation duration in seconds - 
19514          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19515          * @type Number
19516          */
19517         animDuration: .4,
19518         
19519         /**
19520          * Manually trigger a cache refresh.
19521          */
19522         refreshCache : function(){
19523             for(var id in els){
19524                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19525                     els[id]._region = els[id].getRegion();
19526                 }
19527             }
19528         }
19529     };
19530 }();/*
19531  * Based on:
19532  * Ext JS Library 1.1.1
19533  * Copyright(c) 2006-2007, Ext JS, LLC.
19534  *
19535  * Originally Released Under LGPL - original licence link has changed is not relivant.
19536  *
19537  * Fork - LGPL
19538  * <script type="text/javascript">
19539  */
19540  
19541
19542 /**
19543  * @class Roo.dd.Registry
19544  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19545  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19546  * @singleton
19547  */
19548 Roo.dd.Registry = function(){
19549     var elements = {}; 
19550     var handles = {}; 
19551     var autoIdSeed = 0;
19552
19553     var getId = function(el, autogen){
19554         if(typeof el == "string"){
19555             return el;
19556         }
19557         var id = el.id;
19558         if(!id && autogen !== false){
19559             id = "roodd-" + (++autoIdSeed);
19560             el.id = id;
19561         }
19562         return id;
19563     };
19564     
19565     return {
19566     /**
19567      * Register a drag drop element
19568      * @param {String|HTMLElement} element The id or DOM node to register
19569      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19570      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19571      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19572      * populated in the data object (if applicable):
19573      * <pre>
19574 Value      Description<br />
19575 ---------  ------------------------------------------<br />
19576 handles    Array of DOM nodes that trigger dragging<br />
19577            for the element being registered<br />
19578 isHandle   True if the element passed in triggers<br />
19579            dragging itself, else false
19580 </pre>
19581      */
19582         register : function(el, data){
19583             data = data || {};
19584             if(typeof el == "string"){
19585                 el = document.getElementById(el);
19586             }
19587             data.ddel = el;
19588             elements[getId(el)] = data;
19589             if(data.isHandle !== false){
19590                 handles[data.ddel.id] = data;
19591             }
19592             if(data.handles){
19593                 var hs = data.handles;
19594                 for(var i = 0, len = hs.length; i < len; i++){
19595                         handles[getId(hs[i])] = data;
19596                 }
19597             }
19598         },
19599
19600     /**
19601      * Unregister a drag drop element
19602      * @param {String|HTMLElement}  element The id or DOM node to unregister
19603      */
19604         unregister : function(el){
19605             var id = getId(el, false);
19606             var data = elements[id];
19607             if(data){
19608                 delete elements[id];
19609                 if(data.handles){
19610                     var hs = data.handles;
19611                     for(var i = 0, len = hs.length; i < len; i++){
19612                         delete handles[getId(hs[i], false)];
19613                     }
19614                 }
19615             }
19616         },
19617
19618     /**
19619      * Returns the handle registered for a DOM Node by id
19620      * @param {String|HTMLElement} id The DOM node or id to look up
19621      * @return {Object} handle The custom handle data
19622      */
19623         getHandle : function(id){
19624             if(typeof id != "string"){ // must be element?
19625                 id = id.id;
19626             }
19627             return handles[id];
19628         },
19629
19630     /**
19631      * Returns the handle that is registered for the DOM node that is the target of the event
19632      * @param {Event} e The event
19633      * @return {Object} handle The custom handle data
19634      */
19635         getHandleFromEvent : function(e){
19636             var t = Roo.lib.Event.getTarget(e);
19637             return t ? handles[t.id] : null;
19638         },
19639
19640     /**
19641      * Returns a custom data object that is registered for a DOM node by id
19642      * @param {String|HTMLElement} id The DOM node or id to look up
19643      * @return {Object} data The custom data
19644      */
19645         getTarget : function(id){
19646             if(typeof id != "string"){ // must be element?
19647                 id = id.id;
19648             }
19649             return elements[id];
19650         },
19651
19652     /**
19653      * Returns a custom data object that is registered for the DOM node that is the target of the event
19654      * @param {Event} e The event
19655      * @return {Object} data The custom data
19656      */
19657         getTargetFromEvent : function(e){
19658             var t = Roo.lib.Event.getTarget(e);
19659             return t ? elements[t.id] || handles[t.id] : null;
19660         }
19661     };
19662 }();/*
19663  * Based on:
19664  * Ext JS Library 1.1.1
19665  * Copyright(c) 2006-2007, Ext JS, LLC.
19666  *
19667  * Originally Released Under LGPL - original licence link has changed is not relivant.
19668  *
19669  * Fork - LGPL
19670  * <script type="text/javascript">
19671  */
19672  
19673
19674 /**
19675  * @class Roo.dd.StatusProxy
19676  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19677  * default drag proxy used by all Roo.dd components.
19678  * @constructor
19679  * @param {Object} config
19680  */
19681 Roo.dd.StatusProxy = function(config){
19682     Roo.apply(this, config);
19683     this.id = this.id || Roo.id();
19684     this.el = new Roo.Layer({
19685         dh: {
19686             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19687                 {tag: "div", cls: "x-dd-drop-icon"},
19688                 {tag: "div", cls: "x-dd-drag-ghost"}
19689             ]
19690         }, 
19691         shadow: !config || config.shadow !== false
19692     });
19693     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19694     this.dropStatus = this.dropNotAllowed;
19695 };
19696
19697 Roo.dd.StatusProxy.prototype = {
19698     /**
19699      * @cfg {String} dropAllowed
19700      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19701      */
19702     dropAllowed : "x-dd-drop-ok",
19703     /**
19704      * @cfg {String} dropNotAllowed
19705      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19706      */
19707     dropNotAllowed : "x-dd-drop-nodrop",
19708
19709     /**
19710      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19711      * over the current target element.
19712      * @param {String} cssClass The css class for the new drop status indicator image
19713      */
19714     setStatus : function(cssClass){
19715         cssClass = cssClass || this.dropNotAllowed;
19716         if(this.dropStatus != cssClass){
19717             this.el.replaceClass(this.dropStatus, cssClass);
19718             this.dropStatus = cssClass;
19719         }
19720     },
19721
19722     /**
19723      * Resets the status indicator to the default dropNotAllowed value
19724      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19725      */
19726     reset : function(clearGhost){
19727         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19728         this.dropStatus = this.dropNotAllowed;
19729         if(clearGhost){
19730             this.ghost.update("");
19731         }
19732     },
19733
19734     /**
19735      * Updates the contents of the ghost element
19736      * @param {String} html The html that will replace the current innerHTML of the ghost element
19737      */
19738     update : function(html){
19739         if(typeof html == "string"){
19740             this.ghost.update(html);
19741         }else{
19742             this.ghost.update("");
19743             html.style.margin = "0";
19744             this.ghost.dom.appendChild(html);
19745         }
19746         // ensure float = none set?? cant remember why though.
19747         var el = this.ghost.dom.firstChild;
19748                 if(el){
19749                         Roo.fly(el).setStyle('float', 'none');
19750                 }
19751     },
19752     
19753     /**
19754      * Returns the underlying proxy {@link Roo.Layer}
19755      * @return {Roo.Layer} el
19756     */
19757     getEl : function(){
19758         return this.el;
19759     },
19760
19761     /**
19762      * Returns the ghost element
19763      * @return {Roo.Element} el
19764      */
19765     getGhost : function(){
19766         return this.ghost;
19767     },
19768
19769     /**
19770      * Hides the proxy
19771      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19772      */
19773     hide : function(clear){
19774         this.el.hide();
19775         if(clear){
19776             this.reset(true);
19777         }
19778     },
19779
19780     /**
19781      * Stops the repair animation if it's currently running
19782      */
19783     stop : function(){
19784         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19785             this.anim.stop();
19786         }
19787     },
19788
19789     /**
19790      * Displays this proxy
19791      */
19792     show : function(){
19793         this.el.show();
19794     },
19795
19796     /**
19797      * Force the Layer to sync its shadow and shim positions to the element
19798      */
19799     sync : function(){
19800         this.el.sync();
19801     },
19802
19803     /**
19804      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19805      * invalid drop operation by the item being dragged.
19806      * @param {Array} xy The XY position of the element ([x, y])
19807      * @param {Function} callback The function to call after the repair is complete
19808      * @param {Object} scope The scope in which to execute the callback
19809      */
19810     repair : function(xy, callback, scope){
19811         this.callback = callback;
19812         this.scope = scope;
19813         if(xy && this.animRepair !== false){
19814             this.el.addClass("x-dd-drag-repair");
19815             this.el.hideUnders(true);
19816             this.anim = this.el.shift({
19817                 duration: this.repairDuration || .5,
19818                 easing: 'easeOut',
19819                 xy: xy,
19820                 stopFx: true,
19821                 callback: this.afterRepair,
19822                 scope: this
19823             });
19824         }else{
19825             this.afterRepair();
19826         }
19827     },
19828
19829     // private
19830     afterRepair : function(){
19831         this.hide(true);
19832         if(typeof this.callback == "function"){
19833             this.callback.call(this.scope || this);
19834         }
19835         this.callback = null;
19836         this.scope = null;
19837     }
19838 };/*
19839  * Based on:
19840  * Ext JS Library 1.1.1
19841  * Copyright(c) 2006-2007, Ext JS, LLC.
19842  *
19843  * Originally Released Under LGPL - original licence link has changed is not relivant.
19844  *
19845  * Fork - LGPL
19846  * <script type="text/javascript">
19847  */
19848
19849 /**
19850  * @class Roo.dd.DragSource
19851  * @extends Roo.dd.DDProxy
19852  * A simple class that provides the basic implementation needed to make any element draggable.
19853  * @constructor
19854  * @param {String/HTMLElement/Element} el The container element
19855  * @param {Object} config
19856  */
19857 Roo.dd.DragSource = function(el, config){
19858     this.el = Roo.get(el);
19859     this.dragData = {};
19860     
19861     Roo.apply(this, config);
19862     
19863     if(!this.proxy){
19864         this.proxy = new Roo.dd.StatusProxy();
19865     }
19866
19867     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19868           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19869     
19870     this.dragging = false;
19871 };
19872
19873 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19874     /**
19875      * @cfg {String} dropAllowed
19876      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19877      */
19878     dropAllowed : "x-dd-drop-ok",
19879     /**
19880      * @cfg {String} dropNotAllowed
19881      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19882      */
19883     dropNotAllowed : "x-dd-drop-nodrop",
19884
19885     /**
19886      * Returns the data object associated with this drag source
19887      * @return {Object} data An object containing arbitrary data
19888      */
19889     getDragData : function(e){
19890         return this.dragData;
19891     },
19892
19893     // private
19894     onDragEnter : function(e, id){
19895         var target = Roo.dd.DragDropMgr.getDDById(id);
19896         this.cachedTarget = target;
19897         if(this.beforeDragEnter(target, e, id) !== false){
19898             if(target.isNotifyTarget){
19899                 var status = target.notifyEnter(this, e, this.dragData);
19900                 this.proxy.setStatus(status);
19901             }else{
19902                 this.proxy.setStatus(this.dropAllowed);
19903             }
19904             
19905             if(this.afterDragEnter){
19906                 /**
19907                  * An empty function by default, but provided so that you can perform a custom action
19908                  * when the dragged item enters the drop target by providing an implementation.
19909                  * @param {Roo.dd.DragDrop} target The drop target
19910                  * @param {Event} e The event object
19911                  * @param {String} id The id of the dragged element
19912                  * @method afterDragEnter
19913                  */
19914                 this.afterDragEnter(target, e, id);
19915             }
19916         }
19917     },
19918
19919     /**
19920      * An empty function by default, but provided so that you can perform a custom action
19921      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19922      * @param {Roo.dd.DragDrop} target The drop target
19923      * @param {Event} e The event object
19924      * @param {String} id The id of the dragged element
19925      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19926      */
19927     beforeDragEnter : function(target, e, id){
19928         return true;
19929     },
19930
19931     // private
19932     alignElWithMouse: function() {
19933         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19934         this.proxy.sync();
19935     },
19936
19937     // private
19938     onDragOver : function(e, id){
19939         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19940         if(this.beforeDragOver(target, e, id) !== false){
19941             if(target.isNotifyTarget){
19942                 var status = target.notifyOver(this, e, this.dragData);
19943                 this.proxy.setStatus(status);
19944             }
19945
19946             if(this.afterDragOver){
19947                 /**
19948                  * An empty function by default, but provided so that you can perform a custom action
19949                  * while the dragged item is over the drop target by providing an implementation.
19950                  * @param {Roo.dd.DragDrop} target The drop target
19951                  * @param {Event} e The event object
19952                  * @param {String} id The id of the dragged element
19953                  * @method afterDragOver
19954                  */
19955                 this.afterDragOver(target, e, id);
19956             }
19957         }
19958     },
19959
19960     /**
19961      * An empty function by default, but provided so that you can perform a custom action
19962      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19963      * @param {Roo.dd.DragDrop} target The drop target
19964      * @param {Event} e The event object
19965      * @param {String} id The id of the dragged element
19966      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19967      */
19968     beforeDragOver : function(target, e, id){
19969         return true;
19970     },
19971
19972     // private
19973     onDragOut : function(e, id){
19974         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19975         if(this.beforeDragOut(target, e, id) !== false){
19976             if(target.isNotifyTarget){
19977                 target.notifyOut(this, e, this.dragData);
19978             }
19979             this.proxy.reset();
19980             if(this.afterDragOut){
19981                 /**
19982                  * An empty function by default, but provided so that you can perform a custom action
19983                  * after the dragged item is dragged out of the target without dropping.
19984                  * @param {Roo.dd.DragDrop} target The drop target
19985                  * @param {Event} e The event object
19986                  * @param {String} id The id of the dragged element
19987                  * @method afterDragOut
19988                  */
19989                 this.afterDragOut(target, e, id);
19990             }
19991         }
19992         this.cachedTarget = null;
19993     },
19994
19995     /**
19996      * An empty function by default, but provided so that you can perform a custom action before the dragged
19997      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
19998      * @param {Roo.dd.DragDrop} target The drop target
19999      * @param {Event} e The event object
20000      * @param {String} id The id of the dragged element
20001      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20002      */
20003     beforeDragOut : function(target, e, id){
20004         return true;
20005     },
20006     
20007     // private
20008     onDragDrop : function(e, id){
20009         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20010         if(this.beforeDragDrop(target, e, id) !== false){
20011             if(target.isNotifyTarget){
20012                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20013                     this.onValidDrop(target, e, id);
20014                 }else{
20015                     this.onInvalidDrop(target, e, id);
20016                 }
20017             }else{
20018                 this.onValidDrop(target, e, id);
20019             }
20020             
20021             if(this.afterDragDrop){
20022                 /**
20023                  * An empty function by default, but provided so that you can perform a custom action
20024                  * after a valid drag drop has occurred by providing an implementation.
20025                  * @param {Roo.dd.DragDrop} target The drop target
20026                  * @param {Event} e The event object
20027                  * @param {String} id The id of the dropped element
20028                  * @method afterDragDrop
20029                  */
20030                 this.afterDragDrop(target, e, id);
20031             }
20032         }
20033         delete this.cachedTarget;
20034     },
20035
20036     /**
20037      * An empty function by default, but provided so that you can perform a custom action before the dragged
20038      * item is dropped onto the target and optionally cancel the onDragDrop.
20039      * @param {Roo.dd.DragDrop} target The drop target
20040      * @param {Event} e The event object
20041      * @param {String} id The id of the dragged element
20042      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20043      */
20044     beforeDragDrop : function(target, e, id){
20045         return true;
20046     },
20047
20048     // private
20049     onValidDrop : function(target, e, id){
20050         this.hideProxy();
20051         if(this.afterValidDrop){
20052             /**
20053              * An empty function by default, but provided so that you can perform a custom action
20054              * after a valid drop has occurred by providing an implementation.
20055              * @param {Object} target The target DD 
20056              * @param {Event} e The event object
20057              * @param {String} id The id of the dropped element
20058              * @method afterInvalidDrop
20059              */
20060             this.afterValidDrop(target, e, id);
20061         }
20062     },
20063
20064     // private
20065     getRepairXY : function(e, data){
20066         return this.el.getXY();  
20067     },
20068
20069     // private
20070     onInvalidDrop : function(target, e, id){
20071         this.beforeInvalidDrop(target, e, id);
20072         if(this.cachedTarget){
20073             if(this.cachedTarget.isNotifyTarget){
20074                 this.cachedTarget.notifyOut(this, e, this.dragData);
20075             }
20076             this.cacheTarget = null;
20077         }
20078         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20079
20080         if(this.afterInvalidDrop){
20081             /**
20082              * An empty function by default, but provided so that you can perform a custom action
20083              * after an invalid drop has occurred by providing an implementation.
20084              * @param {Event} e The event object
20085              * @param {String} id The id of the dropped element
20086              * @method afterInvalidDrop
20087              */
20088             this.afterInvalidDrop(e, id);
20089         }
20090     },
20091
20092     // private
20093     afterRepair : function(){
20094         if(Roo.enableFx){
20095             this.el.highlight(this.hlColor || "c3daf9");
20096         }
20097         this.dragging = false;
20098     },
20099
20100     /**
20101      * An empty function by default, but provided so that you can perform a custom action after an invalid
20102      * drop has occurred.
20103      * @param {Roo.dd.DragDrop} target The drop target
20104      * @param {Event} e The event object
20105      * @param {String} id The id of the dragged element
20106      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20107      */
20108     beforeInvalidDrop : function(target, e, id){
20109         return true;
20110     },
20111
20112     // private
20113     handleMouseDown : function(e){
20114         if(this.dragging) {
20115             return;
20116         }
20117         var data = this.getDragData(e);
20118         if(data && this.onBeforeDrag(data, e) !== false){
20119             this.dragData = data;
20120             this.proxy.stop();
20121             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20122         } 
20123     },
20124
20125     /**
20126      * An empty function by default, but provided so that you can perform a custom action before the initial
20127      * drag event begins and optionally cancel it.
20128      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20129      * @param {Event} e The event object
20130      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20131      */
20132     onBeforeDrag : function(data, e){
20133         return true;
20134     },
20135
20136     /**
20137      * An empty function by default, but provided so that you can perform a custom action once the initial
20138      * drag event has begun.  The drag cannot be canceled from this function.
20139      * @param {Number} x The x position of the click on the dragged object
20140      * @param {Number} y The y position of the click on the dragged object
20141      */
20142     onStartDrag : Roo.emptyFn,
20143
20144     // private - YUI override
20145     startDrag : function(x, y){
20146         this.proxy.reset();
20147         this.dragging = true;
20148         this.proxy.update("");
20149         this.onInitDrag(x, y);
20150         this.proxy.show();
20151     },
20152
20153     // private
20154     onInitDrag : function(x, y){
20155         var clone = this.el.dom.cloneNode(true);
20156         clone.id = Roo.id(); // prevent duplicate ids
20157         this.proxy.update(clone);
20158         this.onStartDrag(x, y);
20159         return true;
20160     },
20161
20162     /**
20163      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20164      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20165      */
20166     getProxy : function(){
20167         return this.proxy;  
20168     },
20169
20170     /**
20171      * Hides the drag source's {@link Roo.dd.StatusProxy}
20172      */
20173     hideProxy : function(){
20174         this.proxy.hide();  
20175         this.proxy.reset(true);
20176         this.dragging = false;
20177     },
20178
20179     // private
20180     triggerCacheRefresh : function(){
20181         Roo.dd.DDM.refreshCache(this.groups);
20182     },
20183
20184     // private - override to prevent hiding
20185     b4EndDrag: function(e) {
20186     },
20187
20188     // private - override to prevent moving
20189     endDrag : function(e){
20190         this.onEndDrag(this.dragData, e);
20191     },
20192
20193     // private
20194     onEndDrag : function(data, e){
20195     },
20196     
20197     // private - pin to cursor
20198     autoOffset : function(x, y) {
20199         this.setDelta(-12, -20);
20200     }    
20201 });/*
20202  * Based on:
20203  * Ext JS Library 1.1.1
20204  * Copyright(c) 2006-2007, Ext JS, LLC.
20205  *
20206  * Originally Released Under LGPL - original licence link has changed is not relivant.
20207  *
20208  * Fork - LGPL
20209  * <script type="text/javascript">
20210  */
20211
20212
20213 /**
20214  * @class Roo.dd.DropTarget
20215  * @extends Roo.dd.DDTarget
20216  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20217  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20218  * @constructor
20219  * @param {String/HTMLElement/Element} el The container element
20220  * @param {Object} config
20221  */
20222 Roo.dd.DropTarget = function(el, config){
20223     this.el = Roo.get(el);
20224     
20225     var listeners = false; ;
20226     if (config && config.listeners) {
20227         listeners= config.listeners;
20228         delete config.listeners;
20229     }
20230     Roo.apply(this, config);
20231     
20232     if(this.containerScroll){
20233         Roo.dd.ScrollManager.register(this.el);
20234     }
20235     this.addEvents( {
20236          /**
20237          * @scope Roo.dd.DropTarget
20238          */
20239          
20240          /**
20241          * @event enter
20242          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20243          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20244          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20245          * 
20246          * IMPORTANT : it should set this.overClass and this.dropAllowed
20247          * 
20248          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20249          * @param {Event} e The event
20250          * @param {Object} data An object containing arbitrary data supplied by the drag source
20251          */
20252         "enter" : true,
20253         
20254          /**
20255          * @event over
20256          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20257          * This method will be called on every mouse movement while the drag source is over the drop target.
20258          * This default implementation simply returns the dropAllowed config value.
20259          * 
20260          * IMPORTANT : it should set this.dropAllowed
20261          * 
20262          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20263          * @param {Event} e The event
20264          * @param {Object} data An object containing arbitrary data supplied by the drag source
20265          
20266          */
20267         "over" : true,
20268         /**
20269          * @event out
20270          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20271          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20272          * overClass (if any) from the drop element.
20273          * 
20274          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20275          * @param {Event} e The event
20276          * @param {Object} data An object containing arbitrary data supplied by the drag source
20277          */
20278          "out" : true,
20279          
20280         /**
20281          * @event drop
20282          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20283          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20284          * implementation that does something to process the drop event and returns true so that the drag source's
20285          * repair action does not run.
20286          * 
20287          * IMPORTANT : it should set this.success
20288          * 
20289          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20290          * @param {Event} e The event
20291          * @param {Object} data An object containing arbitrary data supplied by the drag source
20292         */
20293          "drop" : true
20294     });
20295             
20296      
20297     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20298         this.el.dom, 
20299         this.ddGroup || this.group,
20300         {
20301             isTarget: true,
20302             listeners : listeners || {} 
20303            
20304         
20305         }
20306     );
20307
20308 };
20309
20310 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20311     /**
20312      * @cfg {String} overClass
20313      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20314      */
20315      /**
20316      * @cfg {String} ddGroup
20317      * The drag drop group to handle drop events for
20318      */
20319      
20320     /**
20321      * @cfg {String} dropAllowed
20322      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20323      */
20324     dropAllowed : "x-dd-drop-ok",
20325     /**
20326      * @cfg {String} dropNotAllowed
20327      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20328      */
20329     dropNotAllowed : "x-dd-drop-nodrop",
20330     /**
20331      * @cfg {boolean} success
20332      * set this after drop listener.. 
20333      */
20334     success : false,
20335     /**
20336      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20337      * if the drop point is valid for over/enter..
20338      */
20339     valid : false,
20340     // private
20341     isTarget : true,
20342
20343     // private
20344     isNotifyTarget : true,
20345     
20346     /**
20347      * @hide
20348      */
20349     notifyEnter : function(dd, e, data)
20350     {
20351         this.valid = true;
20352         this.fireEvent('enter', dd, e, data);
20353         if(this.overClass){
20354             this.el.addClass(this.overClass);
20355         }
20356         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20357             this.valid ? this.dropAllowed : this.dropNotAllowed
20358         );
20359     },
20360
20361     /**
20362      * @hide
20363      */
20364     notifyOver : function(dd, e, data)
20365     {
20366         this.valid = true;
20367         this.fireEvent('over', dd, e, data);
20368         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20369             this.valid ? this.dropAllowed : this.dropNotAllowed
20370         );
20371     },
20372
20373     /**
20374      * @hide
20375      */
20376     notifyOut : function(dd, e, data)
20377     {
20378         this.fireEvent('out', dd, e, data);
20379         if(this.overClass){
20380             this.el.removeClass(this.overClass);
20381         }
20382     },
20383
20384     /**
20385      * @hide
20386      */
20387     notifyDrop : function(dd, e, data)
20388     {
20389         this.success = false;
20390         this.fireEvent('drop', dd, e, data);
20391         return this.success;
20392     }
20393 });/*
20394  * Based on:
20395  * Ext JS Library 1.1.1
20396  * Copyright(c) 2006-2007, Ext JS, LLC.
20397  *
20398  * Originally Released Under LGPL - original licence link has changed is not relivant.
20399  *
20400  * Fork - LGPL
20401  * <script type="text/javascript">
20402  */
20403
20404
20405 /**
20406  * @class Roo.dd.DragZone
20407  * @extends Roo.dd.DragSource
20408  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20409  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20410  * @constructor
20411  * @param {String/HTMLElement/Element} el The container element
20412  * @param {Object} config
20413  */
20414 Roo.dd.DragZone = function(el, config){
20415     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20416     if(this.containerScroll){
20417         Roo.dd.ScrollManager.register(this.el);
20418     }
20419 };
20420
20421 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20422     /**
20423      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20424      * for auto scrolling during drag operations.
20425      */
20426     /**
20427      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20428      * method after a failed drop (defaults to "c3daf9" - light blue)
20429      */
20430
20431     /**
20432      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20433      * for a valid target to drag based on the mouse down. Override this method
20434      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20435      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20436      * @param {EventObject} e The mouse down event
20437      * @return {Object} The dragData
20438      */
20439     getDragData : function(e){
20440         return Roo.dd.Registry.getHandleFromEvent(e);
20441     },
20442     
20443     /**
20444      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20445      * this.dragData.ddel
20446      * @param {Number} x The x position of the click on the dragged object
20447      * @param {Number} y The y position of the click on the dragged object
20448      * @return {Boolean} true to continue the drag, false to cancel
20449      */
20450     onInitDrag : function(x, y){
20451         this.proxy.update(this.dragData.ddel.cloneNode(true));
20452         this.onStartDrag(x, y);
20453         return true;
20454     },
20455     
20456     /**
20457      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20458      */
20459     afterRepair : function(){
20460         if(Roo.enableFx){
20461             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20462         }
20463         this.dragging = false;
20464     },
20465
20466     /**
20467      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20468      * the XY of this.dragData.ddel
20469      * @param {EventObject} e The mouse up event
20470      * @return {Array} The xy location (e.g. [100, 200])
20471      */
20472     getRepairXY : function(e){
20473         return Roo.Element.fly(this.dragData.ddel).getXY();  
20474     }
20475 });/*
20476  * Based on:
20477  * Ext JS Library 1.1.1
20478  * Copyright(c) 2006-2007, Ext JS, LLC.
20479  *
20480  * Originally Released Under LGPL - original licence link has changed is not relivant.
20481  *
20482  * Fork - LGPL
20483  * <script type="text/javascript">
20484  */
20485 /**
20486  * @class Roo.dd.DropZone
20487  * @extends Roo.dd.DropTarget
20488  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20489  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20490  * @constructor
20491  * @param {String/HTMLElement/Element} el The container element
20492  * @param {Object} config
20493  */
20494 Roo.dd.DropZone = function(el, config){
20495     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20496 };
20497
20498 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20499     /**
20500      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20501      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20502      * provide your own custom lookup.
20503      * @param {Event} e The event
20504      * @return {Object} data The custom data
20505      */
20506     getTargetFromEvent : function(e){
20507         return Roo.dd.Registry.getTargetFromEvent(e);
20508     },
20509
20510     /**
20511      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20512      * that it has registered.  This method has no default implementation and should be overridden to provide
20513      * node-specific processing if necessary.
20514      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20515      * {@link #getTargetFromEvent} for this node)
20516      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20517      * @param {Event} e The event
20518      * @param {Object} data An object containing arbitrary data supplied by the drag source
20519      */
20520     onNodeEnter : function(n, dd, e, data){
20521         
20522     },
20523
20524     /**
20525      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20526      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20527      * overridden to provide the proper feedback.
20528      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20529      * {@link #getTargetFromEvent} for this node)
20530      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20531      * @param {Event} e The event
20532      * @param {Object} data An object containing arbitrary data supplied by the drag source
20533      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20534      * underlying {@link Roo.dd.StatusProxy} can be updated
20535      */
20536     onNodeOver : function(n, dd, e, data){
20537         return this.dropAllowed;
20538     },
20539
20540     /**
20541      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20542      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20543      * node-specific processing if necessary.
20544      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20545      * {@link #getTargetFromEvent} for this node)
20546      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20547      * @param {Event} e The event
20548      * @param {Object} data An object containing arbitrary data supplied by the drag source
20549      */
20550     onNodeOut : function(n, dd, e, data){
20551         
20552     },
20553
20554     /**
20555      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20556      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20557      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20558      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20559      * {@link #getTargetFromEvent} for this node)
20560      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20561      * @param {Event} e The event
20562      * @param {Object} data An object containing arbitrary data supplied by the drag source
20563      * @return {Boolean} True if the drop was valid, else false
20564      */
20565     onNodeDrop : function(n, dd, e, data){
20566         return false;
20567     },
20568
20569     /**
20570      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20571      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20572      * it should be overridden to provide the proper feedback if necessary.
20573      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20574      * @param {Event} e The event
20575      * @param {Object} data An object containing arbitrary data supplied by the drag source
20576      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20577      * underlying {@link Roo.dd.StatusProxy} can be updated
20578      */
20579     onContainerOver : function(dd, e, data){
20580         return this.dropNotAllowed;
20581     },
20582
20583     /**
20584      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20585      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20586      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20587      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20588      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20589      * @param {Event} e The event
20590      * @param {Object} data An object containing arbitrary data supplied by the drag source
20591      * @return {Boolean} True if the drop was valid, else false
20592      */
20593     onContainerDrop : function(dd, e, data){
20594         return false;
20595     },
20596
20597     /**
20598      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20599      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20600      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20601      * you should override this method and provide a custom implementation.
20602      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20603      * @param {Event} e The event
20604      * @param {Object} data An object containing arbitrary data supplied by the drag source
20605      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20606      * underlying {@link Roo.dd.StatusProxy} can be updated
20607      */
20608     notifyEnter : function(dd, e, data){
20609         return this.dropNotAllowed;
20610     },
20611
20612     /**
20613      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20614      * This method will be called on every mouse movement while the drag source is over the drop zone.
20615      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20616      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20617      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20618      * registered node, it will call {@link #onContainerOver}.
20619      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20620      * @param {Event} e The event
20621      * @param {Object} data An object containing arbitrary data supplied by the drag source
20622      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20623      * underlying {@link Roo.dd.StatusProxy} can be updated
20624      */
20625     notifyOver : function(dd, e, data){
20626         var n = this.getTargetFromEvent(e);
20627         if(!n){ // not over valid drop target
20628             if(this.lastOverNode){
20629                 this.onNodeOut(this.lastOverNode, dd, e, data);
20630                 this.lastOverNode = null;
20631             }
20632             return this.onContainerOver(dd, e, data);
20633         }
20634         if(this.lastOverNode != n){
20635             if(this.lastOverNode){
20636                 this.onNodeOut(this.lastOverNode, dd, e, data);
20637             }
20638             this.onNodeEnter(n, dd, e, data);
20639             this.lastOverNode = n;
20640         }
20641         return this.onNodeOver(n, dd, e, data);
20642     },
20643
20644     /**
20645      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20646      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20647      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20648      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20649      * @param {Event} e The event
20650      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20651      */
20652     notifyOut : function(dd, e, data){
20653         if(this.lastOverNode){
20654             this.onNodeOut(this.lastOverNode, dd, e, data);
20655             this.lastOverNode = null;
20656         }
20657     },
20658
20659     /**
20660      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20661      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20662      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20663      * otherwise it will call {@link #onContainerDrop}.
20664      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20665      * @param {Event} e The event
20666      * @param {Object} data An object containing arbitrary data supplied by the drag source
20667      * @return {Boolean} True if the drop was valid, else false
20668      */
20669     notifyDrop : function(dd, e, data){
20670         if(this.lastOverNode){
20671             this.onNodeOut(this.lastOverNode, dd, e, data);
20672             this.lastOverNode = null;
20673         }
20674         var n = this.getTargetFromEvent(e);
20675         return n ?
20676             this.onNodeDrop(n, dd, e, data) :
20677             this.onContainerDrop(dd, e, data);
20678     },
20679
20680     // private
20681     triggerCacheRefresh : function(){
20682         Roo.dd.DDM.refreshCache(this.groups);
20683     }  
20684 });/*
20685  * Based on:
20686  * Ext JS Library 1.1.1
20687  * Copyright(c) 2006-2007, Ext JS, LLC.
20688  *
20689  * Originally Released Under LGPL - original licence link has changed is not relivant.
20690  *
20691  * Fork - LGPL
20692  * <script type="text/javascript">
20693  */
20694
20695
20696 /**
20697  * @class Roo.data.SortTypes
20698  * @singleton
20699  * Defines the default sorting (casting?) comparison functions used when sorting data.
20700  */
20701 Roo.data.SortTypes = {
20702     /**
20703      * Default sort that does nothing
20704      * @param {Mixed} s The value being converted
20705      * @return {Mixed} The comparison value
20706      */
20707     none : function(s){
20708         return s;
20709     },
20710     
20711     /**
20712      * The regular expression used to strip tags
20713      * @type {RegExp}
20714      * @property
20715      */
20716     stripTagsRE : /<\/?[^>]+>/gi,
20717     
20718     /**
20719      * Strips all HTML tags to sort on text only
20720      * @param {Mixed} s The value being converted
20721      * @return {String} The comparison value
20722      */
20723     asText : function(s){
20724         return String(s).replace(this.stripTagsRE, "");
20725     },
20726     
20727     /**
20728      * Strips all HTML tags to sort on text only - Case insensitive
20729      * @param {Mixed} s The value being converted
20730      * @return {String} The comparison value
20731      */
20732     asUCText : function(s){
20733         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20734     },
20735     
20736     /**
20737      * Case insensitive string
20738      * @param {Mixed} s The value being converted
20739      * @return {String} The comparison value
20740      */
20741     asUCString : function(s) {
20742         return String(s).toUpperCase();
20743     },
20744     
20745     /**
20746      * Date sorting
20747      * @param {Mixed} s The value being converted
20748      * @return {Number} The comparison value
20749      */
20750     asDate : function(s) {
20751         if(!s){
20752             return 0;
20753         }
20754         if(s instanceof Date){
20755             return s.getTime();
20756         }
20757         return Date.parse(String(s));
20758     },
20759     
20760     /**
20761      * Float sorting
20762      * @param {Mixed} s The value being converted
20763      * @return {Float} The comparison value
20764      */
20765     asFloat : function(s) {
20766         var val = parseFloat(String(s).replace(/,/g, ""));
20767         if(isNaN(val)) val = 0;
20768         return val;
20769     },
20770     
20771     /**
20772      * Integer sorting
20773      * @param {Mixed} s The value being converted
20774      * @return {Number} The comparison value
20775      */
20776     asInt : function(s) {
20777         var val = parseInt(String(s).replace(/,/g, ""));
20778         if(isNaN(val)) val = 0;
20779         return val;
20780     }
20781 };/*
20782  * Based on:
20783  * Ext JS Library 1.1.1
20784  * Copyright(c) 2006-2007, Ext JS, LLC.
20785  *
20786  * Originally Released Under LGPL - original licence link has changed is not relivant.
20787  *
20788  * Fork - LGPL
20789  * <script type="text/javascript">
20790  */
20791
20792 /**
20793 * @class Roo.data.Record
20794  * Instances of this class encapsulate both record <em>definition</em> information, and record
20795  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20796  * to access Records cached in an {@link Roo.data.Store} object.<br>
20797  * <p>
20798  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20799  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20800  * objects.<br>
20801  * <p>
20802  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20803  * @constructor
20804  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20805  * {@link #create}. The parameters are the same.
20806  * @param {Array} data An associative Array of data values keyed by the field name.
20807  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20808  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20809  * not specified an integer id is generated.
20810  */
20811 Roo.data.Record = function(data, id){
20812     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20813     this.data = data;
20814 };
20815
20816 /**
20817  * Generate a constructor for a specific record layout.
20818  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20819  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20820  * Each field definition object may contain the following properties: <ul>
20821  * <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,
20822  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20823  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20824  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20825  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20826  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20827  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20828  * this may be omitted.</p></li>
20829  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20830  * <ul><li>auto (Default, implies no conversion)</li>
20831  * <li>string</li>
20832  * <li>int</li>
20833  * <li>float</li>
20834  * <li>boolean</li>
20835  * <li>date</li></ul></p></li>
20836  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20837  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20838  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20839  * by the Reader into an object that will be stored in the Record. It is passed the
20840  * following parameters:<ul>
20841  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20842  * </ul></p></li>
20843  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20844  * </ul>
20845  * <br>usage:<br><pre><code>
20846 var TopicRecord = Roo.data.Record.create(
20847     {name: 'title', mapping: 'topic_title'},
20848     {name: 'author', mapping: 'username'},
20849     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20850     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20851     {name: 'lastPoster', mapping: 'user2'},
20852     {name: 'excerpt', mapping: 'post_text'}
20853 );
20854
20855 var myNewRecord = new TopicRecord({
20856     title: 'Do my job please',
20857     author: 'noobie',
20858     totalPosts: 1,
20859     lastPost: new Date(),
20860     lastPoster: 'Animal',
20861     excerpt: 'No way dude!'
20862 });
20863 myStore.add(myNewRecord);
20864 </code></pre>
20865  * @method create
20866  * @static
20867  */
20868 Roo.data.Record.create = function(o){
20869     var f = function(){
20870         f.superclass.constructor.apply(this, arguments);
20871     };
20872     Roo.extend(f, Roo.data.Record);
20873     var p = f.prototype;
20874     p.fields = new Roo.util.MixedCollection(false, function(field){
20875         return field.name;
20876     });
20877     for(var i = 0, len = o.length; i < len; i++){
20878         p.fields.add(new Roo.data.Field(o[i]));
20879     }
20880     f.getField = function(name){
20881         return p.fields.get(name);  
20882     };
20883     return f;
20884 };
20885
20886 Roo.data.Record.AUTO_ID = 1000;
20887 Roo.data.Record.EDIT = 'edit';
20888 Roo.data.Record.REJECT = 'reject';
20889 Roo.data.Record.COMMIT = 'commit';
20890
20891 Roo.data.Record.prototype = {
20892     /**
20893      * Readonly flag - true if this record has been modified.
20894      * @type Boolean
20895      */
20896     dirty : false,
20897     editing : false,
20898     error: null,
20899     modified: null,
20900
20901     // private
20902     join : function(store){
20903         this.store = store;
20904     },
20905
20906     /**
20907      * Set the named field to the specified value.
20908      * @param {String} name The name of the field to set.
20909      * @param {Object} value The value to set the field to.
20910      */
20911     set : function(name, value){
20912         if(this.data[name] == value){
20913             return;
20914         }
20915         this.dirty = true;
20916         if(!this.modified){
20917             this.modified = {};
20918         }
20919         if(typeof this.modified[name] == 'undefined'){
20920             this.modified[name] = this.data[name];
20921         }
20922         this.data[name] = value;
20923         if(!this.editing && this.store){
20924             this.store.afterEdit(this);
20925         }       
20926     },
20927
20928     /**
20929      * Get the value of the named field.
20930      * @param {String} name The name of the field to get the value of.
20931      * @return {Object} The value of the field.
20932      */
20933     get : function(name){
20934         return this.data[name]; 
20935     },
20936
20937     // private
20938     beginEdit : function(){
20939         this.editing = true;
20940         this.modified = {}; 
20941     },
20942
20943     // private
20944     cancelEdit : function(){
20945         this.editing = false;
20946         delete this.modified;
20947     },
20948
20949     // private
20950     endEdit : function(){
20951         this.editing = false;
20952         if(this.dirty && this.store){
20953             this.store.afterEdit(this);
20954         }
20955     },
20956
20957     /**
20958      * Usually called by the {@link Roo.data.Store} which owns the Record.
20959      * Rejects all changes made to the Record since either creation, or the last commit operation.
20960      * Modified fields are reverted to their original values.
20961      * <p>
20962      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20963      * of reject operations.
20964      */
20965     reject : function(){
20966         var m = this.modified;
20967         for(var n in m){
20968             if(typeof m[n] != "function"){
20969                 this.data[n] = m[n];
20970             }
20971         }
20972         this.dirty = false;
20973         delete this.modified;
20974         this.editing = false;
20975         if(this.store){
20976             this.store.afterReject(this);
20977         }
20978     },
20979
20980     /**
20981      * Usually called by the {@link Roo.data.Store} which owns the Record.
20982      * Commits all changes made to the Record since either creation, or the last commit operation.
20983      * <p>
20984      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20985      * of commit operations.
20986      */
20987     commit : function(){
20988         this.dirty = false;
20989         delete this.modified;
20990         this.editing = false;
20991         if(this.store){
20992             this.store.afterCommit(this);
20993         }
20994     },
20995
20996     // private
20997     hasError : function(){
20998         return this.error != null;
20999     },
21000
21001     // private
21002     clearError : function(){
21003         this.error = null;
21004     },
21005
21006     /**
21007      * Creates a copy of this record.
21008      * @param {String} id (optional) A new record id if you don't want to use this record's id
21009      * @return {Record}
21010      */
21011     copy : function(newId) {
21012         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21013     }
21014 };/*
21015  * Based on:
21016  * Ext JS Library 1.1.1
21017  * Copyright(c) 2006-2007, Ext JS, LLC.
21018  *
21019  * Originally Released Under LGPL - original licence link has changed is not relivant.
21020  *
21021  * Fork - LGPL
21022  * <script type="text/javascript">
21023  */
21024
21025
21026
21027 /**
21028  * @class Roo.data.Store
21029  * @extends Roo.util.Observable
21030  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21031  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21032  * <p>
21033  * 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
21034  * has no knowledge of the format of the data returned by the Proxy.<br>
21035  * <p>
21036  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21037  * instances from the data object. These records are cached and made available through accessor functions.
21038  * @constructor
21039  * Creates a new Store.
21040  * @param {Object} config A config object containing the objects needed for the Store to access data,
21041  * and read the data into Records.
21042  */
21043 Roo.data.Store = function(config){
21044     this.data = new Roo.util.MixedCollection(false);
21045     this.data.getKey = function(o){
21046         return o.id;
21047     };
21048     this.baseParams = {};
21049     // private
21050     this.paramNames = {
21051         "start" : "start",
21052         "limit" : "limit",
21053         "sort" : "sort",
21054         "dir" : "dir",
21055         "multisort" : "_multisort"
21056     };
21057
21058     if(config && config.data){
21059         this.inlineData = config.data;
21060         delete config.data;
21061     }
21062
21063     Roo.apply(this, config);
21064     
21065     if(this.reader){ // reader passed
21066         this.reader = Roo.factory(this.reader, Roo.data);
21067         this.reader.xmodule = this.xmodule || false;
21068         if(!this.recordType){
21069             this.recordType = this.reader.recordType;
21070         }
21071         if(this.reader.onMetaChange){
21072             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21073         }
21074     }
21075
21076     if(this.recordType){
21077         this.fields = this.recordType.prototype.fields;
21078     }
21079     this.modified = [];
21080
21081     this.addEvents({
21082         /**
21083          * @event datachanged
21084          * Fires when the data cache has changed, and a widget which is using this Store
21085          * as a Record cache should refresh its view.
21086          * @param {Store} this
21087          */
21088         datachanged : true,
21089         /**
21090          * @event metachange
21091          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21092          * @param {Store} this
21093          * @param {Object} meta The JSON metadata
21094          */
21095         metachange : true,
21096         /**
21097          * @event add
21098          * Fires when Records have been added to the Store
21099          * @param {Store} this
21100          * @param {Roo.data.Record[]} records The array of Records added
21101          * @param {Number} index The index at which the record(s) were added
21102          */
21103         add : true,
21104         /**
21105          * @event remove
21106          * Fires when a Record has been removed from the Store
21107          * @param {Store} this
21108          * @param {Roo.data.Record} record The Record that was removed
21109          * @param {Number} index The index at which the record was removed
21110          */
21111         remove : true,
21112         /**
21113          * @event update
21114          * Fires when a Record has been updated
21115          * @param {Store} this
21116          * @param {Roo.data.Record} record The Record that was updated
21117          * @param {String} operation The update operation being performed.  Value may be one of:
21118          * <pre><code>
21119  Roo.data.Record.EDIT
21120  Roo.data.Record.REJECT
21121  Roo.data.Record.COMMIT
21122          * </code></pre>
21123          */
21124         update : true,
21125         /**
21126          * @event clear
21127          * Fires when the data cache has been cleared.
21128          * @param {Store} this
21129          */
21130         clear : true,
21131         /**
21132          * @event beforeload
21133          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21134          * the load action will be canceled.
21135          * @param {Store} this
21136          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21137          */
21138         beforeload : true,
21139         /**
21140          * @event beforeloadadd
21141          * Fires after a new set of Records has been loaded.
21142          * @param {Store} this
21143          * @param {Roo.data.Record[]} records The Records that were loaded
21144          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21145          */
21146         beforeloadadd : true,
21147         /**
21148          * @event load
21149          * Fires after a new set of Records has been loaded, before they are added to the store.
21150          * @param {Store} this
21151          * @param {Roo.data.Record[]} records The Records that were loaded
21152          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21153          * @params {Object} return from reader
21154          */
21155         load : true,
21156         /**
21157          * @event loadexception
21158          * Fires if an exception occurs in the Proxy during loading.
21159          * Called with the signature of the Proxy's "loadexception" event.
21160          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21161          * 
21162          * @param {Proxy} 
21163          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21164          * @param {Object} load options 
21165          * @param {Object} jsonData from your request (normally this contains the Exception)
21166          */
21167         loadexception : true
21168     });
21169     
21170     if(this.proxy){
21171         this.proxy = Roo.factory(this.proxy, Roo.data);
21172         this.proxy.xmodule = this.xmodule || false;
21173         this.relayEvents(this.proxy,  ["loadexception"]);
21174     }
21175     this.sortToggle = {};
21176     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21177
21178     Roo.data.Store.superclass.constructor.call(this);
21179
21180     if(this.inlineData){
21181         this.loadData(this.inlineData);
21182         delete this.inlineData;
21183     }
21184 };
21185
21186 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21187      /**
21188     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21189     * without a remote query - used by combo/forms at present.
21190     */
21191     
21192     /**
21193     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21194     */
21195     /**
21196     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21197     */
21198     /**
21199     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21200     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21201     */
21202     /**
21203     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21204     * on any HTTP request
21205     */
21206     /**
21207     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21208     */
21209     /**
21210     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21211     */
21212     multiSort: false,
21213     /**
21214     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21215     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21216     */
21217     remoteSort : false,
21218
21219     /**
21220     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21221      * loaded or when a record is removed. (defaults to false).
21222     */
21223     pruneModifiedRecords : false,
21224
21225     // private
21226     lastOptions : null,
21227
21228     /**
21229      * Add Records to the Store and fires the add event.
21230      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21231      */
21232     add : function(records){
21233         records = [].concat(records);
21234         for(var i = 0, len = records.length; i < len; i++){
21235             records[i].join(this);
21236         }
21237         var index = this.data.length;
21238         this.data.addAll(records);
21239         this.fireEvent("add", this, records, index);
21240     },
21241
21242     /**
21243      * Remove a Record from the Store and fires the remove event.
21244      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21245      */
21246     remove : function(record){
21247         var index = this.data.indexOf(record);
21248         this.data.removeAt(index);
21249         if(this.pruneModifiedRecords){
21250             this.modified.remove(record);
21251         }
21252         this.fireEvent("remove", this, record, index);
21253     },
21254
21255     /**
21256      * Remove all Records from the Store and fires the clear event.
21257      */
21258     removeAll : function(){
21259         this.data.clear();
21260         if(this.pruneModifiedRecords){
21261             this.modified = [];
21262         }
21263         this.fireEvent("clear", this);
21264     },
21265
21266     /**
21267      * Inserts Records to the Store at the given index and fires the add event.
21268      * @param {Number} index The start index at which to insert the passed Records.
21269      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21270      */
21271     insert : function(index, records){
21272         records = [].concat(records);
21273         for(var i = 0, len = records.length; i < len; i++){
21274             this.data.insert(index, records[i]);
21275             records[i].join(this);
21276         }
21277         this.fireEvent("add", this, records, index);
21278     },
21279
21280     /**
21281      * Get the index within the cache of the passed Record.
21282      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21283      * @return {Number} The index of the passed Record. Returns -1 if not found.
21284      */
21285     indexOf : function(record){
21286         return this.data.indexOf(record);
21287     },
21288
21289     /**
21290      * Get the index within the cache of the Record with the passed id.
21291      * @param {String} id The id of the Record to find.
21292      * @return {Number} The index of the Record. Returns -1 if not found.
21293      */
21294     indexOfId : function(id){
21295         return this.data.indexOfKey(id);
21296     },
21297
21298     /**
21299      * Get the Record with the specified id.
21300      * @param {String} id The id of the Record to find.
21301      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21302      */
21303     getById : function(id){
21304         return this.data.key(id);
21305     },
21306
21307     /**
21308      * Get the Record at the specified index.
21309      * @param {Number} index The index of the Record to find.
21310      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21311      */
21312     getAt : function(index){
21313         return this.data.itemAt(index);
21314     },
21315
21316     /**
21317      * Returns a range of Records between specified indices.
21318      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21319      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21320      * @return {Roo.data.Record[]} An array of Records
21321      */
21322     getRange : function(start, end){
21323         return this.data.getRange(start, end);
21324     },
21325
21326     // private
21327     storeOptions : function(o){
21328         o = Roo.apply({}, o);
21329         delete o.callback;
21330         delete o.scope;
21331         this.lastOptions = o;
21332     },
21333
21334     /**
21335      * Loads the Record cache from the configured Proxy using the configured Reader.
21336      * <p>
21337      * If using remote paging, then the first load call must specify the <em>start</em>
21338      * and <em>limit</em> properties in the options.params property to establish the initial
21339      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21340      * <p>
21341      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21342      * and this call will return before the new data has been loaded. Perform any post-processing
21343      * in a callback function, or in a "load" event handler.</strong>
21344      * <p>
21345      * @param {Object} options An object containing properties which control loading options:<ul>
21346      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21347      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21348      * passed the following arguments:<ul>
21349      * <li>r : Roo.data.Record[]</li>
21350      * <li>options: Options object from the load call</li>
21351      * <li>success: Boolean success indicator</li></ul></li>
21352      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21353      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21354      * </ul>
21355      */
21356     load : function(options){
21357         options = options || {};
21358         if(this.fireEvent("beforeload", this, options) !== false){
21359             this.storeOptions(options);
21360             var p = Roo.apply(options.params || {}, this.baseParams);
21361             // if meta was not loaded from remote source.. try requesting it.
21362             if (!this.reader.metaFromRemote) {
21363                 p._requestMeta = 1;
21364             }
21365             if(this.sortInfo && this.remoteSort){
21366                 var pn = this.paramNames;
21367                 p[pn["sort"]] = this.sortInfo.field;
21368                 p[pn["dir"]] = this.sortInfo.direction;
21369             }
21370             if (this.multiSort) {
21371                 var pn = this.paramNames;
21372                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21373             }
21374             
21375             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21376         }
21377     },
21378
21379     /**
21380      * Reloads the Record cache from the configured Proxy using the configured Reader and
21381      * the options from the last load operation performed.
21382      * @param {Object} options (optional) An object containing properties which may override the options
21383      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21384      * the most recently used options are reused).
21385      */
21386     reload : function(options){
21387         this.load(Roo.applyIf(options||{}, this.lastOptions));
21388     },
21389
21390     // private
21391     // Called as a callback by the Reader during a load operation.
21392     loadRecords : function(o, options, success){
21393         if(!o || success === false){
21394             if(success !== false){
21395                 this.fireEvent("load", this, [], options, o);
21396             }
21397             if(options.callback){
21398                 options.callback.call(options.scope || this, [], options, false);
21399             }
21400             return;
21401         }
21402         // if data returned failure - throw an exception.
21403         if (o.success === false) {
21404             // show a message if no listener is registered.
21405             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21406                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21407             }
21408             // loadmask wil be hooked into this..
21409             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21410             return;
21411         }
21412         var r = o.records, t = o.totalRecords || r.length;
21413         
21414         this.fireEvent("beforeloadadd", this, r, options, o);
21415         
21416         if(!options || options.add !== true){
21417             if(this.pruneModifiedRecords){
21418                 this.modified = [];
21419             }
21420             for(var i = 0, len = r.length; i < len; i++){
21421                 r[i].join(this);
21422             }
21423             if(this.snapshot){
21424                 this.data = this.snapshot;
21425                 delete this.snapshot;
21426             }
21427             this.data.clear();
21428             this.data.addAll(r);
21429             this.totalLength = t;
21430             this.applySort();
21431             this.fireEvent("datachanged", this);
21432         }else{
21433             this.totalLength = Math.max(t, this.data.length+r.length);
21434             this.add(r);
21435         }
21436         this.fireEvent("load", this, r, options, o);
21437         if(options.callback){
21438             options.callback.call(options.scope || this, r, options, true);
21439         }
21440     },
21441
21442
21443     /**
21444      * Loads data from a passed data block. A Reader which understands the format of the data
21445      * must have been configured in the constructor.
21446      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21447      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21448      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21449      */
21450     loadData : function(o, append){
21451         var r = this.reader.readRecords(o);
21452         this.loadRecords(r, {add: append}, true);
21453     },
21454
21455     /**
21456      * Gets the number of cached records.
21457      * <p>
21458      * <em>If using paging, this may not be the total size of the dataset. If the data object
21459      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21460      * the data set size</em>
21461      */
21462     getCount : function(){
21463         return this.data.length || 0;
21464     },
21465
21466     /**
21467      * Gets the total number of records in the dataset as returned by the server.
21468      * <p>
21469      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21470      * the dataset size</em>
21471      */
21472     getTotalCount : function(){
21473         return this.totalLength || 0;
21474     },
21475
21476     /**
21477      * Returns the sort state of the Store as an object with two properties:
21478      * <pre><code>
21479  field {String} The name of the field by which the Records are sorted
21480  direction {String} The sort order, "ASC" or "DESC"
21481      * </code></pre>
21482      */
21483     getSortState : function(){
21484         return this.sortInfo;
21485     },
21486
21487     // private
21488     applySort : function(){
21489         if(this.sortInfo && !this.remoteSort){
21490             var s = this.sortInfo, f = s.field;
21491             var st = this.fields.get(f).sortType;
21492             var fn = function(r1, r2){
21493                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21494                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21495             };
21496             this.data.sort(s.direction, fn);
21497             if(this.snapshot && this.snapshot != this.data){
21498                 this.snapshot.sort(s.direction, fn);
21499             }
21500         }
21501     },
21502
21503     /**
21504      * Sets the default sort column and order to be used by the next load operation.
21505      * @param {String} fieldName The name of the field to sort by.
21506      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21507      */
21508     setDefaultSort : function(field, dir){
21509         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21510     },
21511
21512     /**
21513      * Sort the Records.
21514      * If remote sorting is used, the sort is performed on the server, and the cache is
21515      * reloaded. If local sorting is used, the cache is sorted internally.
21516      * @param {String} fieldName The name of the field to sort by.
21517      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21518      */
21519     sort : function(fieldName, dir){
21520         var f = this.fields.get(fieldName);
21521         if(!dir){
21522             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21523             
21524             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21525                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21526             }else{
21527                 dir = f.sortDir;
21528             }
21529         }
21530         this.sortToggle[f.name] = dir;
21531         this.sortInfo = {field: f.name, direction: dir};
21532         if(!this.remoteSort){
21533             this.applySort();
21534             this.fireEvent("datachanged", this);
21535         }else{
21536             this.load(this.lastOptions);
21537         }
21538     },
21539
21540     /**
21541      * Calls the specified function for each of the Records in the cache.
21542      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21543      * Returning <em>false</em> aborts and exits the iteration.
21544      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21545      */
21546     each : function(fn, scope){
21547         this.data.each(fn, scope);
21548     },
21549
21550     /**
21551      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21552      * (e.g., during paging).
21553      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21554      */
21555     getModifiedRecords : function(){
21556         return this.modified;
21557     },
21558
21559     // private
21560     createFilterFn : function(property, value, anyMatch){
21561         if(!value.exec){ // not a regex
21562             value = String(value);
21563             if(value.length == 0){
21564                 return false;
21565             }
21566             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21567         }
21568         return function(r){
21569             return value.test(r.data[property]);
21570         };
21571     },
21572
21573     /**
21574      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21575      * @param {String} property A field on your records
21576      * @param {Number} start The record index to start at (defaults to 0)
21577      * @param {Number} end The last record index to include (defaults to length - 1)
21578      * @return {Number} The sum
21579      */
21580     sum : function(property, start, end){
21581         var rs = this.data.items, v = 0;
21582         start = start || 0;
21583         end = (end || end === 0) ? end : rs.length-1;
21584
21585         for(var i = start; i <= end; i++){
21586             v += (rs[i].data[property] || 0);
21587         }
21588         return v;
21589     },
21590
21591     /**
21592      * Filter the records by a specified property.
21593      * @param {String} field A field on your records
21594      * @param {String/RegExp} value Either a string that the field
21595      * should start with or a RegExp to test against the field
21596      * @param {Boolean} anyMatch True to match any part not just the beginning
21597      */
21598     filter : function(property, value, anyMatch){
21599         var fn = this.createFilterFn(property, value, anyMatch);
21600         return fn ? this.filterBy(fn) : this.clearFilter();
21601     },
21602
21603     /**
21604      * Filter by a function. The specified function will be called with each
21605      * record in this data source. If the function returns true the record is included,
21606      * otherwise it is filtered.
21607      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21608      * @param {Object} scope (optional) The scope of the function (defaults to this)
21609      */
21610     filterBy : function(fn, scope){
21611         this.snapshot = this.snapshot || this.data;
21612         this.data = this.queryBy(fn, scope||this);
21613         this.fireEvent("datachanged", this);
21614     },
21615
21616     /**
21617      * Query the records by a specified property.
21618      * @param {String} field A field on your records
21619      * @param {String/RegExp} value Either a string that the field
21620      * should start with or a RegExp to test against the field
21621      * @param {Boolean} anyMatch True to match any part not just the beginning
21622      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21623      */
21624     query : function(property, value, anyMatch){
21625         var fn = this.createFilterFn(property, value, anyMatch);
21626         return fn ? this.queryBy(fn) : this.data.clone();
21627     },
21628
21629     /**
21630      * Query by a function. The specified function will be called with each
21631      * record in this data source. If the function returns true the record is included
21632      * in the results.
21633      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21634      * @param {Object} scope (optional) The scope of the function (defaults to this)
21635       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21636      **/
21637     queryBy : function(fn, scope){
21638         var data = this.snapshot || this.data;
21639         return data.filterBy(fn, scope||this);
21640     },
21641
21642     /**
21643      * Collects unique values for a particular dataIndex from this store.
21644      * @param {String} dataIndex The property to collect
21645      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21646      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21647      * @return {Array} An array of the unique values
21648      **/
21649     collect : function(dataIndex, allowNull, bypassFilter){
21650         var d = (bypassFilter === true && this.snapshot) ?
21651                 this.snapshot.items : this.data.items;
21652         var v, sv, r = [], l = {};
21653         for(var i = 0, len = d.length; i < len; i++){
21654             v = d[i].data[dataIndex];
21655             sv = String(v);
21656             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21657                 l[sv] = true;
21658                 r[r.length] = v;
21659             }
21660         }
21661         return r;
21662     },
21663
21664     /**
21665      * Revert to a view of the Record cache with no filtering applied.
21666      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21667      */
21668     clearFilter : function(suppressEvent){
21669         if(this.snapshot && this.snapshot != this.data){
21670             this.data = this.snapshot;
21671             delete this.snapshot;
21672             if(suppressEvent !== true){
21673                 this.fireEvent("datachanged", this);
21674             }
21675         }
21676     },
21677
21678     // private
21679     afterEdit : function(record){
21680         if(this.modified.indexOf(record) == -1){
21681             this.modified.push(record);
21682         }
21683         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21684     },
21685     
21686     // private
21687     afterReject : function(record){
21688         this.modified.remove(record);
21689         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21690     },
21691
21692     // private
21693     afterCommit : function(record){
21694         this.modified.remove(record);
21695         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21696     },
21697
21698     /**
21699      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21700      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21701      */
21702     commitChanges : function(){
21703         var m = this.modified.slice(0);
21704         this.modified = [];
21705         for(var i = 0, len = m.length; i < len; i++){
21706             m[i].commit();
21707         }
21708     },
21709
21710     /**
21711      * Cancel outstanding changes on all changed records.
21712      */
21713     rejectChanges : function(){
21714         var m = this.modified.slice(0);
21715         this.modified = [];
21716         for(var i = 0, len = m.length; i < len; i++){
21717             m[i].reject();
21718         }
21719     },
21720
21721     onMetaChange : function(meta, rtype, o){
21722         this.recordType = rtype;
21723         this.fields = rtype.prototype.fields;
21724         delete this.snapshot;
21725         this.sortInfo = meta.sortInfo || this.sortInfo;
21726         this.modified = [];
21727         this.fireEvent('metachange', this, this.reader.meta);
21728     }
21729 });/*
21730  * Based on:
21731  * Ext JS Library 1.1.1
21732  * Copyright(c) 2006-2007, Ext JS, LLC.
21733  *
21734  * Originally Released Under LGPL - original licence link has changed is not relivant.
21735  *
21736  * Fork - LGPL
21737  * <script type="text/javascript">
21738  */
21739
21740 /**
21741  * @class Roo.data.SimpleStore
21742  * @extends Roo.data.Store
21743  * Small helper class to make creating Stores from Array data easier.
21744  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21745  * @cfg {Array} fields An array of field definition objects, or field name strings.
21746  * @cfg {Array} data The multi-dimensional array of data
21747  * @constructor
21748  * @param {Object} config
21749  */
21750 Roo.data.SimpleStore = function(config){
21751     Roo.data.SimpleStore.superclass.constructor.call(this, {
21752         isLocal : true,
21753         reader: new Roo.data.ArrayReader({
21754                 id: config.id
21755             },
21756             Roo.data.Record.create(config.fields)
21757         ),
21758         proxy : new Roo.data.MemoryProxy(config.data)
21759     });
21760     this.load();
21761 };
21762 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21763  * Based on:
21764  * Ext JS Library 1.1.1
21765  * Copyright(c) 2006-2007, Ext JS, LLC.
21766  *
21767  * Originally Released Under LGPL - original licence link has changed is not relivant.
21768  *
21769  * Fork - LGPL
21770  * <script type="text/javascript">
21771  */
21772
21773 /**
21774 /**
21775  * @extends Roo.data.Store
21776  * @class Roo.data.JsonStore
21777  * Small helper class to make creating Stores for JSON data easier. <br/>
21778 <pre><code>
21779 var store = new Roo.data.JsonStore({
21780     url: 'get-images.php',
21781     root: 'images',
21782     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21783 });
21784 </code></pre>
21785  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21786  * JsonReader and HttpProxy (unless inline data is provided).</b>
21787  * @cfg {Array} fields An array of field definition objects, or field name strings.
21788  * @constructor
21789  * @param {Object} config
21790  */
21791 Roo.data.JsonStore = function(c){
21792     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21793         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21794         reader: new Roo.data.JsonReader(c, c.fields)
21795     }));
21796 };
21797 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21798  * Based on:
21799  * Ext JS Library 1.1.1
21800  * Copyright(c) 2006-2007, Ext JS, LLC.
21801  *
21802  * Originally Released Under LGPL - original licence link has changed is not relivant.
21803  *
21804  * Fork - LGPL
21805  * <script type="text/javascript">
21806  */
21807
21808  
21809 Roo.data.Field = function(config){
21810     if(typeof config == "string"){
21811         config = {name: config};
21812     }
21813     Roo.apply(this, config);
21814     
21815     if(!this.type){
21816         this.type = "auto";
21817     }
21818     
21819     var st = Roo.data.SortTypes;
21820     // named sortTypes are supported, here we look them up
21821     if(typeof this.sortType == "string"){
21822         this.sortType = st[this.sortType];
21823     }
21824     
21825     // set default sortType for strings and dates
21826     if(!this.sortType){
21827         switch(this.type){
21828             case "string":
21829                 this.sortType = st.asUCString;
21830                 break;
21831             case "date":
21832                 this.sortType = st.asDate;
21833                 break;
21834             default:
21835                 this.sortType = st.none;
21836         }
21837     }
21838
21839     // define once
21840     var stripRe = /[\$,%]/g;
21841
21842     // prebuilt conversion function for this field, instead of
21843     // switching every time we're reading a value
21844     if(!this.convert){
21845         var cv, dateFormat = this.dateFormat;
21846         switch(this.type){
21847             case "":
21848             case "auto":
21849             case undefined:
21850                 cv = function(v){ return v; };
21851                 break;
21852             case "string":
21853                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21854                 break;
21855             case "int":
21856                 cv = function(v){
21857                     return v !== undefined && v !== null && v !== '' ?
21858                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21859                     };
21860                 break;
21861             case "float":
21862                 cv = function(v){
21863                     return v !== undefined && v !== null && v !== '' ?
21864                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21865                     };
21866                 break;
21867             case "bool":
21868             case "boolean":
21869                 cv = function(v){ return v === true || v === "true" || v == 1; };
21870                 break;
21871             case "date":
21872                 cv = function(v){
21873                     if(!v){
21874                         return '';
21875                     }
21876                     if(v instanceof Date){
21877                         return v;
21878                     }
21879                     if(dateFormat){
21880                         if(dateFormat == "timestamp"){
21881                             return new Date(v*1000);
21882                         }
21883                         return Date.parseDate(v, dateFormat);
21884                     }
21885                     var parsed = Date.parse(v);
21886                     return parsed ? new Date(parsed) : null;
21887                 };
21888              break;
21889             
21890         }
21891         this.convert = cv;
21892     }
21893 };
21894
21895 Roo.data.Field.prototype = {
21896     dateFormat: null,
21897     defaultValue: "",
21898     mapping: null,
21899     sortType : null,
21900     sortDir : "ASC"
21901 };/*
21902  * Based on:
21903  * Ext JS Library 1.1.1
21904  * Copyright(c) 2006-2007, Ext JS, LLC.
21905  *
21906  * Originally Released Under LGPL - original licence link has changed is not relivant.
21907  *
21908  * Fork - LGPL
21909  * <script type="text/javascript">
21910  */
21911  
21912 // Base class for reading structured data from a data source.  This class is intended to be
21913 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21914
21915 /**
21916  * @class Roo.data.DataReader
21917  * Base class for reading structured data from a data source.  This class is intended to be
21918  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21919  */
21920
21921 Roo.data.DataReader = function(meta, recordType){
21922     
21923     this.meta = meta;
21924     
21925     this.recordType = recordType instanceof Array ? 
21926         Roo.data.Record.create(recordType) : recordType;
21927 };
21928
21929 Roo.data.DataReader.prototype = {
21930      /**
21931      * Create an empty record
21932      * @param {Object} data (optional) - overlay some values
21933      * @return {Roo.data.Record} record created.
21934      */
21935     newRow :  function(d) {
21936         var da =  {};
21937         this.recordType.prototype.fields.each(function(c) {
21938             switch( c.type) {
21939                 case 'int' : da[c.name] = 0; break;
21940                 case 'date' : da[c.name] = new Date(); break;
21941                 case 'float' : da[c.name] = 0.0; break;
21942                 case 'boolean' : da[c.name] = false; break;
21943                 default : da[c.name] = ""; break;
21944             }
21945             
21946         });
21947         return new this.recordType(Roo.apply(da, d));
21948     }
21949     
21950 };/*
21951  * Based on:
21952  * Ext JS Library 1.1.1
21953  * Copyright(c) 2006-2007, Ext JS, LLC.
21954  *
21955  * Originally Released Under LGPL - original licence link has changed is not relivant.
21956  *
21957  * Fork - LGPL
21958  * <script type="text/javascript">
21959  */
21960
21961 /**
21962  * @class Roo.data.DataProxy
21963  * @extends Roo.data.Observable
21964  * This class is an abstract base class for implementations which provide retrieval of
21965  * unformatted data objects.<br>
21966  * <p>
21967  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21968  * (of the appropriate type which knows how to parse the data object) to provide a block of
21969  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21970  * <p>
21971  * Custom implementations must implement the load method as described in
21972  * {@link Roo.data.HttpProxy#load}.
21973  */
21974 Roo.data.DataProxy = function(){
21975     this.addEvents({
21976         /**
21977          * @event beforeload
21978          * Fires before a network request is made to retrieve a data object.
21979          * @param {Object} This DataProxy object.
21980          * @param {Object} params The params parameter to the load function.
21981          */
21982         beforeload : true,
21983         /**
21984          * @event load
21985          * Fires before the load method's callback is called.
21986          * @param {Object} This DataProxy object.
21987          * @param {Object} o The data object.
21988          * @param {Object} arg The callback argument object passed to the load function.
21989          */
21990         load : true,
21991         /**
21992          * @event loadexception
21993          * Fires if an Exception occurs during data retrieval.
21994          * @param {Object} This DataProxy object.
21995          * @param {Object} o The data object.
21996          * @param {Object} arg The callback argument object passed to the load function.
21997          * @param {Object} e The Exception.
21998          */
21999         loadexception : true
22000     });
22001     Roo.data.DataProxy.superclass.constructor.call(this);
22002 };
22003
22004 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22005
22006     /**
22007      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22008      */
22009 /*
22010  * Based on:
22011  * Ext JS Library 1.1.1
22012  * Copyright(c) 2006-2007, Ext JS, LLC.
22013  *
22014  * Originally Released Under LGPL - original licence link has changed is not relivant.
22015  *
22016  * Fork - LGPL
22017  * <script type="text/javascript">
22018  */
22019 /**
22020  * @class Roo.data.MemoryProxy
22021  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22022  * to the Reader when its load method is called.
22023  * @constructor
22024  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22025  */
22026 Roo.data.MemoryProxy = function(data){
22027     if (data.data) {
22028         data = data.data;
22029     }
22030     Roo.data.MemoryProxy.superclass.constructor.call(this);
22031     this.data = data;
22032 };
22033
22034 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22035     /**
22036      * Load data from the requested source (in this case an in-memory
22037      * data object passed to the constructor), read the data object into
22038      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22039      * process that block using the passed callback.
22040      * @param {Object} params This parameter is not used by the MemoryProxy class.
22041      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22042      * object into a block of Roo.data.Records.
22043      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22044      * The function must be passed <ul>
22045      * <li>The Record block object</li>
22046      * <li>The "arg" argument from the load function</li>
22047      * <li>A boolean success indicator</li>
22048      * </ul>
22049      * @param {Object} scope The scope in which to call the callback
22050      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22051      */
22052     load : function(params, reader, callback, scope, arg){
22053         params = params || {};
22054         var result;
22055         try {
22056             result = reader.readRecords(this.data);
22057         }catch(e){
22058             this.fireEvent("loadexception", this, arg, null, e);
22059             callback.call(scope, null, arg, false);
22060             return;
22061         }
22062         callback.call(scope, result, arg, true);
22063     },
22064     
22065     // private
22066     update : function(params, records){
22067         
22068     }
22069 });/*
22070  * Based on:
22071  * Ext JS Library 1.1.1
22072  * Copyright(c) 2006-2007, Ext JS, LLC.
22073  *
22074  * Originally Released Under LGPL - original licence link has changed is not relivant.
22075  *
22076  * Fork - LGPL
22077  * <script type="text/javascript">
22078  */
22079 /**
22080  * @class Roo.data.HttpProxy
22081  * @extends Roo.data.DataProxy
22082  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22083  * configured to reference a certain URL.<br><br>
22084  * <p>
22085  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22086  * from which the running page was served.<br><br>
22087  * <p>
22088  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22089  * <p>
22090  * Be aware that to enable the browser to parse an XML document, the server must set
22091  * the Content-Type header in the HTTP response to "text/xml".
22092  * @constructor
22093  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22094  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22095  * will be used to make the request.
22096  */
22097 Roo.data.HttpProxy = function(conn){
22098     Roo.data.HttpProxy.superclass.constructor.call(this);
22099     // is conn a conn config or a real conn?
22100     this.conn = conn;
22101     this.useAjax = !conn || !conn.events;
22102   
22103 };
22104
22105 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22106     // thse are take from connection...
22107     
22108     /**
22109      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22110      */
22111     /**
22112      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22113      * extra parameters to each request made by this object. (defaults to undefined)
22114      */
22115     /**
22116      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22117      *  to each request made by this object. (defaults to undefined)
22118      */
22119     /**
22120      * @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)
22121      */
22122     /**
22123      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22124      */
22125      /**
22126      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22127      * @type Boolean
22128      */
22129   
22130
22131     /**
22132      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22133      * @type Boolean
22134      */
22135     /**
22136      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22137      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22138      * a finer-grained basis than the DataProxy events.
22139      */
22140     getConnection : function(){
22141         return this.useAjax ? Roo.Ajax : this.conn;
22142     },
22143
22144     /**
22145      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22146      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22147      * process that block using the passed callback.
22148      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22149      * for the request to the remote server.
22150      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22151      * object into a block of Roo.data.Records.
22152      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22153      * The function must be passed <ul>
22154      * <li>The Record block object</li>
22155      * <li>The "arg" argument from the load function</li>
22156      * <li>A boolean success indicator</li>
22157      * </ul>
22158      * @param {Object} scope The scope in which to call the callback
22159      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22160      */
22161     load : function(params, reader, callback, scope, arg){
22162         if(this.fireEvent("beforeload", this, params) !== false){
22163             var  o = {
22164                 params : params || {},
22165                 request: {
22166                     callback : callback,
22167                     scope : scope,
22168                     arg : arg
22169                 },
22170                 reader: reader,
22171                 callback : this.loadResponse,
22172                 scope: this
22173             };
22174             if(this.useAjax){
22175                 Roo.applyIf(o, this.conn);
22176                 if(this.activeRequest){
22177                     Roo.Ajax.abort(this.activeRequest);
22178                 }
22179                 this.activeRequest = Roo.Ajax.request(o);
22180             }else{
22181                 this.conn.request(o);
22182             }
22183         }else{
22184             callback.call(scope||this, null, arg, false);
22185         }
22186     },
22187
22188     // private
22189     loadResponse : function(o, success, response){
22190         delete this.activeRequest;
22191         if(!success){
22192             this.fireEvent("loadexception", this, o, response);
22193             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22194             return;
22195         }
22196         var result;
22197         try {
22198             result = o.reader.read(response);
22199         }catch(e){
22200             this.fireEvent("loadexception", this, o, response, e);
22201             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22202             return;
22203         }
22204         
22205         this.fireEvent("load", this, o, o.request.arg);
22206         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22207     },
22208
22209     // private
22210     update : function(dataSet){
22211
22212     },
22213
22214     // private
22215     updateResponse : function(dataSet){
22216
22217     }
22218 });/*
22219  * Based on:
22220  * Ext JS Library 1.1.1
22221  * Copyright(c) 2006-2007, Ext JS, LLC.
22222  *
22223  * Originally Released Under LGPL - original licence link has changed is not relivant.
22224  *
22225  * Fork - LGPL
22226  * <script type="text/javascript">
22227  */
22228
22229 /**
22230  * @class Roo.data.ScriptTagProxy
22231  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22232  * other than the originating domain of the running page.<br><br>
22233  * <p>
22234  * <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
22235  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22236  * <p>
22237  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22238  * source code that is used as the source inside a &lt;script> tag.<br><br>
22239  * <p>
22240  * In order for the browser to process the returned data, the server must wrap the data object
22241  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22242  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22243  * depending on whether the callback name was passed:
22244  * <p>
22245  * <pre><code>
22246 boolean scriptTag = false;
22247 String cb = request.getParameter("callback");
22248 if (cb != null) {
22249     scriptTag = true;
22250     response.setContentType("text/javascript");
22251 } else {
22252     response.setContentType("application/x-json");
22253 }
22254 Writer out = response.getWriter();
22255 if (scriptTag) {
22256     out.write(cb + "(");
22257 }
22258 out.print(dataBlock.toJsonString());
22259 if (scriptTag) {
22260     out.write(");");
22261 }
22262 </pre></code>
22263  *
22264  * @constructor
22265  * @param {Object} config A configuration object.
22266  */
22267 Roo.data.ScriptTagProxy = function(config){
22268     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22269     Roo.apply(this, config);
22270     this.head = document.getElementsByTagName("head")[0];
22271 };
22272
22273 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22274
22275 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22276     /**
22277      * @cfg {String} url The URL from which to request the data object.
22278      */
22279     /**
22280      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22281      */
22282     timeout : 30000,
22283     /**
22284      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22285      * the server the name of the callback function set up by the load call to process the returned data object.
22286      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22287      * javascript output which calls this named function passing the data object as its only parameter.
22288      */
22289     callbackParam : "callback",
22290     /**
22291      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22292      * name to the request.
22293      */
22294     nocache : true,
22295
22296     /**
22297      * Load data from the configured URL, read the data object into
22298      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22299      * process that block using the passed callback.
22300      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22301      * for the request to the remote server.
22302      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22303      * object into a block of Roo.data.Records.
22304      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22305      * The function must be passed <ul>
22306      * <li>The Record block object</li>
22307      * <li>The "arg" argument from the load function</li>
22308      * <li>A boolean success indicator</li>
22309      * </ul>
22310      * @param {Object} scope The scope in which to call the callback
22311      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22312      */
22313     load : function(params, reader, callback, scope, arg){
22314         if(this.fireEvent("beforeload", this, params) !== false){
22315
22316             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22317
22318             var url = this.url;
22319             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22320             if(this.nocache){
22321                 url += "&_dc=" + (new Date().getTime());
22322             }
22323             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22324             var trans = {
22325                 id : transId,
22326                 cb : "stcCallback"+transId,
22327                 scriptId : "stcScript"+transId,
22328                 params : params,
22329                 arg : arg,
22330                 url : url,
22331                 callback : callback,
22332                 scope : scope,
22333                 reader : reader
22334             };
22335             var conn = this;
22336
22337             window[trans.cb] = function(o){
22338                 conn.handleResponse(o, trans);
22339             };
22340
22341             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22342
22343             if(this.autoAbort !== false){
22344                 this.abort();
22345             }
22346
22347             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22348
22349             var script = document.createElement("script");
22350             script.setAttribute("src", url);
22351             script.setAttribute("type", "text/javascript");
22352             script.setAttribute("id", trans.scriptId);
22353             this.head.appendChild(script);
22354
22355             this.trans = trans;
22356         }else{
22357             callback.call(scope||this, null, arg, false);
22358         }
22359     },
22360
22361     // private
22362     isLoading : function(){
22363         return this.trans ? true : false;
22364     },
22365
22366     /**
22367      * Abort the current server request.
22368      */
22369     abort : function(){
22370         if(this.isLoading()){
22371             this.destroyTrans(this.trans);
22372         }
22373     },
22374
22375     // private
22376     destroyTrans : function(trans, isLoaded){
22377         this.head.removeChild(document.getElementById(trans.scriptId));
22378         clearTimeout(trans.timeoutId);
22379         if(isLoaded){
22380             window[trans.cb] = undefined;
22381             try{
22382                 delete window[trans.cb];
22383             }catch(e){}
22384         }else{
22385             // if hasn't been loaded, wait for load to remove it to prevent script error
22386             window[trans.cb] = function(){
22387                 window[trans.cb] = undefined;
22388                 try{
22389                     delete window[trans.cb];
22390                 }catch(e){}
22391             };
22392         }
22393     },
22394
22395     // private
22396     handleResponse : function(o, trans){
22397         this.trans = false;
22398         this.destroyTrans(trans, true);
22399         var result;
22400         try {
22401             result = trans.reader.readRecords(o);
22402         }catch(e){
22403             this.fireEvent("loadexception", this, o, trans.arg, e);
22404             trans.callback.call(trans.scope||window, null, trans.arg, false);
22405             return;
22406         }
22407         this.fireEvent("load", this, o, trans.arg);
22408         trans.callback.call(trans.scope||window, result, trans.arg, true);
22409     },
22410
22411     // private
22412     handleFailure : function(trans){
22413         this.trans = false;
22414         this.destroyTrans(trans, false);
22415         this.fireEvent("loadexception", this, null, trans.arg);
22416         trans.callback.call(trans.scope||window, null, trans.arg, false);
22417     }
22418 });/*
22419  * Based on:
22420  * Ext JS Library 1.1.1
22421  * Copyright(c) 2006-2007, Ext JS, LLC.
22422  *
22423  * Originally Released Under LGPL - original licence link has changed is not relivant.
22424  *
22425  * Fork - LGPL
22426  * <script type="text/javascript">
22427  */
22428
22429 /**
22430  * @class Roo.data.JsonReader
22431  * @extends Roo.data.DataReader
22432  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22433  * based on mappings in a provided Roo.data.Record constructor.
22434  * 
22435  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22436  * in the reply previously. 
22437  * 
22438  * <p>
22439  * Example code:
22440  * <pre><code>
22441 var RecordDef = Roo.data.Record.create([
22442     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22443     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22444 ]);
22445 var myReader = new Roo.data.JsonReader({
22446     totalProperty: "results",    // The property which contains the total dataset size (optional)
22447     root: "rows",                // The property which contains an Array of row objects
22448     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22449 }, RecordDef);
22450 </code></pre>
22451  * <p>
22452  * This would consume a JSON file like this:
22453  * <pre><code>
22454 { 'results': 2, 'rows': [
22455     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22456     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22457 }
22458 </code></pre>
22459  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22460  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22461  * paged from the remote server.
22462  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22463  * @cfg {String} root name of the property which contains the Array of row objects.
22464  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22465  * @constructor
22466  * Create a new JsonReader
22467  * @param {Object} meta Metadata configuration options
22468  * @param {Object} recordType Either an Array of field definition objects,
22469  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22470  */
22471 Roo.data.JsonReader = function(meta, recordType){
22472     
22473     meta = meta || {};
22474     // set some defaults:
22475     Roo.applyIf(meta, {
22476         totalProperty: 'total',
22477         successProperty : 'success',
22478         root : 'data',
22479         id : 'id'
22480     });
22481     
22482     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22483 };
22484 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22485     
22486     /**
22487      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22488      * Used by Store query builder to append _requestMeta to params.
22489      * 
22490      */
22491     metaFromRemote : false,
22492     /**
22493      * This method is only used by a DataProxy which has retrieved data from a remote server.
22494      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22495      * @return {Object} data A data block which is used by an Roo.data.Store object as
22496      * a cache of Roo.data.Records.
22497      */
22498     read : function(response){
22499         var json = response.responseText;
22500        
22501         var o = /* eval:var:o */ eval("("+json+")");
22502         if(!o) {
22503             throw {message: "JsonReader.read: Json object not found"};
22504         }
22505         
22506         if(o.metaData){
22507             
22508             delete this.ef;
22509             this.metaFromRemote = true;
22510             this.meta = o.metaData;
22511             this.recordType = Roo.data.Record.create(o.metaData.fields);
22512             this.onMetaChange(this.meta, this.recordType, o);
22513         }
22514         return this.readRecords(o);
22515     },
22516
22517     // private function a store will implement
22518     onMetaChange : function(meta, recordType, o){
22519
22520     },
22521
22522     /**
22523          * @ignore
22524          */
22525     simpleAccess: function(obj, subsc) {
22526         return obj[subsc];
22527     },
22528
22529         /**
22530          * @ignore
22531          */
22532     getJsonAccessor: function(){
22533         var re = /[\[\.]/;
22534         return function(expr) {
22535             try {
22536                 return(re.test(expr))
22537                     ? new Function("obj", "return obj." + expr)
22538                     : function(obj){
22539                         return obj[expr];
22540                     };
22541             } catch(e){}
22542             return Roo.emptyFn;
22543         };
22544     }(),
22545
22546     /**
22547      * Create a data block containing Roo.data.Records from an XML document.
22548      * @param {Object} o An object which contains an Array of row objects in the property specified
22549      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22550      * which contains the total size of the dataset.
22551      * @return {Object} data A data block which is used by an Roo.data.Store object as
22552      * a cache of Roo.data.Records.
22553      */
22554     readRecords : function(o){
22555         /**
22556          * After any data loads, the raw JSON data is available for further custom processing.
22557          * @type Object
22558          */
22559         this.o = o;
22560         var s = this.meta, Record = this.recordType,
22561             f = Record.prototype.fields, fi = f.items, fl = f.length;
22562
22563 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22564         if (!this.ef) {
22565             if(s.totalProperty) {
22566                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22567                 }
22568                 if(s.successProperty) {
22569                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22570                 }
22571                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22572                 if (s.id) {
22573                         var g = this.getJsonAccessor(s.id);
22574                         this.getId = function(rec) {
22575                                 var r = g(rec);
22576                                 return (r === undefined || r === "") ? null : r;
22577                         };
22578                 } else {
22579                         this.getId = function(){return null;};
22580                 }
22581             this.ef = [];
22582             for(var jj = 0; jj < fl; jj++){
22583                 f = fi[jj];
22584                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22585                 this.ef[jj] = this.getJsonAccessor(map);
22586             }
22587         }
22588
22589         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22590         if(s.totalProperty){
22591             var vt = parseInt(this.getTotal(o), 10);
22592             if(!isNaN(vt)){
22593                 totalRecords = vt;
22594             }
22595         }
22596         if(s.successProperty){
22597             var vs = this.getSuccess(o);
22598             if(vs === false || vs === 'false'){
22599                 success = false;
22600             }
22601         }
22602         var records = [];
22603             for(var i = 0; i < c; i++){
22604                     var n = root[i];
22605                 var values = {};
22606                 var id = this.getId(n);
22607                 for(var j = 0; j < fl; j++){
22608                     f = fi[j];
22609                 var v = this.ef[j](n);
22610                 if (!f.convert) {
22611                     Roo.log('missing convert for ' + f.name);
22612                     Roo.log(f);
22613                     continue;
22614                 }
22615                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22616                 }
22617                 var record = new Record(values, id);
22618                 record.json = n;
22619                 records[i] = record;
22620             }
22621             return {
22622             raw : o,
22623                 success : success,
22624                 records : records,
22625                 totalRecords : totalRecords
22626             };
22627     }
22628 });/*
22629  * Based on:
22630  * Ext JS Library 1.1.1
22631  * Copyright(c) 2006-2007, Ext JS, LLC.
22632  *
22633  * Originally Released Under LGPL - original licence link has changed is not relivant.
22634  *
22635  * Fork - LGPL
22636  * <script type="text/javascript">
22637  */
22638
22639 /**
22640  * @class Roo.data.XmlReader
22641  * @extends Roo.data.DataReader
22642  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22643  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22644  * <p>
22645  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22646  * header in the HTTP response must be set to "text/xml".</em>
22647  * <p>
22648  * Example code:
22649  * <pre><code>
22650 var RecordDef = Roo.data.Record.create([
22651    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22652    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22653 ]);
22654 var myReader = new Roo.data.XmlReader({
22655    totalRecords: "results", // The element which contains the total dataset size (optional)
22656    record: "row",           // The repeated element which contains row information
22657    id: "id"                 // The element within the row that provides an ID for the record (optional)
22658 }, RecordDef);
22659 </code></pre>
22660  * <p>
22661  * This would consume an XML file like this:
22662  * <pre><code>
22663 &lt;?xml?>
22664 &lt;dataset>
22665  &lt;results>2&lt;/results>
22666  &lt;row>
22667    &lt;id>1&lt;/id>
22668    &lt;name>Bill&lt;/name>
22669    &lt;occupation>Gardener&lt;/occupation>
22670  &lt;/row>
22671  &lt;row>
22672    &lt;id>2&lt;/id>
22673    &lt;name>Ben&lt;/name>
22674    &lt;occupation>Horticulturalist&lt;/occupation>
22675  &lt;/row>
22676 &lt;/dataset>
22677 </code></pre>
22678  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22679  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22680  * paged from the remote server.
22681  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22682  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22683  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22684  * a record identifier value.
22685  * @constructor
22686  * Create a new XmlReader
22687  * @param {Object} meta Metadata configuration options
22688  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22689  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22690  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22691  */
22692 Roo.data.XmlReader = function(meta, recordType){
22693     meta = meta || {};
22694     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22695 };
22696 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22697     /**
22698      * This method is only used by a DataProxy which has retrieved data from a remote server.
22699          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22700          * to contain a method called 'responseXML' that returns an XML document object.
22701      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22702      * a cache of Roo.data.Records.
22703      */
22704     read : function(response){
22705         var doc = response.responseXML;
22706         if(!doc) {
22707             throw {message: "XmlReader.read: XML Document not available"};
22708         }
22709         return this.readRecords(doc);
22710     },
22711
22712     /**
22713      * Create a data block containing Roo.data.Records from an XML document.
22714          * @param {Object} doc A parsed XML document.
22715      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22716      * a cache of Roo.data.Records.
22717      */
22718     readRecords : function(doc){
22719         /**
22720          * After any data loads/reads, the raw XML Document is available for further custom processing.
22721          * @type XMLDocument
22722          */
22723         this.xmlData = doc;
22724         var root = doc.documentElement || doc;
22725         var q = Roo.DomQuery;
22726         var recordType = this.recordType, fields = recordType.prototype.fields;
22727         var sid = this.meta.id;
22728         var totalRecords = 0, success = true;
22729         if(this.meta.totalRecords){
22730             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22731         }
22732         
22733         if(this.meta.success){
22734             var sv = q.selectValue(this.meta.success, root, true);
22735             success = sv !== false && sv !== 'false';
22736         }
22737         var records = [];
22738         var ns = q.select(this.meta.record, root);
22739         for(var i = 0, len = ns.length; i < len; i++) {
22740                 var n = ns[i];
22741                 var values = {};
22742                 var id = sid ? q.selectValue(sid, n) : undefined;
22743                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22744                     var f = fields.items[j];
22745                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22746                     v = f.convert(v);
22747                     values[f.name] = v;
22748                 }
22749                 var record = new recordType(values, id);
22750                 record.node = n;
22751                 records[records.length] = record;
22752             }
22753
22754             return {
22755                 success : success,
22756                 records : records,
22757                 totalRecords : totalRecords || records.length
22758             };
22759     }
22760 });/*
22761  * Based on:
22762  * Ext JS Library 1.1.1
22763  * Copyright(c) 2006-2007, Ext JS, LLC.
22764  *
22765  * Originally Released Under LGPL - original licence link has changed is not relivant.
22766  *
22767  * Fork - LGPL
22768  * <script type="text/javascript">
22769  */
22770
22771 /**
22772  * @class Roo.data.ArrayReader
22773  * @extends Roo.data.DataReader
22774  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22775  * Each element of that Array represents a row of data fields. The
22776  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22777  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22778  * <p>
22779  * Example code:.
22780  * <pre><code>
22781 var RecordDef = Roo.data.Record.create([
22782     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22783     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22784 ]);
22785 var myReader = new Roo.data.ArrayReader({
22786     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22787 }, RecordDef);
22788 </code></pre>
22789  * <p>
22790  * This would consume an Array like this:
22791  * <pre><code>
22792 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22793   </code></pre>
22794  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22795  * @constructor
22796  * Create a new JsonReader
22797  * @param {Object} meta Metadata configuration options.
22798  * @param {Object} recordType Either an Array of field definition objects
22799  * as specified to {@link Roo.data.Record#create},
22800  * or an {@link Roo.data.Record} object
22801  * created using {@link Roo.data.Record#create}.
22802  */
22803 Roo.data.ArrayReader = function(meta, recordType){
22804     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22805 };
22806
22807 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22808     /**
22809      * Create a data block containing Roo.data.Records from an XML document.
22810      * @param {Object} o An Array of row objects which represents the dataset.
22811      * @return {Object} data A data block which is used by an Roo.data.Store object as
22812      * a cache of Roo.data.Records.
22813      */
22814     readRecords : function(o){
22815         var sid = this.meta ? this.meta.id : null;
22816         var recordType = this.recordType, fields = recordType.prototype.fields;
22817         var records = [];
22818         var root = o;
22819             for(var i = 0; i < root.length; i++){
22820                     var n = root[i];
22821                 var values = {};
22822                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22823                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22824                 var f = fields.items[j];
22825                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22826                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22827                 v = f.convert(v);
22828                 values[f.name] = v;
22829             }
22830                 var record = new recordType(values, id);
22831                 record.json = n;
22832                 records[records.length] = record;
22833             }
22834             return {
22835                 records : records,
22836                 totalRecords : records.length
22837             };
22838     }
22839 });/*
22840  * Based on:
22841  * Ext JS Library 1.1.1
22842  * Copyright(c) 2006-2007, Ext JS, LLC.
22843  *
22844  * Originally Released Under LGPL - original licence link has changed is not relivant.
22845  *
22846  * Fork - LGPL
22847  * <script type="text/javascript">
22848  */
22849
22850
22851 /**
22852  * @class Roo.data.Tree
22853  * @extends Roo.util.Observable
22854  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22855  * in the tree have most standard DOM functionality.
22856  * @constructor
22857  * @param {Node} root (optional) The root node
22858  */
22859 Roo.data.Tree = function(root){
22860    this.nodeHash = {};
22861    /**
22862     * The root node for this tree
22863     * @type Node
22864     */
22865    this.root = null;
22866    if(root){
22867        this.setRootNode(root);
22868    }
22869    this.addEvents({
22870        /**
22871         * @event append
22872         * Fires when a new child node is appended to a node in this tree.
22873         * @param {Tree} tree The owner tree
22874         * @param {Node} parent The parent node
22875         * @param {Node} node The newly appended node
22876         * @param {Number} index The index of the newly appended node
22877         */
22878        "append" : true,
22879        /**
22880         * @event remove
22881         * Fires when a child node is removed from a node in this tree.
22882         * @param {Tree} tree The owner tree
22883         * @param {Node} parent The parent node
22884         * @param {Node} node The child node removed
22885         */
22886        "remove" : true,
22887        /**
22888         * @event move
22889         * Fires when a node is moved to a new location in the tree
22890         * @param {Tree} tree The owner tree
22891         * @param {Node} node The node moved
22892         * @param {Node} oldParent The old parent of this node
22893         * @param {Node} newParent The new parent of this node
22894         * @param {Number} index The index it was moved to
22895         */
22896        "move" : true,
22897        /**
22898         * @event insert
22899         * Fires when a new child node is inserted in a node in this tree.
22900         * @param {Tree} tree The owner tree
22901         * @param {Node} parent The parent node
22902         * @param {Node} node The child node inserted
22903         * @param {Node} refNode The child node the node was inserted before
22904         */
22905        "insert" : true,
22906        /**
22907         * @event beforeappend
22908         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22909         * @param {Tree} tree The owner tree
22910         * @param {Node} parent The parent node
22911         * @param {Node} node The child node to be appended
22912         */
22913        "beforeappend" : true,
22914        /**
22915         * @event beforeremove
22916         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22917         * @param {Tree} tree The owner tree
22918         * @param {Node} parent The parent node
22919         * @param {Node} node The child node to be removed
22920         */
22921        "beforeremove" : true,
22922        /**
22923         * @event beforemove
22924         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22925         * @param {Tree} tree The owner tree
22926         * @param {Node} node The node being moved
22927         * @param {Node} oldParent The parent of the node
22928         * @param {Node} newParent The new parent the node is moving to
22929         * @param {Number} index The index it is being moved to
22930         */
22931        "beforemove" : true,
22932        /**
22933         * @event beforeinsert
22934         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22935         * @param {Tree} tree The owner tree
22936         * @param {Node} parent The parent node
22937         * @param {Node} node The child node to be inserted
22938         * @param {Node} refNode The child node the node is being inserted before
22939         */
22940        "beforeinsert" : true
22941    });
22942
22943     Roo.data.Tree.superclass.constructor.call(this);
22944 };
22945
22946 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22947     pathSeparator: "/",
22948
22949     proxyNodeEvent : function(){
22950         return this.fireEvent.apply(this, arguments);
22951     },
22952
22953     /**
22954      * Returns the root node for this tree.
22955      * @return {Node}
22956      */
22957     getRootNode : function(){
22958         return this.root;
22959     },
22960
22961     /**
22962      * Sets the root node for this tree.
22963      * @param {Node} node
22964      * @return {Node}
22965      */
22966     setRootNode : function(node){
22967         this.root = node;
22968         node.ownerTree = this;
22969         node.isRoot = true;
22970         this.registerNode(node);
22971         return node;
22972     },
22973
22974     /**
22975      * Gets a node in this tree by its id.
22976      * @param {String} id
22977      * @return {Node}
22978      */
22979     getNodeById : function(id){
22980         return this.nodeHash[id];
22981     },
22982
22983     registerNode : function(node){
22984         this.nodeHash[node.id] = node;
22985     },
22986
22987     unregisterNode : function(node){
22988         delete this.nodeHash[node.id];
22989     },
22990
22991     toString : function(){
22992         return "[Tree"+(this.id?" "+this.id:"")+"]";
22993     }
22994 });
22995
22996 /**
22997  * @class Roo.data.Node
22998  * @extends Roo.util.Observable
22999  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23000  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23001  * @constructor
23002  * @param {Object} attributes The attributes/config for the node
23003  */
23004 Roo.data.Node = function(attributes){
23005     /**
23006      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23007      * @type {Object}
23008      */
23009     this.attributes = attributes || {};
23010     this.leaf = this.attributes.leaf;
23011     /**
23012      * The node id. @type String
23013      */
23014     this.id = this.attributes.id;
23015     if(!this.id){
23016         this.id = Roo.id(null, "ynode-");
23017         this.attributes.id = this.id;
23018     }
23019      
23020     
23021     /**
23022      * All child nodes of this node. @type Array
23023      */
23024     this.childNodes = [];
23025     if(!this.childNodes.indexOf){ // indexOf is a must
23026         this.childNodes.indexOf = function(o){
23027             for(var i = 0, len = this.length; i < len; i++){
23028                 if(this[i] == o) {
23029                     return i;
23030                 }
23031             }
23032             return -1;
23033         };
23034     }
23035     /**
23036      * The parent node for this node. @type Node
23037      */
23038     this.parentNode = null;
23039     /**
23040      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23041      */
23042     this.firstChild = null;
23043     /**
23044      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23045      */
23046     this.lastChild = null;
23047     /**
23048      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23049      */
23050     this.previousSibling = null;
23051     /**
23052      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23053      */
23054     this.nextSibling = null;
23055
23056     this.addEvents({
23057        /**
23058         * @event append
23059         * Fires when a new child node is appended
23060         * @param {Tree} tree The owner tree
23061         * @param {Node} this This node
23062         * @param {Node} node The newly appended node
23063         * @param {Number} index The index of the newly appended node
23064         */
23065        "append" : true,
23066        /**
23067         * @event remove
23068         * Fires when a child node is removed
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} this This node
23071         * @param {Node} node The removed node
23072         */
23073        "remove" : true,
23074        /**
23075         * @event move
23076         * Fires when this node is moved to a new location in the tree
23077         * @param {Tree} tree The owner tree
23078         * @param {Node} this This node
23079         * @param {Node} oldParent The old parent of this node
23080         * @param {Node} newParent The new parent of this node
23081         * @param {Number} index The index it was moved to
23082         */
23083        "move" : true,
23084        /**
23085         * @event insert
23086         * Fires when a new child node is inserted.
23087         * @param {Tree} tree The owner tree
23088         * @param {Node} this This node
23089         * @param {Node} node The child node inserted
23090         * @param {Node} refNode The child node the node was inserted before
23091         */
23092        "insert" : true,
23093        /**
23094         * @event beforeappend
23095         * Fires before a new child is appended, return false to cancel the append.
23096         * @param {Tree} tree The owner tree
23097         * @param {Node} this This node
23098         * @param {Node} node The child node to be appended
23099         */
23100        "beforeappend" : true,
23101        /**
23102         * @event beforeremove
23103         * Fires before a child is removed, return false to cancel the remove.
23104         * @param {Tree} tree The owner tree
23105         * @param {Node} this This node
23106         * @param {Node} node The child node to be removed
23107         */
23108        "beforeremove" : true,
23109        /**
23110         * @event beforemove
23111         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23112         * @param {Tree} tree The owner tree
23113         * @param {Node} this This node
23114         * @param {Node} oldParent The parent of this node
23115         * @param {Node} newParent The new parent this node is moving to
23116         * @param {Number} index The index it is being moved to
23117         */
23118        "beforemove" : true,
23119        /**
23120         * @event beforeinsert
23121         * Fires before a new child is inserted, return false to cancel the insert.
23122         * @param {Tree} tree The owner tree
23123         * @param {Node} this This node
23124         * @param {Node} node The child node to be inserted
23125         * @param {Node} refNode The child node the node is being inserted before
23126         */
23127        "beforeinsert" : true
23128    });
23129     this.listeners = this.attributes.listeners;
23130     Roo.data.Node.superclass.constructor.call(this);
23131 };
23132
23133 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23134     fireEvent : function(evtName){
23135         // first do standard event for this node
23136         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23137             return false;
23138         }
23139         // then bubble it up to the tree if the event wasn't cancelled
23140         var ot = this.getOwnerTree();
23141         if(ot){
23142             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23143                 return false;
23144             }
23145         }
23146         return true;
23147     },
23148
23149     /**
23150      * Returns true if this node is a leaf
23151      * @return {Boolean}
23152      */
23153     isLeaf : function(){
23154         return this.leaf === true;
23155     },
23156
23157     // private
23158     setFirstChild : function(node){
23159         this.firstChild = node;
23160     },
23161
23162     //private
23163     setLastChild : function(node){
23164         this.lastChild = node;
23165     },
23166
23167
23168     /**
23169      * Returns true if this node is the last child of its parent
23170      * @return {Boolean}
23171      */
23172     isLast : function(){
23173        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23174     },
23175
23176     /**
23177      * Returns true if this node is the first child of its parent
23178      * @return {Boolean}
23179      */
23180     isFirst : function(){
23181        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23182     },
23183
23184     hasChildNodes : function(){
23185         return !this.isLeaf() && this.childNodes.length > 0;
23186     },
23187
23188     /**
23189      * Insert node(s) as the last child node of this node.
23190      * @param {Node/Array} node The node or Array of nodes to append
23191      * @return {Node} The appended node if single append, or null if an array was passed
23192      */
23193     appendChild : function(node){
23194         var multi = false;
23195         if(node instanceof Array){
23196             multi = node;
23197         }else if(arguments.length > 1){
23198             multi = arguments;
23199         }
23200         // if passed an array or multiple args do them one by one
23201         if(multi){
23202             for(var i = 0, len = multi.length; i < len; i++) {
23203                 this.appendChild(multi[i]);
23204             }
23205         }else{
23206             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23207                 return false;
23208             }
23209             var index = this.childNodes.length;
23210             var oldParent = node.parentNode;
23211             // it's a move, make sure we move it cleanly
23212             if(oldParent){
23213                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23214                     return false;
23215                 }
23216                 oldParent.removeChild(node);
23217             }
23218             index = this.childNodes.length;
23219             if(index == 0){
23220                 this.setFirstChild(node);
23221             }
23222             this.childNodes.push(node);
23223             node.parentNode = this;
23224             var ps = this.childNodes[index-1];
23225             if(ps){
23226                 node.previousSibling = ps;
23227                 ps.nextSibling = node;
23228             }else{
23229                 node.previousSibling = null;
23230             }
23231             node.nextSibling = null;
23232             this.setLastChild(node);
23233             node.setOwnerTree(this.getOwnerTree());
23234             this.fireEvent("append", this.ownerTree, this, node, index);
23235             if(oldParent){
23236                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23237             }
23238             return node;
23239         }
23240     },
23241
23242     /**
23243      * Removes a child node from this node.
23244      * @param {Node} node The node to remove
23245      * @return {Node} The removed node
23246      */
23247     removeChild : function(node){
23248         var index = this.childNodes.indexOf(node);
23249         if(index == -1){
23250             return false;
23251         }
23252         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23253             return false;
23254         }
23255
23256         // remove it from childNodes collection
23257         this.childNodes.splice(index, 1);
23258
23259         // update siblings
23260         if(node.previousSibling){
23261             node.previousSibling.nextSibling = node.nextSibling;
23262         }
23263         if(node.nextSibling){
23264             node.nextSibling.previousSibling = node.previousSibling;
23265         }
23266
23267         // update child refs
23268         if(this.firstChild == node){
23269             this.setFirstChild(node.nextSibling);
23270         }
23271         if(this.lastChild == node){
23272             this.setLastChild(node.previousSibling);
23273         }
23274
23275         node.setOwnerTree(null);
23276         // clear any references from the node
23277         node.parentNode = null;
23278         node.previousSibling = null;
23279         node.nextSibling = null;
23280         this.fireEvent("remove", this.ownerTree, this, node);
23281         return node;
23282     },
23283
23284     /**
23285      * Inserts the first node before the second node in this nodes childNodes collection.
23286      * @param {Node} node The node to insert
23287      * @param {Node} refNode The node to insert before (if null the node is appended)
23288      * @return {Node} The inserted node
23289      */
23290     insertBefore : function(node, refNode){
23291         if(!refNode){ // like standard Dom, refNode can be null for append
23292             return this.appendChild(node);
23293         }
23294         // nothing to do
23295         if(node == refNode){
23296             return false;
23297         }
23298
23299         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23300             return false;
23301         }
23302         var index = this.childNodes.indexOf(refNode);
23303         var oldParent = node.parentNode;
23304         var refIndex = index;
23305
23306         // when moving internally, indexes will change after remove
23307         if(oldParent == this && this.childNodes.indexOf(node) < index){
23308             refIndex--;
23309         }
23310
23311         // it's a move, make sure we move it cleanly
23312         if(oldParent){
23313             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23314                 return false;
23315             }
23316             oldParent.removeChild(node);
23317         }
23318         if(refIndex == 0){
23319             this.setFirstChild(node);
23320         }
23321         this.childNodes.splice(refIndex, 0, node);
23322         node.parentNode = this;
23323         var ps = this.childNodes[refIndex-1];
23324         if(ps){
23325             node.previousSibling = ps;
23326             ps.nextSibling = node;
23327         }else{
23328             node.previousSibling = null;
23329         }
23330         node.nextSibling = refNode;
23331         refNode.previousSibling = node;
23332         node.setOwnerTree(this.getOwnerTree());
23333         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23334         if(oldParent){
23335             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23336         }
23337         return node;
23338     },
23339
23340     /**
23341      * Returns the child node at the specified index.
23342      * @param {Number} index
23343      * @return {Node}
23344      */
23345     item : function(index){
23346         return this.childNodes[index];
23347     },
23348
23349     /**
23350      * Replaces one child node in this node with another.
23351      * @param {Node} newChild The replacement node
23352      * @param {Node} oldChild The node to replace
23353      * @return {Node} The replaced node
23354      */
23355     replaceChild : function(newChild, oldChild){
23356         this.insertBefore(newChild, oldChild);
23357         this.removeChild(oldChild);
23358         return oldChild;
23359     },
23360
23361     /**
23362      * Returns the index of a child node
23363      * @param {Node} node
23364      * @return {Number} The index of the node or -1 if it was not found
23365      */
23366     indexOf : function(child){
23367         return this.childNodes.indexOf(child);
23368     },
23369
23370     /**
23371      * Returns the tree this node is in.
23372      * @return {Tree}
23373      */
23374     getOwnerTree : function(){
23375         // if it doesn't have one, look for one
23376         if(!this.ownerTree){
23377             var p = this;
23378             while(p){
23379                 if(p.ownerTree){
23380                     this.ownerTree = p.ownerTree;
23381                     break;
23382                 }
23383                 p = p.parentNode;
23384             }
23385         }
23386         return this.ownerTree;
23387     },
23388
23389     /**
23390      * Returns depth of this node (the root node has a depth of 0)
23391      * @return {Number}
23392      */
23393     getDepth : function(){
23394         var depth = 0;
23395         var p = this;
23396         while(p.parentNode){
23397             ++depth;
23398             p = p.parentNode;
23399         }
23400         return depth;
23401     },
23402
23403     // private
23404     setOwnerTree : function(tree){
23405         // if it's move, we need to update everyone
23406         if(tree != this.ownerTree){
23407             if(this.ownerTree){
23408                 this.ownerTree.unregisterNode(this);
23409             }
23410             this.ownerTree = tree;
23411             var cs = this.childNodes;
23412             for(var i = 0, len = cs.length; i < len; i++) {
23413                 cs[i].setOwnerTree(tree);
23414             }
23415             if(tree){
23416                 tree.registerNode(this);
23417             }
23418         }
23419     },
23420
23421     /**
23422      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23423      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23424      * @return {String} The path
23425      */
23426     getPath : function(attr){
23427         attr = attr || "id";
23428         var p = this.parentNode;
23429         var b = [this.attributes[attr]];
23430         while(p){
23431             b.unshift(p.attributes[attr]);
23432             p = p.parentNode;
23433         }
23434         var sep = this.getOwnerTree().pathSeparator;
23435         return sep + b.join(sep);
23436     },
23437
23438     /**
23439      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23440      * function call will be the scope provided or the current node. The arguments to the function
23441      * will be the args provided or the current node. If the function returns false at any point,
23442      * the bubble is stopped.
23443      * @param {Function} fn The function to call
23444      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23445      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23446      */
23447     bubble : function(fn, scope, args){
23448         var p = this;
23449         while(p){
23450             if(fn.call(scope || p, args || p) === false){
23451                 break;
23452             }
23453             p = p.parentNode;
23454         }
23455     },
23456
23457     /**
23458      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23459      * function call will be the scope provided or the current node. The arguments to the function
23460      * will be the args provided or the current node. If the function returns false at any point,
23461      * the cascade is stopped on that branch.
23462      * @param {Function} fn The function to call
23463      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23464      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23465      */
23466     cascade : function(fn, scope, args){
23467         if(fn.call(scope || this, args || this) !== false){
23468             var cs = this.childNodes;
23469             for(var i = 0, len = cs.length; i < len; i++) {
23470                 cs[i].cascade(fn, scope, args);
23471             }
23472         }
23473     },
23474
23475     /**
23476      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23477      * function call will be the scope provided or the current node. The arguments to the function
23478      * will be the args provided or the current node. If the function returns false at any point,
23479      * the iteration stops.
23480      * @param {Function} fn The function to call
23481      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23482      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23483      */
23484     eachChild : function(fn, scope, args){
23485         var cs = this.childNodes;
23486         for(var i = 0, len = cs.length; i < len; i++) {
23487                 if(fn.call(scope || this, args || cs[i]) === false){
23488                     break;
23489                 }
23490         }
23491     },
23492
23493     /**
23494      * Finds the first child that has the attribute with the specified value.
23495      * @param {String} attribute The attribute name
23496      * @param {Mixed} value The value to search for
23497      * @return {Node} The found child or null if none was found
23498      */
23499     findChild : function(attribute, value){
23500         var cs = this.childNodes;
23501         for(var i = 0, len = cs.length; i < len; i++) {
23502                 if(cs[i].attributes[attribute] == value){
23503                     return cs[i];
23504                 }
23505         }
23506         return null;
23507     },
23508
23509     /**
23510      * Finds the first child by a custom function. The child matches if the function passed
23511      * returns true.
23512      * @param {Function} fn
23513      * @param {Object} scope (optional)
23514      * @return {Node} The found child or null if none was found
23515      */
23516     findChildBy : function(fn, scope){
23517         var cs = this.childNodes;
23518         for(var i = 0, len = cs.length; i < len; i++) {
23519                 if(fn.call(scope||cs[i], cs[i]) === true){
23520                     return cs[i];
23521                 }
23522         }
23523         return null;
23524     },
23525
23526     /**
23527      * Sorts this nodes children using the supplied sort function
23528      * @param {Function} fn
23529      * @param {Object} scope (optional)
23530      */
23531     sort : function(fn, scope){
23532         var cs = this.childNodes;
23533         var len = cs.length;
23534         if(len > 0){
23535             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23536             cs.sort(sortFn);
23537             for(var i = 0; i < len; i++){
23538                 var n = cs[i];
23539                 n.previousSibling = cs[i-1];
23540                 n.nextSibling = cs[i+1];
23541                 if(i == 0){
23542                     this.setFirstChild(n);
23543                 }
23544                 if(i == len-1){
23545                     this.setLastChild(n);
23546                 }
23547             }
23548         }
23549     },
23550
23551     /**
23552      * Returns true if this node is an ancestor (at any point) of the passed node.
23553      * @param {Node} node
23554      * @return {Boolean}
23555      */
23556     contains : function(node){
23557         return node.isAncestor(this);
23558     },
23559
23560     /**
23561      * Returns true if the passed node is an ancestor (at any point) of this node.
23562      * @param {Node} node
23563      * @return {Boolean}
23564      */
23565     isAncestor : function(node){
23566         var p = this.parentNode;
23567         while(p){
23568             if(p == node){
23569                 return true;
23570             }
23571             p = p.parentNode;
23572         }
23573         return false;
23574     },
23575
23576     toString : function(){
23577         return "[Node"+(this.id?" "+this.id:"")+"]";
23578     }
23579 });/*
23580  * Based on:
23581  * Ext JS Library 1.1.1
23582  * Copyright(c) 2006-2007, Ext JS, LLC.
23583  *
23584  * Originally Released Under LGPL - original licence link has changed is not relivant.
23585  *
23586  * Fork - LGPL
23587  * <script type="text/javascript">
23588  */
23589  (function(){ 
23590 /**
23591  * @class Roo.Layer
23592  * @extends Roo.Element
23593  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23594  * automatic maintaining of shadow/shim positions.
23595  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23596  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23597  * you can pass a string with a CSS class name. False turns off the shadow.
23598  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23599  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23600  * @cfg {String} cls CSS class to add to the element
23601  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23602  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23603  * @constructor
23604  * @param {Object} config An object with config options.
23605  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23606  */
23607
23608 Roo.Layer = function(config, existingEl){
23609     config = config || {};
23610     var dh = Roo.DomHelper;
23611     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23612     if(existingEl){
23613         this.dom = Roo.getDom(existingEl);
23614     }
23615     if(!this.dom){
23616         var o = config.dh || {tag: "div", cls: "x-layer"};
23617         this.dom = dh.append(pel, o);
23618     }
23619     if(config.cls){
23620         this.addClass(config.cls);
23621     }
23622     this.constrain = config.constrain !== false;
23623     this.visibilityMode = Roo.Element.VISIBILITY;
23624     if(config.id){
23625         this.id = this.dom.id = config.id;
23626     }else{
23627         this.id = Roo.id(this.dom);
23628     }
23629     this.zindex = config.zindex || this.getZIndex();
23630     this.position("absolute", this.zindex);
23631     if(config.shadow){
23632         this.shadowOffset = config.shadowOffset || 4;
23633         this.shadow = new Roo.Shadow({
23634             offset : this.shadowOffset,
23635             mode : config.shadow
23636         });
23637     }else{
23638         this.shadowOffset = 0;
23639     }
23640     this.useShim = config.shim !== false && Roo.useShims;
23641     this.useDisplay = config.useDisplay;
23642     this.hide();
23643 };
23644
23645 var supr = Roo.Element.prototype;
23646
23647 // shims are shared among layer to keep from having 100 iframes
23648 var shims = [];
23649
23650 Roo.extend(Roo.Layer, Roo.Element, {
23651
23652     getZIndex : function(){
23653         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23654     },
23655
23656     getShim : function(){
23657         if(!this.useShim){
23658             return null;
23659         }
23660         if(this.shim){
23661             return this.shim;
23662         }
23663         var shim = shims.shift();
23664         if(!shim){
23665             shim = this.createShim();
23666             shim.enableDisplayMode('block');
23667             shim.dom.style.display = 'none';
23668             shim.dom.style.visibility = 'visible';
23669         }
23670         var pn = this.dom.parentNode;
23671         if(shim.dom.parentNode != pn){
23672             pn.insertBefore(shim.dom, this.dom);
23673         }
23674         shim.setStyle('z-index', this.getZIndex()-2);
23675         this.shim = shim;
23676         return shim;
23677     },
23678
23679     hideShim : function(){
23680         if(this.shim){
23681             this.shim.setDisplayed(false);
23682             shims.push(this.shim);
23683             delete this.shim;
23684         }
23685     },
23686
23687     disableShadow : function(){
23688         if(this.shadow){
23689             this.shadowDisabled = true;
23690             this.shadow.hide();
23691             this.lastShadowOffset = this.shadowOffset;
23692             this.shadowOffset = 0;
23693         }
23694     },
23695
23696     enableShadow : function(show){
23697         if(this.shadow){
23698             this.shadowDisabled = false;
23699             this.shadowOffset = this.lastShadowOffset;
23700             delete this.lastShadowOffset;
23701             if(show){
23702                 this.sync(true);
23703             }
23704         }
23705     },
23706
23707     // private
23708     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23709     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23710     sync : function(doShow){
23711         var sw = this.shadow;
23712         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23713             var sh = this.getShim();
23714
23715             var w = this.getWidth(),
23716                 h = this.getHeight();
23717
23718             var l = this.getLeft(true),
23719                 t = this.getTop(true);
23720
23721             if(sw && !this.shadowDisabled){
23722                 if(doShow && !sw.isVisible()){
23723                     sw.show(this);
23724                 }else{
23725                     sw.realign(l, t, w, h);
23726                 }
23727                 if(sh){
23728                     if(doShow){
23729                        sh.show();
23730                     }
23731                     // fit the shim behind the shadow, so it is shimmed too
23732                     var a = sw.adjusts, s = sh.dom.style;
23733                     s.left = (Math.min(l, l+a.l))+"px";
23734                     s.top = (Math.min(t, t+a.t))+"px";
23735                     s.width = (w+a.w)+"px";
23736                     s.height = (h+a.h)+"px";
23737                 }
23738             }else if(sh){
23739                 if(doShow){
23740                    sh.show();
23741                 }
23742                 sh.setSize(w, h);
23743                 sh.setLeftTop(l, t);
23744             }
23745             
23746         }
23747     },
23748
23749     // private
23750     destroy : function(){
23751         this.hideShim();
23752         if(this.shadow){
23753             this.shadow.hide();
23754         }
23755         this.removeAllListeners();
23756         var pn = this.dom.parentNode;
23757         if(pn){
23758             pn.removeChild(this.dom);
23759         }
23760         Roo.Element.uncache(this.id);
23761     },
23762
23763     remove : function(){
23764         this.destroy();
23765     },
23766
23767     // private
23768     beginUpdate : function(){
23769         this.updating = true;
23770     },
23771
23772     // private
23773     endUpdate : function(){
23774         this.updating = false;
23775         this.sync(true);
23776     },
23777
23778     // private
23779     hideUnders : function(negOffset){
23780         if(this.shadow){
23781             this.shadow.hide();
23782         }
23783         this.hideShim();
23784     },
23785
23786     // private
23787     constrainXY : function(){
23788         if(this.constrain){
23789             var vw = Roo.lib.Dom.getViewWidth(),
23790                 vh = Roo.lib.Dom.getViewHeight();
23791             var s = Roo.get(document).getScroll();
23792
23793             var xy = this.getXY();
23794             var x = xy[0], y = xy[1];   
23795             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23796             // only move it if it needs it
23797             var moved = false;
23798             // first validate right/bottom
23799             if((x + w) > vw+s.left){
23800                 x = vw - w - this.shadowOffset;
23801                 moved = true;
23802             }
23803             if((y + h) > vh+s.top){
23804                 y = vh - h - this.shadowOffset;
23805                 moved = true;
23806             }
23807             // then make sure top/left isn't negative
23808             if(x < s.left){
23809                 x = s.left;
23810                 moved = true;
23811             }
23812             if(y < s.top){
23813                 y = s.top;
23814                 moved = true;
23815             }
23816             if(moved){
23817                 if(this.avoidY){
23818                     var ay = this.avoidY;
23819                     if(y <= ay && (y+h) >= ay){
23820                         y = ay-h-5;   
23821                     }
23822                 }
23823                 xy = [x, y];
23824                 this.storeXY(xy);
23825                 supr.setXY.call(this, xy);
23826                 this.sync();
23827             }
23828         }
23829     },
23830
23831     isVisible : function(){
23832         return this.visible;    
23833     },
23834
23835     // private
23836     showAction : function(){
23837         this.visible = true; // track visibility to prevent getStyle calls
23838         if(this.useDisplay === true){
23839             this.setDisplayed("");
23840         }else if(this.lastXY){
23841             supr.setXY.call(this, this.lastXY);
23842         }else if(this.lastLT){
23843             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23844         }
23845     },
23846
23847     // private
23848     hideAction : function(){
23849         this.visible = false;
23850         if(this.useDisplay === true){
23851             this.setDisplayed(false);
23852         }else{
23853             this.setLeftTop(-10000,-10000);
23854         }
23855     },
23856
23857     // overridden Element method
23858     setVisible : function(v, a, d, c, e){
23859         if(v){
23860             this.showAction();
23861         }
23862         if(a && v){
23863             var cb = function(){
23864                 this.sync(true);
23865                 if(c){
23866                     c();
23867                 }
23868             }.createDelegate(this);
23869             supr.setVisible.call(this, true, true, d, cb, e);
23870         }else{
23871             if(!v){
23872                 this.hideUnders(true);
23873             }
23874             var cb = c;
23875             if(a){
23876                 cb = function(){
23877                     this.hideAction();
23878                     if(c){
23879                         c();
23880                     }
23881                 }.createDelegate(this);
23882             }
23883             supr.setVisible.call(this, v, a, d, cb, e);
23884             if(v){
23885                 this.sync(true);
23886             }else if(!a){
23887                 this.hideAction();
23888             }
23889         }
23890     },
23891
23892     storeXY : function(xy){
23893         delete this.lastLT;
23894         this.lastXY = xy;
23895     },
23896
23897     storeLeftTop : function(left, top){
23898         delete this.lastXY;
23899         this.lastLT = [left, top];
23900     },
23901
23902     // private
23903     beforeFx : function(){
23904         this.beforeAction();
23905         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23906     },
23907
23908     // private
23909     afterFx : function(){
23910         Roo.Layer.superclass.afterFx.apply(this, arguments);
23911         this.sync(this.isVisible());
23912     },
23913
23914     // private
23915     beforeAction : function(){
23916         if(!this.updating && this.shadow){
23917             this.shadow.hide();
23918         }
23919     },
23920
23921     // overridden Element method
23922     setLeft : function(left){
23923         this.storeLeftTop(left, this.getTop(true));
23924         supr.setLeft.apply(this, arguments);
23925         this.sync();
23926     },
23927
23928     setTop : function(top){
23929         this.storeLeftTop(this.getLeft(true), top);
23930         supr.setTop.apply(this, arguments);
23931         this.sync();
23932     },
23933
23934     setLeftTop : function(left, top){
23935         this.storeLeftTop(left, top);
23936         supr.setLeftTop.apply(this, arguments);
23937         this.sync();
23938     },
23939
23940     setXY : function(xy, a, d, c, e){
23941         this.fixDisplay();
23942         this.beforeAction();
23943         this.storeXY(xy);
23944         var cb = this.createCB(c);
23945         supr.setXY.call(this, xy, a, d, cb, e);
23946         if(!a){
23947             cb();
23948         }
23949     },
23950
23951     // private
23952     createCB : function(c){
23953         var el = this;
23954         return function(){
23955             el.constrainXY();
23956             el.sync(true);
23957             if(c){
23958                 c();
23959             }
23960         };
23961     },
23962
23963     // overridden Element method
23964     setX : function(x, a, d, c, e){
23965         this.setXY([x, this.getY()], a, d, c, e);
23966     },
23967
23968     // overridden Element method
23969     setY : function(y, a, d, c, e){
23970         this.setXY([this.getX(), y], a, d, c, e);
23971     },
23972
23973     // overridden Element method
23974     setSize : function(w, h, a, d, c, e){
23975         this.beforeAction();
23976         var cb = this.createCB(c);
23977         supr.setSize.call(this, w, h, a, d, cb, e);
23978         if(!a){
23979             cb();
23980         }
23981     },
23982
23983     // overridden Element method
23984     setWidth : function(w, a, d, c, e){
23985         this.beforeAction();
23986         var cb = this.createCB(c);
23987         supr.setWidth.call(this, w, a, d, cb, e);
23988         if(!a){
23989             cb();
23990         }
23991     },
23992
23993     // overridden Element method
23994     setHeight : function(h, a, d, c, e){
23995         this.beforeAction();
23996         var cb = this.createCB(c);
23997         supr.setHeight.call(this, h, a, d, cb, e);
23998         if(!a){
23999             cb();
24000         }
24001     },
24002
24003     // overridden Element method
24004     setBounds : function(x, y, w, h, a, d, c, e){
24005         this.beforeAction();
24006         var cb = this.createCB(c);
24007         if(!a){
24008             this.storeXY([x, y]);
24009             supr.setXY.call(this, [x, y]);
24010             supr.setSize.call(this, w, h, a, d, cb, e);
24011             cb();
24012         }else{
24013             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24014         }
24015         return this;
24016     },
24017     
24018     /**
24019      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24020      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24021      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24022      * @param {Number} zindex The new z-index to set
24023      * @return {this} The Layer
24024      */
24025     setZIndex : function(zindex){
24026         this.zindex = zindex;
24027         this.setStyle("z-index", zindex + 2);
24028         if(this.shadow){
24029             this.shadow.setZIndex(zindex + 1);
24030         }
24031         if(this.shim){
24032             this.shim.setStyle("z-index", zindex);
24033         }
24034     }
24035 });
24036 })();/*
24037  * Based on:
24038  * Ext JS Library 1.1.1
24039  * Copyright(c) 2006-2007, Ext JS, LLC.
24040  *
24041  * Originally Released Under LGPL - original licence link has changed is not relivant.
24042  *
24043  * Fork - LGPL
24044  * <script type="text/javascript">
24045  */
24046
24047
24048 /**
24049  * @class Roo.Shadow
24050  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24051  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24052  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24053  * @constructor
24054  * Create a new Shadow
24055  * @param {Object} config The config object
24056  */
24057 Roo.Shadow = function(config){
24058     Roo.apply(this, config);
24059     if(typeof this.mode != "string"){
24060         this.mode = this.defaultMode;
24061     }
24062     var o = this.offset, a = {h: 0};
24063     var rad = Math.floor(this.offset/2);
24064     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24065         case "drop":
24066             a.w = 0;
24067             a.l = a.t = o;
24068             a.t -= 1;
24069             if(Roo.isIE){
24070                 a.l -= this.offset + rad;
24071                 a.t -= this.offset + rad;
24072                 a.w -= rad;
24073                 a.h -= rad;
24074                 a.t += 1;
24075             }
24076         break;
24077         case "sides":
24078             a.w = (o*2);
24079             a.l = -o;
24080             a.t = o-1;
24081             if(Roo.isIE){
24082                 a.l -= (this.offset - rad);
24083                 a.t -= this.offset + rad;
24084                 a.l += 1;
24085                 a.w -= (this.offset - rad)*2;
24086                 a.w -= rad + 1;
24087                 a.h -= 1;
24088             }
24089         break;
24090         case "frame":
24091             a.w = a.h = (o*2);
24092             a.l = a.t = -o;
24093             a.t += 1;
24094             a.h -= 2;
24095             if(Roo.isIE){
24096                 a.l -= (this.offset - rad);
24097                 a.t -= (this.offset - rad);
24098                 a.l += 1;
24099                 a.w -= (this.offset + rad + 1);
24100                 a.h -= (this.offset + rad);
24101                 a.h += 1;
24102             }
24103         break;
24104     };
24105
24106     this.adjusts = a;
24107 };
24108
24109 Roo.Shadow.prototype = {
24110     /**
24111      * @cfg {String} mode
24112      * The shadow display mode.  Supports the following options:<br />
24113      * sides: Shadow displays on both sides and bottom only<br />
24114      * frame: Shadow displays equally on all four sides<br />
24115      * drop: Traditional bottom-right drop shadow (default)
24116      */
24117     /**
24118      * @cfg {String} offset
24119      * The number of pixels to offset the shadow from the element (defaults to 4)
24120      */
24121     offset: 4,
24122
24123     // private
24124     defaultMode: "drop",
24125
24126     /**
24127      * Displays the shadow under the target element
24128      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24129      */
24130     show : function(target){
24131         target = Roo.get(target);
24132         if(!this.el){
24133             this.el = Roo.Shadow.Pool.pull();
24134             if(this.el.dom.nextSibling != target.dom){
24135                 this.el.insertBefore(target);
24136             }
24137         }
24138         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24139         if(Roo.isIE){
24140             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24141         }
24142         this.realign(
24143             target.getLeft(true),
24144             target.getTop(true),
24145             target.getWidth(),
24146             target.getHeight()
24147         );
24148         this.el.dom.style.display = "block";
24149     },
24150
24151     /**
24152      * Returns true if the shadow is visible, else false
24153      */
24154     isVisible : function(){
24155         return this.el ? true : false;  
24156     },
24157
24158     /**
24159      * Direct alignment when values are already available. Show must be called at least once before
24160      * calling this method to ensure it is initialized.
24161      * @param {Number} left The target element left position
24162      * @param {Number} top The target element top position
24163      * @param {Number} width The target element width
24164      * @param {Number} height The target element height
24165      */
24166     realign : function(l, t, w, h){
24167         if(!this.el){
24168             return;
24169         }
24170         var a = this.adjusts, d = this.el.dom, s = d.style;
24171         var iea = 0;
24172         s.left = (l+a.l)+"px";
24173         s.top = (t+a.t)+"px";
24174         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24175  
24176         if(s.width != sws || s.height != shs){
24177             s.width = sws;
24178             s.height = shs;
24179             if(!Roo.isIE){
24180                 var cn = d.childNodes;
24181                 var sww = Math.max(0, (sw-12))+"px";
24182                 cn[0].childNodes[1].style.width = sww;
24183                 cn[1].childNodes[1].style.width = sww;
24184                 cn[2].childNodes[1].style.width = sww;
24185                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24186             }
24187         }
24188     },
24189
24190     /**
24191      * Hides this shadow
24192      */
24193     hide : function(){
24194         if(this.el){
24195             this.el.dom.style.display = "none";
24196             Roo.Shadow.Pool.push(this.el);
24197             delete this.el;
24198         }
24199     },
24200
24201     /**
24202      * Adjust the z-index of this shadow
24203      * @param {Number} zindex The new z-index
24204      */
24205     setZIndex : function(z){
24206         this.zIndex = z;
24207         if(this.el){
24208             this.el.setStyle("z-index", z);
24209         }
24210     }
24211 };
24212
24213 // Private utility class that manages the internal Shadow cache
24214 Roo.Shadow.Pool = function(){
24215     var p = [];
24216     var markup = Roo.isIE ?
24217                  '<div class="x-ie-shadow"></div>' :
24218                  '<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>';
24219     return {
24220         pull : function(){
24221             var sh = p.shift();
24222             if(!sh){
24223                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24224                 sh.autoBoxAdjust = false;
24225             }
24226             return sh;
24227         },
24228
24229         push : function(sh){
24230             p.push(sh);
24231         }
24232     };
24233 }();/*
24234  * Based on:
24235  * Ext JS Library 1.1.1
24236  * Copyright(c) 2006-2007, Ext JS, LLC.
24237  *
24238  * Originally Released Under LGPL - original licence link has changed is not relivant.
24239  *
24240  * Fork - LGPL
24241  * <script type="text/javascript">
24242  */
24243
24244
24245 /**
24246  * @class Roo.SplitBar
24247  * @extends Roo.util.Observable
24248  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24249  * <br><br>
24250  * Usage:
24251  * <pre><code>
24252 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24253                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24254 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24255 split.minSize = 100;
24256 split.maxSize = 600;
24257 split.animate = true;
24258 split.on('moved', splitterMoved);
24259 </code></pre>
24260  * @constructor
24261  * Create a new SplitBar
24262  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24263  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24264  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24265  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24266                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24267                         position of the SplitBar).
24268  */
24269 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24270     
24271     /** @private */
24272     this.el = Roo.get(dragElement, true);
24273     this.el.dom.unselectable = "on";
24274     /** @private */
24275     this.resizingEl = Roo.get(resizingElement, true);
24276
24277     /**
24278      * @private
24279      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24280      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24281      * @type Number
24282      */
24283     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24284     
24285     /**
24286      * The minimum size of the resizing element. (Defaults to 0)
24287      * @type Number
24288      */
24289     this.minSize = 0;
24290     
24291     /**
24292      * The maximum size of the resizing element. (Defaults to 2000)
24293      * @type Number
24294      */
24295     this.maxSize = 2000;
24296     
24297     /**
24298      * Whether to animate the transition to the new size
24299      * @type Boolean
24300      */
24301     this.animate = false;
24302     
24303     /**
24304      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24305      * @type Boolean
24306      */
24307     this.useShim = false;
24308     
24309     /** @private */
24310     this.shim = null;
24311     
24312     if(!existingProxy){
24313         /** @private */
24314         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24315     }else{
24316         this.proxy = Roo.get(existingProxy).dom;
24317     }
24318     /** @private */
24319     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24320     
24321     /** @private */
24322     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24323     
24324     /** @private */
24325     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24326     
24327     /** @private */
24328     this.dragSpecs = {};
24329     
24330     /**
24331      * @private The adapter to use to positon and resize elements
24332      */
24333     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24334     this.adapter.init(this);
24335     
24336     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24337         /** @private */
24338         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24339         this.el.addClass("x-splitbar-h");
24340     }else{
24341         /** @private */
24342         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24343         this.el.addClass("x-splitbar-v");
24344     }
24345     
24346     this.addEvents({
24347         /**
24348          * @event resize
24349          * Fires when the splitter is moved (alias for {@link #event-moved})
24350          * @param {Roo.SplitBar} this
24351          * @param {Number} newSize the new width or height
24352          */
24353         "resize" : true,
24354         /**
24355          * @event moved
24356          * Fires when the splitter is moved
24357          * @param {Roo.SplitBar} this
24358          * @param {Number} newSize the new width or height
24359          */
24360         "moved" : true,
24361         /**
24362          * @event beforeresize
24363          * Fires before the splitter is dragged
24364          * @param {Roo.SplitBar} this
24365          */
24366         "beforeresize" : true,
24367
24368         "beforeapply" : true
24369     });
24370
24371     Roo.util.Observable.call(this);
24372 };
24373
24374 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24375     onStartProxyDrag : function(x, y){
24376         this.fireEvent("beforeresize", this);
24377         if(!this.overlay){
24378             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24379             o.unselectable();
24380             o.enableDisplayMode("block");
24381             // all splitbars share the same overlay
24382             Roo.SplitBar.prototype.overlay = o;
24383         }
24384         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24385         this.overlay.show();
24386         Roo.get(this.proxy).setDisplayed("block");
24387         var size = this.adapter.getElementSize(this);
24388         this.activeMinSize = this.getMinimumSize();;
24389         this.activeMaxSize = this.getMaximumSize();;
24390         var c1 = size - this.activeMinSize;
24391         var c2 = Math.max(this.activeMaxSize - size, 0);
24392         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24393             this.dd.resetConstraints();
24394             this.dd.setXConstraint(
24395                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24396                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24397             );
24398             this.dd.setYConstraint(0, 0);
24399         }else{
24400             this.dd.resetConstraints();
24401             this.dd.setXConstraint(0, 0);
24402             this.dd.setYConstraint(
24403                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24404                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24405             );
24406          }
24407         this.dragSpecs.startSize = size;
24408         this.dragSpecs.startPoint = [x, y];
24409         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24410     },
24411     
24412     /** 
24413      * @private Called after the drag operation by the DDProxy
24414      */
24415     onEndProxyDrag : function(e){
24416         Roo.get(this.proxy).setDisplayed(false);
24417         var endPoint = Roo.lib.Event.getXY(e);
24418         if(this.overlay){
24419             this.overlay.hide();
24420         }
24421         var newSize;
24422         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24423             newSize = this.dragSpecs.startSize + 
24424                 (this.placement == Roo.SplitBar.LEFT ?
24425                     endPoint[0] - this.dragSpecs.startPoint[0] :
24426                     this.dragSpecs.startPoint[0] - endPoint[0]
24427                 );
24428         }else{
24429             newSize = this.dragSpecs.startSize + 
24430                 (this.placement == Roo.SplitBar.TOP ?
24431                     endPoint[1] - this.dragSpecs.startPoint[1] :
24432                     this.dragSpecs.startPoint[1] - endPoint[1]
24433                 );
24434         }
24435         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24436         if(newSize != this.dragSpecs.startSize){
24437             if(this.fireEvent('beforeapply', this, newSize) !== false){
24438                 this.adapter.setElementSize(this, newSize);
24439                 this.fireEvent("moved", this, newSize);
24440                 this.fireEvent("resize", this, newSize);
24441             }
24442         }
24443     },
24444     
24445     /**
24446      * Get the adapter this SplitBar uses
24447      * @return The adapter object
24448      */
24449     getAdapter : function(){
24450         return this.adapter;
24451     },
24452     
24453     /**
24454      * Set the adapter this SplitBar uses
24455      * @param {Object} adapter A SplitBar adapter object
24456      */
24457     setAdapter : function(adapter){
24458         this.adapter = adapter;
24459         this.adapter.init(this);
24460     },
24461     
24462     /**
24463      * Gets the minimum size for the resizing element
24464      * @return {Number} The minimum size
24465      */
24466     getMinimumSize : function(){
24467         return this.minSize;
24468     },
24469     
24470     /**
24471      * Sets the minimum size for the resizing element
24472      * @param {Number} minSize The minimum size
24473      */
24474     setMinimumSize : function(minSize){
24475         this.minSize = minSize;
24476     },
24477     
24478     /**
24479      * Gets the maximum size for the resizing element
24480      * @return {Number} The maximum size
24481      */
24482     getMaximumSize : function(){
24483         return this.maxSize;
24484     },
24485     
24486     /**
24487      * Sets the maximum size for the resizing element
24488      * @param {Number} maxSize The maximum size
24489      */
24490     setMaximumSize : function(maxSize){
24491         this.maxSize = maxSize;
24492     },
24493     
24494     /**
24495      * Sets the initialize size for the resizing element
24496      * @param {Number} size The initial size
24497      */
24498     setCurrentSize : function(size){
24499         var oldAnimate = this.animate;
24500         this.animate = false;
24501         this.adapter.setElementSize(this, size);
24502         this.animate = oldAnimate;
24503     },
24504     
24505     /**
24506      * Destroy this splitbar. 
24507      * @param {Boolean} removeEl True to remove the element
24508      */
24509     destroy : function(removeEl){
24510         if(this.shim){
24511             this.shim.remove();
24512         }
24513         this.dd.unreg();
24514         this.proxy.parentNode.removeChild(this.proxy);
24515         if(removeEl){
24516             this.el.remove();
24517         }
24518     }
24519 });
24520
24521 /**
24522  * @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.
24523  */
24524 Roo.SplitBar.createProxy = function(dir){
24525     var proxy = new Roo.Element(document.createElement("div"));
24526     proxy.unselectable();
24527     var cls = 'x-splitbar-proxy';
24528     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24529     document.body.appendChild(proxy.dom);
24530     return proxy.dom;
24531 };
24532
24533 /** 
24534  * @class Roo.SplitBar.BasicLayoutAdapter
24535  * Default Adapter. It assumes the splitter and resizing element are not positioned
24536  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24537  */
24538 Roo.SplitBar.BasicLayoutAdapter = function(){
24539 };
24540
24541 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24542     // do nothing for now
24543     init : function(s){
24544     
24545     },
24546     /**
24547      * Called before drag operations to get the current size of the resizing element. 
24548      * @param {Roo.SplitBar} s The SplitBar using this adapter
24549      */
24550      getElementSize : function(s){
24551         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24552             return s.resizingEl.getWidth();
24553         }else{
24554             return s.resizingEl.getHeight();
24555         }
24556     },
24557     
24558     /**
24559      * Called after drag operations to set the size of the resizing element.
24560      * @param {Roo.SplitBar} s The SplitBar using this adapter
24561      * @param {Number} newSize The new size to set
24562      * @param {Function} onComplete A function to be invoked when resizing is complete
24563      */
24564     setElementSize : function(s, newSize, onComplete){
24565         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24566             if(!s.animate){
24567                 s.resizingEl.setWidth(newSize);
24568                 if(onComplete){
24569                     onComplete(s, newSize);
24570                 }
24571             }else{
24572                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24573             }
24574         }else{
24575             
24576             if(!s.animate){
24577                 s.resizingEl.setHeight(newSize);
24578                 if(onComplete){
24579                     onComplete(s, newSize);
24580                 }
24581             }else{
24582                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24583             }
24584         }
24585     }
24586 };
24587
24588 /** 
24589  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24590  * @extends Roo.SplitBar.BasicLayoutAdapter
24591  * Adapter that  moves the splitter element to align with the resized sizing element. 
24592  * Used with an absolute positioned SplitBar.
24593  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24594  * document.body, make sure you assign an id to the body element.
24595  */
24596 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24597     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24598     this.container = Roo.get(container);
24599 };
24600
24601 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24602     init : function(s){
24603         this.basic.init(s);
24604     },
24605     
24606     getElementSize : function(s){
24607         return this.basic.getElementSize(s);
24608     },
24609     
24610     setElementSize : function(s, newSize, onComplete){
24611         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24612     },
24613     
24614     moveSplitter : function(s){
24615         var yes = Roo.SplitBar;
24616         switch(s.placement){
24617             case yes.LEFT:
24618                 s.el.setX(s.resizingEl.getRight());
24619                 break;
24620             case yes.RIGHT:
24621                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24622                 break;
24623             case yes.TOP:
24624                 s.el.setY(s.resizingEl.getBottom());
24625                 break;
24626             case yes.BOTTOM:
24627                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24628                 break;
24629         }
24630     }
24631 };
24632
24633 /**
24634  * Orientation constant - Create a vertical SplitBar
24635  * @static
24636  * @type Number
24637  */
24638 Roo.SplitBar.VERTICAL = 1;
24639
24640 /**
24641  * Orientation constant - Create a horizontal SplitBar
24642  * @static
24643  * @type Number
24644  */
24645 Roo.SplitBar.HORIZONTAL = 2;
24646
24647 /**
24648  * Placement constant - The resizing element is to the left of the splitter element
24649  * @static
24650  * @type Number
24651  */
24652 Roo.SplitBar.LEFT = 1;
24653
24654 /**
24655  * Placement constant - The resizing element is to the right of the splitter element
24656  * @static
24657  * @type Number
24658  */
24659 Roo.SplitBar.RIGHT = 2;
24660
24661 /**
24662  * Placement constant - The resizing element is positioned above the splitter element
24663  * @static
24664  * @type Number
24665  */
24666 Roo.SplitBar.TOP = 3;
24667
24668 /**
24669  * Placement constant - The resizing element is positioned under splitter element
24670  * @static
24671  * @type Number
24672  */
24673 Roo.SplitBar.BOTTOM = 4;
24674 /*
24675  * Based on:
24676  * Ext JS Library 1.1.1
24677  * Copyright(c) 2006-2007, Ext JS, LLC.
24678  *
24679  * Originally Released Under LGPL - original licence link has changed is not relivant.
24680  *
24681  * Fork - LGPL
24682  * <script type="text/javascript">
24683  */
24684
24685 /**
24686  * @class Roo.View
24687  * @extends Roo.util.Observable
24688  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24689  * This class also supports single and multi selection modes. <br>
24690  * Create a data model bound view:
24691  <pre><code>
24692  var store = new Roo.data.Store(...);
24693
24694  var view = new Roo.View({
24695     el : "my-element",
24696     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24697  
24698     singleSelect: true,
24699     selectedClass: "ydataview-selected",
24700     store: store
24701  });
24702
24703  // listen for node click?
24704  view.on("click", function(vw, index, node, e){
24705  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24706  });
24707
24708  // load XML data
24709  dataModel.load("foobar.xml");
24710  </code></pre>
24711  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24712  * <br><br>
24713  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24714  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24715  * 
24716  * Note: old style constructor is still suported (container, template, config)
24717  * 
24718  * @constructor
24719  * Create a new View
24720  * @param {Object} config The config object
24721  * 
24722  */
24723 Roo.View = function(config, depreciated_tpl, depreciated_config){
24724     
24725     if (typeof(depreciated_tpl) == 'undefined') {
24726         // new way.. - universal constructor.
24727         Roo.apply(this, config);
24728         this.el  = Roo.get(this.el);
24729     } else {
24730         // old format..
24731         this.el  = Roo.get(config);
24732         this.tpl = depreciated_tpl;
24733         Roo.apply(this, depreciated_config);
24734     }
24735     this.wrapEl  = this.el.wrap().wrap();
24736     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24737     
24738     
24739     if(typeof(this.tpl) == "string"){
24740         this.tpl = new Roo.Template(this.tpl);
24741     } else {
24742         // support xtype ctors..
24743         this.tpl = new Roo.factory(this.tpl, Roo);
24744     }
24745     
24746     
24747     this.tpl.compile();
24748    
24749   
24750     
24751      
24752     /** @private */
24753     this.addEvents({
24754         /**
24755          * @event beforeclick
24756          * Fires before a click is processed. Returns false to cancel the default action.
24757          * @param {Roo.View} this
24758          * @param {Number} index The index of the target node
24759          * @param {HTMLElement} node The target node
24760          * @param {Roo.EventObject} e The raw event object
24761          */
24762             "beforeclick" : true,
24763         /**
24764          * @event click
24765          * Fires when a template node is clicked.
24766          * @param {Roo.View} this
24767          * @param {Number} index The index of the target node
24768          * @param {HTMLElement} node The target node
24769          * @param {Roo.EventObject} e The raw event object
24770          */
24771             "click" : true,
24772         /**
24773          * @event dblclick
24774          * Fires when a template node is double clicked.
24775          * @param {Roo.View} this
24776          * @param {Number} index The index of the target node
24777          * @param {HTMLElement} node The target node
24778          * @param {Roo.EventObject} e The raw event object
24779          */
24780             "dblclick" : true,
24781         /**
24782          * @event contextmenu
24783          * Fires when a template node is right clicked.
24784          * @param {Roo.View} this
24785          * @param {Number} index The index of the target node
24786          * @param {HTMLElement} node The target node
24787          * @param {Roo.EventObject} e The raw event object
24788          */
24789             "contextmenu" : true,
24790         /**
24791          * @event selectionchange
24792          * Fires when the selected nodes change.
24793          * @param {Roo.View} this
24794          * @param {Array} selections Array of the selected nodes
24795          */
24796             "selectionchange" : true,
24797     
24798         /**
24799          * @event beforeselect
24800          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24801          * @param {Roo.View} this
24802          * @param {HTMLElement} node The node to be selected
24803          * @param {Array} selections Array of currently selected nodes
24804          */
24805             "beforeselect" : true,
24806         /**
24807          * @event preparedata
24808          * Fires on every row to render, to allow you to change the data.
24809          * @param {Roo.View} this
24810          * @param {Object} data to be rendered (change this)
24811          */
24812           "preparedata" : true
24813           
24814           
24815         });
24816
24817
24818
24819     this.el.on({
24820         "click": this.onClick,
24821         "dblclick": this.onDblClick,
24822         "contextmenu": this.onContextMenu,
24823         scope:this
24824     });
24825
24826     this.selections = [];
24827     this.nodes = [];
24828     this.cmp = new Roo.CompositeElementLite([]);
24829     if(this.store){
24830         this.store = Roo.factory(this.store, Roo.data);
24831         this.setStore(this.store, true);
24832     }
24833     
24834     if ( this.footer && this.footer.xtype) {
24835            
24836          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24837         
24838         this.footer.dataSource = this.store
24839         this.footer.container = fctr;
24840         this.footer = Roo.factory(this.footer, Roo);
24841         fctr.insertFirst(this.el);
24842         
24843         // this is a bit insane - as the paging toolbar seems to detach the el..
24844 //        dom.parentNode.parentNode.parentNode
24845          // they get detached?
24846     }
24847     
24848     
24849     Roo.View.superclass.constructor.call(this);
24850     
24851     
24852 };
24853
24854 Roo.extend(Roo.View, Roo.util.Observable, {
24855     
24856      /**
24857      * @cfg {Roo.data.Store} store Data store to load data from.
24858      */
24859     store : false,
24860     
24861     /**
24862      * @cfg {String|Roo.Element} el The container element.
24863      */
24864     el : '',
24865     
24866     /**
24867      * @cfg {String|Roo.Template} tpl The template used by this View 
24868      */
24869     tpl : false,
24870     /**
24871      * @cfg {String} dataName the named area of the template to use as the data area
24872      *                          Works with domtemplates roo-name="name"
24873      */
24874     dataName: false,
24875     /**
24876      * @cfg {String} selectedClass The css class to add to selected nodes
24877      */
24878     selectedClass : "x-view-selected",
24879      /**
24880      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24881      */
24882     emptyText : "",
24883     
24884     /**
24885      * @cfg {String} text to display on mask (default Loading)
24886      */
24887     mask : false,
24888     /**
24889      * @cfg {Boolean} multiSelect Allow multiple selection
24890      */
24891     multiSelect : false,
24892     /**
24893      * @cfg {Boolean} singleSelect Allow single selection
24894      */
24895     singleSelect:  false,
24896     
24897     /**
24898      * @cfg {Boolean} toggleSelect - selecting 
24899      */
24900     toggleSelect : false,
24901     
24902     /**
24903      * Returns the element this view is bound to.
24904      * @return {Roo.Element}
24905      */
24906     getEl : function(){
24907         return this.wrapEl;
24908     },
24909     
24910     
24911
24912     /**
24913      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24914      */
24915     refresh : function(){
24916         var t = this.tpl;
24917         
24918         // if we are using something like 'domtemplate', then
24919         // the what gets used is:
24920         // t.applySubtemplate(NAME, data, wrapping data..)
24921         // the outer template then get' applied with
24922         //     the store 'extra data'
24923         // and the body get's added to the
24924         //      roo-name="data" node?
24925         //      <span class='roo-tpl-{name}'></span> ?????
24926         
24927         
24928         
24929         this.clearSelections();
24930         this.el.update("");
24931         var html = [];
24932         var records = this.store.getRange();
24933         if(records.length < 1) {
24934             
24935             // is this valid??  = should it render a template??
24936             
24937             this.el.update(this.emptyText);
24938             return;
24939         }
24940         var el = this.el;
24941         if (this.dataName) {
24942             this.el.update(t.apply(this.store.meta)); //????
24943             el = this.el.child('.roo-tpl-' + this.dataName);
24944         }
24945         
24946         for(var i = 0, len = records.length; i < len; i++){
24947             var data = this.prepareData(records[i].data, i, records[i]);
24948             this.fireEvent("preparedata", this, data, i, records[i]);
24949             html[html.length] = Roo.util.Format.trim(
24950                 this.dataName ?
24951                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24952                     t.apply(data)
24953             );
24954         }
24955         
24956         
24957         
24958         el.update(html.join(""));
24959         this.nodes = el.dom.childNodes;
24960         this.updateIndexes(0);
24961     },
24962
24963     /**
24964      * Function to override to reformat the data that is sent to
24965      * the template for each node.
24966      * DEPRICATED - use the preparedata event handler.
24967      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24968      * a JSON object for an UpdateManager bound view).
24969      */
24970     prepareData : function(data, index, record)
24971     {
24972         this.fireEvent("preparedata", this, data, index, record);
24973         return data;
24974     },
24975
24976     onUpdate : function(ds, record){
24977         this.clearSelections();
24978         var index = this.store.indexOf(record);
24979         var n = this.nodes[index];
24980         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24981         n.parentNode.removeChild(n);
24982         this.updateIndexes(index, index);
24983     },
24984
24985     
24986     
24987 // --------- FIXME     
24988     onAdd : function(ds, records, index)
24989     {
24990         this.clearSelections();
24991         if(this.nodes.length == 0){
24992             this.refresh();
24993             return;
24994         }
24995         var n = this.nodes[index];
24996         for(var i = 0, len = records.length; i < len; i++){
24997             var d = this.prepareData(records[i].data, i, records[i]);
24998             if(n){
24999                 this.tpl.insertBefore(n, d);
25000             }else{
25001                 
25002                 this.tpl.append(this.el, d);
25003             }
25004         }
25005         this.updateIndexes(index);
25006     },
25007
25008     onRemove : function(ds, record, index){
25009         this.clearSelections();
25010         var el = this.dataName  ?
25011             this.el.child('.roo-tpl-' + this.dataName) :
25012             this.el; 
25013         el.dom.removeChild(this.nodes[index]);
25014         this.updateIndexes(index);
25015     },
25016
25017     /**
25018      * Refresh an individual node.
25019      * @param {Number} index
25020      */
25021     refreshNode : function(index){
25022         this.onUpdate(this.store, this.store.getAt(index));
25023     },
25024
25025     updateIndexes : function(startIndex, endIndex){
25026         var ns = this.nodes;
25027         startIndex = startIndex || 0;
25028         endIndex = endIndex || ns.length - 1;
25029         for(var i = startIndex; i <= endIndex; i++){
25030             ns[i].nodeIndex = i;
25031         }
25032     },
25033
25034     /**
25035      * Changes the data store this view uses and refresh the view.
25036      * @param {Store} store
25037      */
25038     setStore : function(store, initial){
25039         if(!initial && this.store){
25040             this.store.un("datachanged", this.refresh);
25041             this.store.un("add", this.onAdd);
25042             this.store.un("remove", this.onRemove);
25043             this.store.un("update", this.onUpdate);
25044             this.store.un("clear", this.refresh);
25045             this.store.un("beforeload", this.onBeforeLoad);
25046             this.store.un("load", this.onLoad);
25047             this.store.un("loadexception", this.onLoad);
25048         }
25049         if(store){
25050           
25051             store.on("datachanged", this.refresh, this);
25052             store.on("add", this.onAdd, this);
25053             store.on("remove", this.onRemove, this);
25054             store.on("update", this.onUpdate, this);
25055             store.on("clear", this.refresh, this);
25056             store.on("beforeload", this.onBeforeLoad, this);
25057             store.on("load", this.onLoad, this);
25058             store.on("loadexception", this.onLoad, this);
25059         }
25060         
25061         if(store){
25062             this.refresh();
25063         }
25064     },
25065     /**
25066      * onbeforeLoad - masks the loading area.
25067      *
25068      */
25069     onBeforeLoad : function()
25070     {
25071         this.el.update("");
25072         this.el.mask(this.mask ? this.mask : "Loading" ); 
25073     },
25074     onLoad : function ()
25075     {
25076         this.el.unmask();
25077     },
25078     
25079
25080     /**
25081      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25082      * @param {HTMLElement} node
25083      * @return {HTMLElement} The template node
25084      */
25085     findItemFromChild : function(node){
25086         var el = this.dataName  ?
25087             this.el.child('.roo-tpl-' + this.dataName,true) :
25088             this.el.dom; 
25089         
25090         if(!node || node.parentNode == el){
25091                     return node;
25092             }
25093             var p = node.parentNode;
25094             while(p && p != el){
25095             if(p.parentNode == el){
25096                 return p;
25097             }
25098             p = p.parentNode;
25099         }
25100             return null;
25101     },
25102
25103     /** @ignore */
25104     onClick : function(e){
25105         var item = this.findItemFromChild(e.getTarget());
25106         if(item){
25107             var index = this.indexOf(item);
25108             if(this.onItemClick(item, index, e) !== false){
25109                 this.fireEvent("click", this, index, item, e);
25110             }
25111         }else{
25112             this.clearSelections();
25113         }
25114     },
25115
25116     /** @ignore */
25117     onContextMenu : function(e){
25118         var item = this.findItemFromChild(e.getTarget());
25119         if(item){
25120             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25121         }
25122     },
25123
25124     /** @ignore */
25125     onDblClick : function(e){
25126         var item = this.findItemFromChild(e.getTarget());
25127         if(item){
25128             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25129         }
25130     },
25131
25132     onItemClick : function(item, index, e)
25133     {
25134         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25135             return false;
25136         }
25137         if (this.toggleSelect) {
25138             var m = this.isSelected(item) ? 'unselect' : 'select';
25139             Roo.log(m);
25140             var _t = this;
25141             _t[m](item, true, false);
25142             return true;
25143         }
25144         if(this.multiSelect || this.singleSelect){
25145             if(this.multiSelect && e.shiftKey && this.lastSelection){
25146                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25147             }else{
25148                 this.select(item, this.multiSelect && e.ctrlKey);
25149                 this.lastSelection = item;
25150             }
25151             e.preventDefault();
25152         }
25153         return true;
25154     },
25155
25156     /**
25157      * Get the number of selected nodes.
25158      * @return {Number}
25159      */
25160     getSelectionCount : function(){
25161         return this.selections.length;
25162     },
25163
25164     /**
25165      * Get the currently selected nodes.
25166      * @return {Array} An array of HTMLElements
25167      */
25168     getSelectedNodes : function(){
25169         return this.selections;
25170     },
25171
25172     /**
25173      * Get the indexes of the selected nodes.
25174      * @return {Array}
25175      */
25176     getSelectedIndexes : function(){
25177         var indexes = [], s = this.selections;
25178         for(var i = 0, len = s.length; i < len; i++){
25179             indexes.push(s[i].nodeIndex);
25180         }
25181         return indexes;
25182     },
25183
25184     /**
25185      * Clear all selections
25186      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25187      */
25188     clearSelections : function(suppressEvent){
25189         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25190             this.cmp.elements = this.selections;
25191             this.cmp.removeClass(this.selectedClass);
25192             this.selections = [];
25193             if(!suppressEvent){
25194                 this.fireEvent("selectionchange", this, this.selections);
25195             }
25196         }
25197     },
25198
25199     /**
25200      * Returns true if the passed node is selected
25201      * @param {HTMLElement/Number} node The node or node index
25202      * @return {Boolean}
25203      */
25204     isSelected : function(node){
25205         var s = this.selections;
25206         if(s.length < 1){
25207             return false;
25208         }
25209         node = this.getNode(node);
25210         return s.indexOf(node) !== -1;
25211     },
25212
25213     /**
25214      * Selects nodes.
25215      * @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
25216      * @param {Boolean} keepExisting (optional) true to keep existing selections
25217      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25218      */
25219     select : function(nodeInfo, keepExisting, suppressEvent){
25220         if(nodeInfo instanceof Array){
25221             if(!keepExisting){
25222                 this.clearSelections(true);
25223             }
25224             for(var i = 0, len = nodeInfo.length; i < len; i++){
25225                 this.select(nodeInfo[i], true, true);
25226             }
25227             return;
25228         } 
25229         var node = this.getNode(nodeInfo);
25230         if(!node || this.isSelected(node)){
25231             return; // already selected.
25232         }
25233         if(!keepExisting){
25234             this.clearSelections(true);
25235         }
25236         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25237             Roo.fly(node).addClass(this.selectedClass);
25238             this.selections.push(node);
25239             if(!suppressEvent){
25240                 this.fireEvent("selectionchange", this, this.selections);
25241             }
25242         }
25243         
25244         
25245     },
25246       /**
25247      * Unselects nodes.
25248      * @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
25249      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25250      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25251      */
25252     unselect : function(nodeInfo, keepExisting, suppressEvent)
25253     {
25254         if(nodeInfo instanceof Array){
25255             Roo.each(this.selections, function(s) {
25256                 this.unselect(s, nodeInfo);
25257             }, this);
25258             return;
25259         }
25260         var node = this.getNode(nodeInfo);
25261         if(!node || !this.isSelected(node)){
25262             Roo.log("not selected");
25263             return; // not selected.
25264         }
25265         // fireevent???
25266         var ns = [];
25267         Roo.each(this.selections, function(s) {
25268             if (s == node ) {
25269                 Roo.fly(node).removeClass(this.selectedClass);
25270
25271                 return;
25272             }
25273             ns.push(s);
25274         },this);
25275         
25276         this.selections= ns;
25277         this.fireEvent("selectionchange", this, this.selections);
25278     },
25279
25280     /**
25281      * Gets a template node.
25282      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25283      * @return {HTMLElement} The node or null if it wasn't found
25284      */
25285     getNode : function(nodeInfo){
25286         if(typeof nodeInfo == "string"){
25287             return document.getElementById(nodeInfo);
25288         }else if(typeof nodeInfo == "number"){
25289             return this.nodes[nodeInfo];
25290         }
25291         return nodeInfo;
25292     },
25293
25294     /**
25295      * Gets a range template nodes.
25296      * @param {Number} startIndex
25297      * @param {Number} endIndex
25298      * @return {Array} An array of nodes
25299      */
25300     getNodes : function(start, end){
25301         var ns = this.nodes;
25302         start = start || 0;
25303         end = typeof end == "undefined" ? ns.length - 1 : end;
25304         var nodes = [];
25305         if(start <= end){
25306             for(var i = start; i <= end; i++){
25307                 nodes.push(ns[i]);
25308             }
25309         } else{
25310             for(var i = start; i >= end; i--){
25311                 nodes.push(ns[i]);
25312             }
25313         }
25314         return nodes;
25315     },
25316
25317     /**
25318      * Finds the index of the passed node
25319      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25320      * @return {Number} The index of the node or -1
25321      */
25322     indexOf : function(node){
25323         node = this.getNode(node);
25324         if(typeof node.nodeIndex == "number"){
25325             return node.nodeIndex;
25326         }
25327         var ns = this.nodes;
25328         for(var i = 0, len = ns.length; i < len; i++){
25329             if(ns[i] == node){
25330                 return i;
25331             }
25332         }
25333         return -1;
25334     }
25335 });
25336 /*
25337  * Based on:
25338  * Ext JS Library 1.1.1
25339  * Copyright(c) 2006-2007, Ext JS, LLC.
25340  *
25341  * Originally Released Under LGPL - original licence link has changed is not relivant.
25342  *
25343  * Fork - LGPL
25344  * <script type="text/javascript">
25345  */
25346
25347 /**
25348  * @class Roo.JsonView
25349  * @extends Roo.View
25350  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25351 <pre><code>
25352 var view = new Roo.JsonView({
25353     container: "my-element",
25354     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25355     multiSelect: true, 
25356     jsonRoot: "data" 
25357 });
25358
25359 // listen for node click?
25360 view.on("click", function(vw, index, node, e){
25361     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25362 });
25363
25364 // direct load of JSON data
25365 view.load("foobar.php");
25366
25367 // Example from my blog list
25368 var tpl = new Roo.Template(
25369     '&lt;div class="entry"&gt;' +
25370     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25371     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25372     "&lt;/div&gt;&lt;hr /&gt;"
25373 );
25374
25375 var moreView = new Roo.JsonView({
25376     container :  "entry-list", 
25377     template : tpl,
25378     jsonRoot: "posts"
25379 });
25380 moreView.on("beforerender", this.sortEntries, this);
25381 moreView.load({
25382     url: "/blog/get-posts.php",
25383     params: "allposts=true",
25384     text: "Loading Blog Entries..."
25385 });
25386 </code></pre>
25387
25388 * Note: old code is supported with arguments : (container, template, config)
25389
25390
25391  * @constructor
25392  * Create a new JsonView
25393  * 
25394  * @param {Object} config The config object
25395  * 
25396  */
25397 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25398     
25399     
25400     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25401
25402     var um = this.el.getUpdateManager();
25403     um.setRenderer(this);
25404     um.on("update", this.onLoad, this);
25405     um.on("failure", this.onLoadException, this);
25406
25407     /**
25408      * @event beforerender
25409      * Fires before rendering of the downloaded JSON data.
25410      * @param {Roo.JsonView} this
25411      * @param {Object} data The JSON data loaded
25412      */
25413     /**
25414      * @event load
25415      * Fires when data is loaded.
25416      * @param {Roo.JsonView} this
25417      * @param {Object} data The JSON data loaded
25418      * @param {Object} response The raw Connect response object
25419      */
25420     /**
25421      * @event loadexception
25422      * Fires when loading fails.
25423      * @param {Roo.JsonView} this
25424      * @param {Object} response The raw Connect response object
25425      */
25426     this.addEvents({
25427         'beforerender' : true,
25428         'load' : true,
25429         'loadexception' : true
25430     });
25431 };
25432 Roo.extend(Roo.JsonView, Roo.View, {
25433     /**
25434      * @type {String} The root property in the loaded JSON object that contains the data
25435      */
25436     jsonRoot : "",
25437
25438     /**
25439      * Refreshes the view.
25440      */
25441     refresh : function(){
25442         this.clearSelections();
25443         this.el.update("");
25444         var html = [];
25445         var o = this.jsonData;
25446         if(o && o.length > 0){
25447             for(var i = 0, len = o.length; i < len; i++){
25448                 var data = this.prepareData(o[i], i, o);
25449                 html[html.length] = this.tpl.apply(data);
25450             }
25451         }else{
25452             html.push(this.emptyText);
25453         }
25454         this.el.update(html.join(""));
25455         this.nodes = this.el.dom.childNodes;
25456         this.updateIndexes(0);
25457     },
25458
25459     /**
25460      * 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.
25461      * @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:
25462      <pre><code>
25463      view.load({
25464          url: "your-url.php",
25465          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25466          callback: yourFunction,
25467          scope: yourObject, //(optional scope)
25468          discardUrl: false,
25469          nocache: false,
25470          text: "Loading...",
25471          timeout: 30,
25472          scripts: false
25473      });
25474      </code></pre>
25475      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25476      * 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.
25477      * @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}
25478      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25479      * @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.
25480      */
25481     load : function(){
25482         var um = this.el.getUpdateManager();
25483         um.update.apply(um, arguments);
25484     },
25485
25486     render : function(el, response){
25487         this.clearSelections();
25488         this.el.update("");
25489         var o;
25490         try{
25491             o = Roo.util.JSON.decode(response.responseText);
25492             if(this.jsonRoot){
25493                 
25494                 o = o[this.jsonRoot];
25495             }
25496         } catch(e){
25497         }
25498         /**
25499          * The current JSON data or null
25500          */
25501         this.jsonData = o;
25502         this.beforeRender();
25503         this.refresh();
25504     },
25505
25506 /**
25507  * Get the number of records in the current JSON dataset
25508  * @return {Number}
25509  */
25510     getCount : function(){
25511         return this.jsonData ? this.jsonData.length : 0;
25512     },
25513
25514 /**
25515  * Returns the JSON object for the specified node(s)
25516  * @param {HTMLElement/Array} node The node or an array of nodes
25517  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25518  * you get the JSON object for the node
25519  */
25520     getNodeData : function(node){
25521         if(node instanceof Array){
25522             var data = [];
25523             for(var i = 0, len = node.length; i < len; i++){
25524                 data.push(this.getNodeData(node[i]));
25525             }
25526             return data;
25527         }
25528         return this.jsonData[this.indexOf(node)] || null;
25529     },
25530
25531     beforeRender : function(){
25532         this.snapshot = this.jsonData;
25533         if(this.sortInfo){
25534             this.sort.apply(this, this.sortInfo);
25535         }
25536         this.fireEvent("beforerender", this, this.jsonData);
25537     },
25538
25539     onLoad : function(el, o){
25540         this.fireEvent("load", this, this.jsonData, o);
25541     },
25542
25543     onLoadException : function(el, o){
25544         this.fireEvent("loadexception", this, o);
25545     },
25546
25547 /**
25548  * Filter the data by a specific property.
25549  * @param {String} property A property on your JSON objects
25550  * @param {String/RegExp} value Either string that the property values
25551  * should start with, or a RegExp to test against the property
25552  */
25553     filter : function(property, value){
25554         if(this.jsonData){
25555             var data = [];
25556             var ss = this.snapshot;
25557             if(typeof value == "string"){
25558                 var vlen = value.length;
25559                 if(vlen == 0){
25560                     this.clearFilter();
25561                     return;
25562                 }
25563                 value = value.toLowerCase();
25564                 for(var i = 0, len = ss.length; i < len; i++){
25565                     var o = ss[i];
25566                     if(o[property].substr(0, vlen).toLowerCase() == value){
25567                         data.push(o);
25568                     }
25569                 }
25570             } else if(value.exec){ // regex?
25571                 for(var i = 0, len = ss.length; i < len; i++){
25572                     var o = ss[i];
25573                     if(value.test(o[property])){
25574                         data.push(o);
25575                     }
25576                 }
25577             } else{
25578                 return;
25579             }
25580             this.jsonData = data;
25581             this.refresh();
25582         }
25583     },
25584
25585 /**
25586  * Filter by a function. The passed function will be called with each
25587  * object in the current dataset. If the function returns true the value is kept,
25588  * otherwise it is filtered.
25589  * @param {Function} fn
25590  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25591  */
25592     filterBy : function(fn, scope){
25593         if(this.jsonData){
25594             var data = [];
25595             var ss = this.snapshot;
25596             for(var i = 0, len = ss.length; i < len; i++){
25597                 var o = ss[i];
25598                 if(fn.call(scope || this, o)){
25599                     data.push(o);
25600                 }
25601             }
25602             this.jsonData = data;
25603             this.refresh();
25604         }
25605     },
25606
25607 /**
25608  * Clears the current filter.
25609  */
25610     clearFilter : function(){
25611         if(this.snapshot && this.jsonData != this.snapshot){
25612             this.jsonData = this.snapshot;
25613             this.refresh();
25614         }
25615     },
25616
25617
25618 /**
25619  * Sorts the data for this view and refreshes it.
25620  * @param {String} property A property on your JSON objects to sort on
25621  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25622  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25623  */
25624     sort : function(property, dir, sortType){
25625         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25626         if(this.jsonData){
25627             var p = property;
25628             var dsc = dir && dir.toLowerCase() == "desc";
25629             var f = function(o1, o2){
25630                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25631                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25632                 ;
25633                 if(v1 < v2){
25634                     return dsc ? +1 : -1;
25635                 } else if(v1 > v2){
25636                     return dsc ? -1 : +1;
25637                 } else{
25638                     return 0;
25639                 }
25640             };
25641             this.jsonData.sort(f);
25642             this.refresh();
25643             if(this.jsonData != this.snapshot){
25644                 this.snapshot.sort(f);
25645             }
25646         }
25647     }
25648 });/*
25649  * Based on:
25650  * Ext JS Library 1.1.1
25651  * Copyright(c) 2006-2007, Ext JS, LLC.
25652  *
25653  * Originally Released Under LGPL - original licence link has changed is not relivant.
25654  *
25655  * Fork - LGPL
25656  * <script type="text/javascript">
25657  */
25658  
25659
25660 /**
25661  * @class Roo.ColorPalette
25662  * @extends Roo.Component
25663  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25664  * Here's an example of typical usage:
25665  * <pre><code>
25666 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25667 cp.render('my-div');
25668
25669 cp.on('select', function(palette, selColor){
25670     // do something with selColor
25671 });
25672 </code></pre>
25673  * @constructor
25674  * Create a new ColorPalette
25675  * @param {Object} config The config object
25676  */
25677 Roo.ColorPalette = function(config){
25678     Roo.ColorPalette.superclass.constructor.call(this, config);
25679     this.addEvents({
25680         /**
25681              * @event select
25682              * Fires when a color is selected
25683              * @param {ColorPalette} this
25684              * @param {String} color The 6-digit color hex code (without the # symbol)
25685              */
25686         select: true
25687     });
25688
25689     if(this.handler){
25690         this.on("select", this.handler, this.scope, true);
25691     }
25692 };
25693 Roo.extend(Roo.ColorPalette, Roo.Component, {
25694     /**
25695      * @cfg {String} itemCls
25696      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25697      */
25698     itemCls : "x-color-palette",
25699     /**
25700      * @cfg {String} value
25701      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25702      * the hex codes are case-sensitive.
25703      */
25704     value : null,
25705     clickEvent:'click',
25706     // private
25707     ctype: "Roo.ColorPalette",
25708
25709     /**
25710      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25711      */
25712     allowReselect : false,
25713
25714     /**
25715      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25716      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25717      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25718      * of colors with the width setting until the box is symmetrical.</p>
25719      * <p>You can override individual colors if needed:</p>
25720      * <pre><code>
25721 var cp = new Roo.ColorPalette();
25722 cp.colors[0] = "FF0000";  // change the first box to red
25723 </code></pre>
25724
25725 Or you can provide a custom array of your own for complete control:
25726 <pre><code>
25727 var cp = new Roo.ColorPalette();
25728 cp.colors = ["000000", "993300", "333300"];
25729 </code></pre>
25730      * @type Array
25731      */
25732     colors : [
25733         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25734         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25735         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25736         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25737         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25738     ],
25739
25740     // private
25741     onRender : function(container, position){
25742         var t = new Roo.MasterTemplate(
25743             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25744         );
25745         var c = this.colors;
25746         for(var i = 0, len = c.length; i < len; i++){
25747             t.add([c[i]]);
25748         }
25749         var el = document.createElement("div");
25750         el.className = this.itemCls;
25751         t.overwrite(el);
25752         container.dom.insertBefore(el, position);
25753         this.el = Roo.get(el);
25754         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25755         if(this.clickEvent != 'click'){
25756             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25757         }
25758     },
25759
25760     // private
25761     afterRender : function(){
25762         Roo.ColorPalette.superclass.afterRender.call(this);
25763         if(this.value){
25764             var s = this.value;
25765             this.value = null;
25766             this.select(s);
25767         }
25768     },
25769
25770     // private
25771     handleClick : function(e, t){
25772         e.preventDefault();
25773         if(!this.disabled){
25774             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25775             this.select(c.toUpperCase());
25776         }
25777     },
25778
25779     /**
25780      * Selects the specified color in the palette (fires the select event)
25781      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25782      */
25783     select : function(color){
25784         color = color.replace("#", "");
25785         if(color != this.value || this.allowReselect){
25786             var el = this.el;
25787             if(this.value){
25788                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25789             }
25790             el.child("a.color-"+color).addClass("x-color-palette-sel");
25791             this.value = color;
25792             this.fireEvent("select", this, color);
25793         }
25794     }
25795 });/*
25796  * Based on:
25797  * Ext JS Library 1.1.1
25798  * Copyright(c) 2006-2007, Ext JS, LLC.
25799  *
25800  * Originally Released Under LGPL - original licence link has changed is not relivant.
25801  *
25802  * Fork - LGPL
25803  * <script type="text/javascript">
25804  */
25805  
25806 /**
25807  * @class Roo.DatePicker
25808  * @extends Roo.Component
25809  * Simple date picker class.
25810  * @constructor
25811  * Create a new DatePicker
25812  * @param {Object} config The config object
25813  */
25814 Roo.DatePicker = function(config){
25815     Roo.DatePicker.superclass.constructor.call(this, config);
25816
25817     this.value = config && config.value ?
25818                  config.value.clearTime() : new Date().clearTime();
25819
25820     this.addEvents({
25821         /**
25822              * @event select
25823              * Fires when a date is selected
25824              * @param {DatePicker} this
25825              * @param {Date} date The selected date
25826              */
25827         'select': true,
25828         /**
25829              * @event monthchange
25830              * Fires when the displayed month changes 
25831              * @param {DatePicker} this
25832              * @param {Date} date The selected month
25833              */
25834         'monthchange': true
25835     });
25836
25837     if(this.handler){
25838         this.on("select", this.handler,  this.scope || this);
25839     }
25840     // build the disabledDatesRE
25841     if(!this.disabledDatesRE && this.disabledDates){
25842         var dd = this.disabledDates;
25843         var re = "(?:";
25844         for(var i = 0; i < dd.length; i++){
25845             re += dd[i];
25846             if(i != dd.length-1) re += "|";
25847         }
25848         this.disabledDatesRE = new RegExp(re + ")");
25849     }
25850 };
25851
25852 Roo.extend(Roo.DatePicker, Roo.Component, {
25853     /**
25854      * @cfg {String} todayText
25855      * The text to display on the button that selects the current date (defaults to "Today")
25856      */
25857     todayText : "Today",
25858     /**
25859      * @cfg {String} okText
25860      * The text to display on the ok button
25861      */
25862     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25863     /**
25864      * @cfg {String} cancelText
25865      * The text to display on the cancel button
25866      */
25867     cancelText : "Cancel",
25868     /**
25869      * @cfg {String} todayTip
25870      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25871      */
25872     todayTip : "{0} (Spacebar)",
25873     /**
25874      * @cfg {Date} minDate
25875      * Minimum allowable date (JavaScript date object, defaults to null)
25876      */
25877     minDate : null,
25878     /**
25879      * @cfg {Date} maxDate
25880      * Maximum allowable date (JavaScript date object, defaults to null)
25881      */
25882     maxDate : null,
25883     /**
25884      * @cfg {String} minText
25885      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25886      */
25887     minText : "This date is before the minimum date",
25888     /**
25889      * @cfg {String} maxText
25890      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25891      */
25892     maxText : "This date is after the maximum date",
25893     /**
25894      * @cfg {String} format
25895      * The default date format string which can be overriden for localization support.  The format must be
25896      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25897      */
25898     format : "m/d/y",
25899     /**
25900      * @cfg {Array} disabledDays
25901      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25902      */
25903     disabledDays : null,
25904     /**
25905      * @cfg {String} disabledDaysText
25906      * The tooltip to display when the date falls on a disabled day (defaults to "")
25907      */
25908     disabledDaysText : "",
25909     /**
25910      * @cfg {RegExp} disabledDatesRE
25911      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25912      */
25913     disabledDatesRE : null,
25914     /**
25915      * @cfg {String} disabledDatesText
25916      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25917      */
25918     disabledDatesText : "",
25919     /**
25920      * @cfg {Boolean} constrainToViewport
25921      * True to constrain the date picker to the viewport (defaults to true)
25922      */
25923     constrainToViewport : true,
25924     /**
25925      * @cfg {Array} monthNames
25926      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25927      */
25928     monthNames : Date.monthNames,
25929     /**
25930      * @cfg {Array} dayNames
25931      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25932      */
25933     dayNames : Date.dayNames,
25934     /**
25935      * @cfg {String} nextText
25936      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25937      */
25938     nextText: 'Next Month (Control+Right)',
25939     /**
25940      * @cfg {String} prevText
25941      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25942      */
25943     prevText: 'Previous Month (Control+Left)',
25944     /**
25945      * @cfg {String} monthYearText
25946      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25947      */
25948     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25949     /**
25950      * @cfg {Number} startDay
25951      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25952      */
25953     startDay : 0,
25954     /**
25955      * @cfg {Bool} showClear
25956      * Show a clear button (usefull for date form elements that can be blank.)
25957      */
25958     
25959     showClear: false,
25960     
25961     /**
25962      * Sets the value of the date field
25963      * @param {Date} value The date to set
25964      */
25965     setValue : function(value){
25966         var old = this.value;
25967         
25968         if (typeof(value) == 'string') {
25969          
25970             value = Date.parseDate(value, this.format);
25971         }
25972         if (!value) {
25973             value = new Date();
25974         }
25975         
25976         this.value = value.clearTime(true);
25977         if(this.el){
25978             this.update(this.value);
25979         }
25980     },
25981
25982     /**
25983      * Gets the current selected value of the date field
25984      * @return {Date} The selected date
25985      */
25986     getValue : function(){
25987         return this.value;
25988     },
25989
25990     // private
25991     focus : function(){
25992         if(this.el){
25993             this.update(this.activeDate);
25994         }
25995     },
25996
25997     // privateval
25998     onRender : function(container, position){
25999         
26000         var m = [
26001              '<table cellspacing="0">',
26002                 '<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>',
26003                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26004         var dn = this.dayNames;
26005         for(var i = 0; i < 7; i++){
26006             var d = this.startDay+i;
26007             if(d > 6){
26008                 d = d-7;
26009             }
26010             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26011         }
26012         m[m.length] = "</tr></thead><tbody><tr>";
26013         for(var i = 0; i < 42; i++) {
26014             if(i % 7 == 0 && i != 0){
26015                 m[m.length] = "</tr><tr>";
26016             }
26017             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26018         }
26019         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26020             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26021
26022         var el = document.createElement("div");
26023         el.className = "x-date-picker";
26024         el.innerHTML = m.join("");
26025
26026         container.dom.insertBefore(el, position);
26027
26028         this.el = Roo.get(el);
26029         this.eventEl = Roo.get(el.firstChild);
26030
26031         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26032             handler: this.showPrevMonth,
26033             scope: this,
26034             preventDefault:true,
26035             stopDefault:true
26036         });
26037
26038         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26039             handler: this.showNextMonth,
26040             scope: this,
26041             preventDefault:true,
26042             stopDefault:true
26043         });
26044
26045         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26046
26047         this.monthPicker = this.el.down('div.x-date-mp');
26048         this.monthPicker.enableDisplayMode('block');
26049         
26050         var kn = new Roo.KeyNav(this.eventEl, {
26051             "left" : function(e){
26052                 e.ctrlKey ?
26053                     this.showPrevMonth() :
26054                     this.update(this.activeDate.add("d", -1));
26055             },
26056
26057             "right" : function(e){
26058                 e.ctrlKey ?
26059                     this.showNextMonth() :
26060                     this.update(this.activeDate.add("d", 1));
26061             },
26062
26063             "up" : function(e){
26064                 e.ctrlKey ?
26065                     this.showNextYear() :
26066                     this.update(this.activeDate.add("d", -7));
26067             },
26068
26069             "down" : function(e){
26070                 e.ctrlKey ?
26071                     this.showPrevYear() :
26072                     this.update(this.activeDate.add("d", 7));
26073             },
26074
26075             "pageUp" : function(e){
26076                 this.showNextMonth();
26077             },
26078
26079             "pageDown" : function(e){
26080                 this.showPrevMonth();
26081             },
26082
26083             "enter" : function(e){
26084                 e.stopPropagation();
26085                 return true;
26086             },
26087
26088             scope : this
26089         });
26090
26091         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26092
26093         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26094
26095         this.el.unselectable();
26096         
26097         this.cells = this.el.select("table.x-date-inner tbody td");
26098         this.textNodes = this.el.query("table.x-date-inner tbody span");
26099
26100         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26101             text: "&#160;",
26102             tooltip: this.monthYearText
26103         });
26104
26105         this.mbtn.on('click', this.showMonthPicker, this);
26106         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26107
26108
26109         var today = (new Date()).dateFormat(this.format);
26110         
26111         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26112         if (this.showClear) {
26113             baseTb.add( new Roo.Toolbar.Fill());
26114         }
26115         baseTb.add({
26116             text: String.format(this.todayText, today),
26117             tooltip: String.format(this.todayTip, today),
26118             handler: this.selectToday,
26119             scope: this
26120         });
26121         
26122         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26123             
26124         //});
26125         if (this.showClear) {
26126             
26127             baseTb.add( new Roo.Toolbar.Fill());
26128             baseTb.add({
26129                 text: '&#160;',
26130                 cls: 'x-btn-icon x-btn-clear',
26131                 handler: function() {
26132                     //this.value = '';
26133                     this.fireEvent("select", this, '');
26134                 },
26135                 scope: this
26136             });
26137         }
26138         
26139         
26140         if(Roo.isIE){
26141             this.el.repaint();
26142         }
26143         this.update(this.value);
26144     },
26145
26146     createMonthPicker : function(){
26147         if(!this.monthPicker.dom.firstChild){
26148             var buf = ['<table border="0" cellspacing="0">'];
26149             for(var i = 0; i < 6; i++){
26150                 buf.push(
26151                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26152                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26153                     i == 0 ?
26154                     '<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>' :
26155                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26156                 );
26157             }
26158             buf.push(
26159                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26160                     this.okText,
26161                     '</button><button type="button" class="x-date-mp-cancel">',
26162                     this.cancelText,
26163                     '</button></td></tr>',
26164                 '</table>'
26165             );
26166             this.monthPicker.update(buf.join(''));
26167             this.monthPicker.on('click', this.onMonthClick, this);
26168             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26169
26170             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26171             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26172
26173             this.mpMonths.each(function(m, a, i){
26174                 i += 1;
26175                 if((i%2) == 0){
26176                     m.dom.xmonth = 5 + Math.round(i * .5);
26177                 }else{
26178                     m.dom.xmonth = Math.round((i-1) * .5);
26179                 }
26180             });
26181         }
26182     },
26183
26184     showMonthPicker : function(){
26185         this.createMonthPicker();
26186         var size = this.el.getSize();
26187         this.monthPicker.setSize(size);
26188         this.monthPicker.child('table').setSize(size);
26189
26190         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26191         this.updateMPMonth(this.mpSelMonth);
26192         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26193         this.updateMPYear(this.mpSelYear);
26194
26195         this.monthPicker.slideIn('t', {duration:.2});
26196     },
26197
26198     updateMPYear : function(y){
26199         this.mpyear = y;
26200         var ys = this.mpYears.elements;
26201         for(var i = 1; i <= 10; i++){
26202             var td = ys[i-1], y2;
26203             if((i%2) == 0){
26204                 y2 = y + Math.round(i * .5);
26205                 td.firstChild.innerHTML = y2;
26206                 td.xyear = y2;
26207             }else{
26208                 y2 = y - (5-Math.round(i * .5));
26209                 td.firstChild.innerHTML = y2;
26210                 td.xyear = y2;
26211             }
26212             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26213         }
26214     },
26215
26216     updateMPMonth : function(sm){
26217         this.mpMonths.each(function(m, a, i){
26218             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26219         });
26220     },
26221
26222     selectMPMonth: function(m){
26223         
26224     },
26225
26226     onMonthClick : function(e, t){
26227         e.stopEvent();
26228         var el = new Roo.Element(t), pn;
26229         if(el.is('button.x-date-mp-cancel')){
26230             this.hideMonthPicker();
26231         }
26232         else if(el.is('button.x-date-mp-ok')){
26233             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26234             this.hideMonthPicker();
26235         }
26236         else if(pn = el.up('td.x-date-mp-month', 2)){
26237             this.mpMonths.removeClass('x-date-mp-sel');
26238             pn.addClass('x-date-mp-sel');
26239             this.mpSelMonth = pn.dom.xmonth;
26240         }
26241         else if(pn = el.up('td.x-date-mp-year', 2)){
26242             this.mpYears.removeClass('x-date-mp-sel');
26243             pn.addClass('x-date-mp-sel');
26244             this.mpSelYear = pn.dom.xyear;
26245         }
26246         else if(el.is('a.x-date-mp-prev')){
26247             this.updateMPYear(this.mpyear-10);
26248         }
26249         else if(el.is('a.x-date-mp-next')){
26250             this.updateMPYear(this.mpyear+10);
26251         }
26252     },
26253
26254     onMonthDblClick : function(e, t){
26255         e.stopEvent();
26256         var el = new Roo.Element(t), pn;
26257         if(pn = el.up('td.x-date-mp-month', 2)){
26258             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26259             this.hideMonthPicker();
26260         }
26261         else if(pn = el.up('td.x-date-mp-year', 2)){
26262             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26263             this.hideMonthPicker();
26264         }
26265     },
26266
26267     hideMonthPicker : function(disableAnim){
26268         if(this.monthPicker){
26269             if(disableAnim === true){
26270                 this.monthPicker.hide();
26271             }else{
26272                 this.monthPicker.slideOut('t', {duration:.2});
26273             }
26274         }
26275     },
26276
26277     // private
26278     showPrevMonth : function(e){
26279         this.update(this.activeDate.add("mo", -1));
26280     },
26281
26282     // private
26283     showNextMonth : function(e){
26284         this.update(this.activeDate.add("mo", 1));
26285     },
26286
26287     // private
26288     showPrevYear : function(){
26289         this.update(this.activeDate.add("y", -1));
26290     },
26291
26292     // private
26293     showNextYear : function(){
26294         this.update(this.activeDate.add("y", 1));
26295     },
26296
26297     // private
26298     handleMouseWheel : function(e){
26299         var delta = e.getWheelDelta();
26300         if(delta > 0){
26301             this.showPrevMonth();
26302             e.stopEvent();
26303         } else if(delta < 0){
26304             this.showNextMonth();
26305             e.stopEvent();
26306         }
26307     },
26308
26309     // private
26310     handleDateClick : function(e, t){
26311         e.stopEvent();
26312         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26313             this.setValue(new Date(t.dateValue));
26314             this.fireEvent("select", this, this.value);
26315         }
26316     },
26317
26318     // private
26319     selectToday : function(){
26320         this.setValue(new Date().clearTime());
26321         this.fireEvent("select", this, this.value);
26322     },
26323
26324     // private
26325     update : function(date)
26326     {
26327         var vd = this.activeDate;
26328         this.activeDate = date;
26329         if(vd && this.el){
26330             var t = date.getTime();
26331             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26332                 this.cells.removeClass("x-date-selected");
26333                 this.cells.each(function(c){
26334                    if(c.dom.firstChild.dateValue == t){
26335                        c.addClass("x-date-selected");
26336                        setTimeout(function(){
26337                             try{c.dom.firstChild.focus();}catch(e){}
26338                        }, 50);
26339                        return false;
26340                    }
26341                 });
26342                 return;
26343             }
26344         }
26345         
26346         var days = date.getDaysInMonth();
26347         var firstOfMonth = date.getFirstDateOfMonth();
26348         var startingPos = firstOfMonth.getDay()-this.startDay;
26349
26350         if(startingPos <= this.startDay){
26351             startingPos += 7;
26352         }
26353
26354         var pm = date.add("mo", -1);
26355         var prevStart = pm.getDaysInMonth()-startingPos;
26356
26357         var cells = this.cells.elements;
26358         var textEls = this.textNodes;
26359         days += startingPos;
26360
26361         // convert everything to numbers so it's fast
26362         var day = 86400000;
26363         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26364         var today = new Date().clearTime().getTime();
26365         var sel = date.clearTime().getTime();
26366         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26367         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26368         var ddMatch = this.disabledDatesRE;
26369         var ddText = this.disabledDatesText;
26370         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26371         var ddaysText = this.disabledDaysText;
26372         var format = this.format;
26373
26374         var setCellClass = function(cal, cell){
26375             cell.title = "";
26376             var t = d.getTime();
26377             cell.firstChild.dateValue = t;
26378             if(t == today){
26379                 cell.className += " x-date-today";
26380                 cell.title = cal.todayText;
26381             }
26382             if(t == sel){
26383                 cell.className += " x-date-selected";
26384                 setTimeout(function(){
26385                     try{cell.firstChild.focus();}catch(e){}
26386                 }, 50);
26387             }
26388             // disabling
26389             if(t < min) {
26390                 cell.className = " x-date-disabled";
26391                 cell.title = cal.minText;
26392                 return;
26393             }
26394             if(t > max) {
26395                 cell.className = " x-date-disabled";
26396                 cell.title = cal.maxText;
26397                 return;
26398             }
26399             if(ddays){
26400                 if(ddays.indexOf(d.getDay()) != -1){
26401                     cell.title = ddaysText;
26402                     cell.className = " x-date-disabled";
26403                 }
26404             }
26405             if(ddMatch && format){
26406                 var fvalue = d.dateFormat(format);
26407                 if(ddMatch.test(fvalue)){
26408                     cell.title = ddText.replace("%0", fvalue);
26409                     cell.className = " x-date-disabled";
26410                 }
26411             }
26412         };
26413
26414         var i = 0;
26415         for(; i < startingPos; i++) {
26416             textEls[i].innerHTML = (++prevStart);
26417             d.setDate(d.getDate()+1);
26418             cells[i].className = "x-date-prevday";
26419             setCellClass(this, cells[i]);
26420         }
26421         for(; i < days; i++){
26422             intDay = i - startingPos + 1;
26423             textEls[i].innerHTML = (intDay);
26424             d.setDate(d.getDate()+1);
26425             cells[i].className = "x-date-active";
26426             setCellClass(this, cells[i]);
26427         }
26428         var extraDays = 0;
26429         for(; i < 42; i++) {
26430              textEls[i].innerHTML = (++extraDays);
26431              d.setDate(d.getDate()+1);
26432              cells[i].className = "x-date-nextday";
26433              setCellClass(this, cells[i]);
26434         }
26435
26436         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26437         this.fireEvent('monthchange', this, date);
26438         
26439         if(!this.internalRender){
26440             var main = this.el.dom.firstChild;
26441             var w = main.offsetWidth;
26442             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26443             Roo.fly(main).setWidth(w);
26444             this.internalRender = true;
26445             // opera does not respect the auto grow header center column
26446             // then, after it gets a width opera refuses to recalculate
26447             // without a second pass
26448             if(Roo.isOpera && !this.secondPass){
26449                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26450                 this.secondPass = true;
26451                 this.update.defer(10, this, [date]);
26452             }
26453         }
26454         
26455         
26456     }
26457 });        /*
26458  * Based on:
26459  * Ext JS Library 1.1.1
26460  * Copyright(c) 2006-2007, Ext JS, LLC.
26461  *
26462  * Originally Released Under LGPL - original licence link has changed is not relivant.
26463  *
26464  * Fork - LGPL
26465  * <script type="text/javascript">
26466  */
26467 /**
26468  * @class Roo.TabPanel
26469  * @extends Roo.util.Observable
26470  * A lightweight tab container.
26471  * <br><br>
26472  * Usage:
26473  * <pre><code>
26474 // basic tabs 1, built from existing content
26475 var tabs = new Roo.TabPanel("tabs1");
26476 tabs.addTab("script", "View Script");
26477 tabs.addTab("markup", "View Markup");
26478 tabs.activate("script");
26479
26480 // more advanced tabs, built from javascript
26481 var jtabs = new Roo.TabPanel("jtabs");
26482 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26483
26484 // set up the UpdateManager
26485 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26486 var updater = tab2.getUpdateManager();
26487 updater.setDefaultUrl("ajax1.htm");
26488 tab2.on('activate', updater.refresh, updater, true);
26489
26490 // Use setUrl for Ajax loading
26491 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26492 tab3.setUrl("ajax2.htm", null, true);
26493
26494 // Disabled tab
26495 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26496 tab4.disable();
26497
26498 jtabs.activate("jtabs-1");
26499  * </code></pre>
26500  * @constructor
26501  * Create a new TabPanel.
26502  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26503  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26504  */
26505 Roo.TabPanel = function(container, config){
26506     /**
26507     * The container element for this TabPanel.
26508     * @type Roo.Element
26509     */
26510     this.el = Roo.get(container, true);
26511     if(config){
26512         if(typeof config == "boolean"){
26513             this.tabPosition = config ? "bottom" : "top";
26514         }else{
26515             Roo.apply(this, config);
26516         }
26517     }
26518     if(this.tabPosition == "bottom"){
26519         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26520         this.el.addClass("x-tabs-bottom");
26521     }
26522     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26523     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26524     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26525     if(Roo.isIE){
26526         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26527     }
26528     if(this.tabPosition != "bottom"){
26529         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26530          * @type Roo.Element
26531          */
26532         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26533         this.el.addClass("x-tabs-top");
26534     }
26535     this.items = [];
26536
26537     this.bodyEl.setStyle("position", "relative");
26538
26539     this.active = null;
26540     this.activateDelegate = this.activate.createDelegate(this);
26541
26542     this.addEvents({
26543         /**
26544          * @event tabchange
26545          * Fires when the active tab changes
26546          * @param {Roo.TabPanel} this
26547          * @param {Roo.TabPanelItem} activePanel The new active tab
26548          */
26549         "tabchange": true,
26550         /**
26551          * @event beforetabchange
26552          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26553          * @param {Roo.TabPanel} this
26554          * @param {Object} e Set cancel to true on this object to cancel the tab change
26555          * @param {Roo.TabPanelItem} tab The tab being changed to
26556          */
26557         "beforetabchange" : true
26558     });
26559
26560     Roo.EventManager.onWindowResize(this.onResize, this);
26561     this.cpad = this.el.getPadding("lr");
26562     this.hiddenCount = 0;
26563
26564
26565     // toolbar on the tabbar support...
26566     if (this.toolbar) {
26567         var tcfg = this.toolbar;
26568         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26569         this.toolbar = new Roo.Toolbar(tcfg);
26570         if (Roo.isSafari) {
26571             var tbl = tcfg.container.child('table', true);
26572             tbl.setAttribute('width', '100%');
26573         }
26574         
26575     }
26576    
26577
26578
26579     Roo.TabPanel.superclass.constructor.call(this);
26580 };
26581
26582 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26583     /*
26584      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26585      */
26586     tabPosition : "top",
26587     /*
26588      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26589      */
26590     currentTabWidth : 0,
26591     /*
26592      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26593      */
26594     minTabWidth : 40,
26595     /*
26596      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26597      */
26598     maxTabWidth : 250,
26599     /*
26600      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26601      */
26602     preferredTabWidth : 175,
26603     /*
26604      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26605      */
26606     resizeTabs : false,
26607     /*
26608      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26609      */
26610     monitorResize : true,
26611     /*
26612      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26613      */
26614     toolbar : false,
26615
26616     /**
26617      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26618      * @param {String} id The id of the div to use <b>or create</b>
26619      * @param {String} text The text for the tab
26620      * @param {String} content (optional) Content to put in the TabPanelItem body
26621      * @param {Boolean} closable (optional) True to create a close icon on the tab
26622      * @return {Roo.TabPanelItem} The created TabPanelItem
26623      */
26624     addTab : function(id, text, content, closable){
26625         var item = new Roo.TabPanelItem(this, id, text, closable);
26626         this.addTabItem(item);
26627         if(content){
26628             item.setContent(content);
26629         }
26630         return item;
26631     },
26632
26633     /**
26634      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26635      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26636      * @return {Roo.TabPanelItem}
26637      */
26638     getTab : function(id){
26639         return this.items[id];
26640     },
26641
26642     /**
26643      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26644      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26645      */
26646     hideTab : function(id){
26647         var t = this.items[id];
26648         if(!t.isHidden()){
26649            t.setHidden(true);
26650            this.hiddenCount++;
26651            this.autoSizeTabs();
26652         }
26653     },
26654
26655     /**
26656      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26657      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26658      */
26659     unhideTab : function(id){
26660         var t = this.items[id];
26661         if(t.isHidden()){
26662            t.setHidden(false);
26663            this.hiddenCount--;
26664            this.autoSizeTabs();
26665         }
26666     },
26667
26668     /**
26669      * Adds an existing {@link Roo.TabPanelItem}.
26670      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26671      */
26672     addTabItem : function(item){
26673         this.items[item.id] = item;
26674         this.items.push(item);
26675         if(this.resizeTabs){
26676            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26677            this.autoSizeTabs();
26678         }else{
26679             item.autoSize();
26680         }
26681     },
26682
26683     /**
26684      * Removes a {@link Roo.TabPanelItem}.
26685      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26686      */
26687     removeTab : function(id){
26688         var items = this.items;
26689         var tab = items[id];
26690         if(!tab) { return; }
26691         var index = items.indexOf(tab);
26692         if(this.active == tab && items.length > 1){
26693             var newTab = this.getNextAvailable(index);
26694             if(newTab) {
26695                 newTab.activate();
26696             }
26697         }
26698         this.stripEl.dom.removeChild(tab.pnode.dom);
26699         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26700             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26701         }
26702         items.splice(index, 1);
26703         delete this.items[tab.id];
26704         tab.fireEvent("close", tab);
26705         tab.purgeListeners();
26706         this.autoSizeTabs();
26707     },
26708
26709     getNextAvailable : function(start){
26710         var items = this.items;
26711         var index = start;
26712         // look for a next tab that will slide over to
26713         // replace the one being removed
26714         while(index < items.length){
26715             var item = items[++index];
26716             if(item && !item.isHidden()){
26717                 return item;
26718             }
26719         }
26720         // if one isn't found select the previous tab (on the left)
26721         index = start;
26722         while(index >= 0){
26723             var item = items[--index];
26724             if(item && !item.isHidden()){
26725                 return item;
26726             }
26727         }
26728         return null;
26729     },
26730
26731     /**
26732      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26733      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26734      */
26735     disableTab : function(id){
26736         var tab = this.items[id];
26737         if(tab && this.active != tab){
26738             tab.disable();
26739         }
26740     },
26741
26742     /**
26743      * Enables a {@link Roo.TabPanelItem} that is disabled.
26744      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26745      */
26746     enableTab : function(id){
26747         var tab = this.items[id];
26748         tab.enable();
26749     },
26750
26751     /**
26752      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26753      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26754      * @return {Roo.TabPanelItem} The TabPanelItem.
26755      */
26756     activate : function(id){
26757         var tab = this.items[id];
26758         if(!tab){
26759             return null;
26760         }
26761         if(tab == this.active || tab.disabled){
26762             return tab;
26763         }
26764         var e = {};
26765         this.fireEvent("beforetabchange", this, e, tab);
26766         if(e.cancel !== true && !tab.disabled){
26767             if(this.active){
26768                 this.active.hide();
26769             }
26770             this.active = this.items[id];
26771             this.active.show();
26772             this.fireEvent("tabchange", this, this.active);
26773         }
26774         return tab;
26775     },
26776
26777     /**
26778      * Gets the active {@link Roo.TabPanelItem}.
26779      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26780      */
26781     getActiveTab : function(){
26782         return this.active;
26783     },
26784
26785     /**
26786      * Updates the tab body element to fit the height of the container element
26787      * for overflow scrolling
26788      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26789      */
26790     syncHeight : function(targetHeight){
26791         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26792         var bm = this.bodyEl.getMargins();
26793         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26794         this.bodyEl.setHeight(newHeight);
26795         return newHeight;
26796     },
26797
26798     onResize : function(){
26799         if(this.monitorResize){
26800             this.autoSizeTabs();
26801         }
26802     },
26803
26804     /**
26805      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26806      */
26807     beginUpdate : function(){
26808         this.updating = true;
26809     },
26810
26811     /**
26812      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26813      */
26814     endUpdate : function(){
26815         this.updating = false;
26816         this.autoSizeTabs();
26817     },
26818
26819     /**
26820      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26821      */
26822     autoSizeTabs : function(){
26823         var count = this.items.length;
26824         var vcount = count - this.hiddenCount;
26825         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26826         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26827         var availWidth = Math.floor(w / vcount);
26828         var b = this.stripBody;
26829         if(b.getWidth() > w){
26830             var tabs = this.items;
26831             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26832             if(availWidth < this.minTabWidth){
26833                 /*if(!this.sleft){    // incomplete scrolling code
26834                     this.createScrollButtons();
26835                 }
26836                 this.showScroll();
26837                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26838             }
26839         }else{
26840             if(this.currentTabWidth < this.preferredTabWidth){
26841                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26842             }
26843         }
26844     },
26845
26846     /**
26847      * Returns the number of tabs in this TabPanel.
26848      * @return {Number}
26849      */
26850      getCount : function(){
26851          return this.items.length;
26852      },
26853
26854     /**
26855      * Resizes all the tabs to the passed width
26856      * @param {Number} The new width
26857      */
26858     setTabWidth : function(width){
26859         this.currentTabWidth = width;
26860         for(var i = 0, len = this.items.length; i < len; i++) {
26861                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26862         }
26863     },
26864
26865     /**
26866      * Destroys this TabPanel
26867      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26868      */
26869     destroy : function(removeEl){
26870         Roo.EventManager.removeResizeListener(this.onResize, this);
26871         for(var i = 0, len = this.items.length; i < len; i++){
26872             this.items[i].purgeListeners();
26873         }
26874         if(removeEl === true){
26875             this.el.update("");
26876             this.el.remove();
26877         }
26878     }
26879 });
26880
26881 /**
26882  * @class Roo.TabPanelItem
26883  * @extends Roo.util.Observable
26884  * Represents an individual item (tab plus body) in a TabPanel.
26885  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26886  * @param {String} id The id of this TabPanelItem
26887  * @param {String} text The text for the tab of this TabPanelItem
26888  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26889  */
26890 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26891     /**
26892      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26893      * @type Roo.TabPanel
26894      */
26895     this.tabPanel = tabPanel;
26896     /**
26897      * The id for this TabPanelItem
26898      * @type String
26899      */
26900     this.id = id;
26901     /** @private */
26902     this.disabled = false;
26903     /** @private */
26904     this.text = text;
26905     /** @private */
26906     this.loaded = false;
26907     this.closable = closable;
26908
26909     /**
26910      * The body element for this TabPanelItem.
26911      * @type Roo.Element
26912      */
26913     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26914     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26915     this.bodyEl.setStyle("display", "block");
26916     this.bodyEl.setStyle("zoom", "1");
26917     this.hideAction();
26918
26919     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26920     /** @private */
26921     this.el = Roo.get(els.el, true);
26922     this.inner = Roo.get(els.inner, true);
26923     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26924     this.pnode = Roo.get(els.el.parentNode, true);
26925     this.el.on("mousedown", this.onTabMouseDown, this);
26926     this.el.on("click", this.onTabClick, this);
26927     /** @private */
26928     if(closable){
26929         var c = Roo.get(els.close, true);
26930         c.dom.title = this.closeText;
26931         c.addClassOnOver("close-over");
26932         c.on("click", this.closeClick, this);
26933      }
26934
26935     this.addEvents({
26936          /**
26937          * @event activate
26938          * Fires when this tab becomes the active tab.
26939          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26940          * @param {Roo.TabPanelItem} this
26941          */
26942         "activate": true,
26943         /**
26944          * @event beforeclose
26945          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26946          * @param {Roo.TabPanelItem} this
26947          * @param {Object} e Set cancel to true on this object to cancel the close.
26948          */
26949         "beforeclose": true,
26950         /**
26951          * @event close
26952          * Fires when this tab is closed.
26953          * @param {Roo.TabPanelItem} this
26954          */
26955          "close": true,
26956         /**
26957          * @event deactivate
26958          * Fires when this tab is no longer the active tab.
26959          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26960          * @param {Roo.TabPanelItem} this
26961          */
26962          "deactivate" : true
26963     });
26964     this.hidden = false;
26965
26966     Roo.TabPanelItem.superclass.constructor.call(this);
26967 };
26968
26969 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26970     purgeListeners : function(){
26971        Roo.util.Observable.prototype.purgeListeners.call(this);
26972        this.el.removeAllListeners();
26973     },
26974     /**
26975      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26976      */
26977     show : function(){
26978         this.pnode.addClass("on");
26979         this.showAction();
26980         if(Roo.isOpera){
26981             this.tabPanel.stripWrap.repaint();
26982         }
26983         this.fireEvent("activate", this.tabPanel, this);
26984     },
26985
26986     /**
26987      * Returns true if this tab is the active tab.
26988      * @return {Boolean}
26989      */
26990     isActive : function(){
26991         return this.tabPanel.getActiveTab() == this;
26992     },
26993
26994     /**
26995      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26996      */
26997     hide : function(){
26998         this.pnode.removeClass("on");
26999         this.hideAction();
27000         this.fireEvent("deactivate", this.tabPanel, this);
27001     },
27002
27003     hideAction : function(){
27004         this.bodyEl.hide();
27005         this.bodyEl.setStyle("position", "absolute");
27006         this.bodyEl.setLeft("-20000px");
27007         this.bodyEl.setTop("-20000px");
27008     },
27009
27010     showAction : function(){
27011         this.bodyEl.setStyle("position", "relative");
27012         this.bodyEl.setTop("");
27013         this.bodyEl.setLeft("");
27014         this.bodyEl.show();
27015     },
27016
27017     /**
27018      * Set the tooltip for the tab.
27019      * @param {String} tooltip The tab's tooltip
27020      */
27021     setTooltip : function(text){
27022         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27023             this.textEl.dom.qtip = text;
27024             this.textEl.dom.removeAttribute('title');
27025         }else{
27026             this.textEl.dom.title = text;
27027         }
27028     },
27029
27030     onTabClick : function(e){
27031         e.preventDefault();
27032         this.tabPanel.activate(this.id);
27033     },
27034
27035     onTabMouseDown : function(e){
27036         e.preventDefault();
27037         this.tabPanel.activate(this.id);
27038     },
27039
27040     getWidth : function(){
27041         return this.inner.getWidth();
27042     },
27043
27044     setWidth : function(width){
27045         var iwidth = width - this.pnode.getPadding("lr");
27046         this.inner.setWidth(iwidth);
27047         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27048         this.pnode.setWidth(width);
27049     },
27050
27051     /**
27052      * Show or hide the tab
27053      * @param {Boolean} hidden True to hide or false to show.
27054      */
27055     setHidden : function(hidden){
27056         this.hidden = hidden;
27057         this.pnode.setStyle("display", hidden ? "none" : "");
27058     },
27059
27060     /**
27061      * Returns true if this tab is "hidden"
27062      * @return {Boolean}
27063      */
27064     isHidden : function(){
27065         return this.hidden;
27066     },
27067
27068     /**
27069      * Returns the text for this tab
27070      * @return {String}
27071      */
27072     getText : function(){
27073         return this.text;
27074     },
27075
27076     autoSize : function(){
27077         //this.el.beginMeasure();
27078         this.textEl.setWidth(1);
27079         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27080         //this.el.endMeasure();
27081     },
27082
27083     /**
27084      * Sets the text for the tab (Note: this also sets the tooltip text)
27085      * @param {String} text The tab's text and tooltip
27086      */
27087     setText : function(text){
27088         this.text = text;
27089         this.textEl.update(text);
27090         this.setTooltip(text);
27091         if(!this.tabPanel.resizeTabs){
27092             this.autoSize();
27093         }
27094     },
27095     /**
27096      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27097      */
27098     activate : function(){
27099         this.tabPanel.activate(this.id);
27100     },
27101
27102     /**
27103      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27104      */
27105     disable : function(){
27106         if(this.tabPanel.active != this){
27107             this.disabled = true;
27108             this.pnode.addClass("disabled");
27109         }
27110     },
27111
27112     /**
27113      * Enables this TabPanelItem if it was previously disabled.
27114      */
27115     enable : function(){
27116         this.disabled = false;
27117         this.pnode.removeClass("disabled");
27118     },
27119
27120     /**
27121      * Sets the content for this TabPanelItem.
27122      * @param {String} content The content
27123      * @param {Boolean} loadScripts true to look for and load scripts
27124      */
27125     setContent : function(content, loadScripts){
27126         this.bodyEl.update(content, loadScripts);
27127     },
27128
27129     /**
27130      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27131      * @return {Roo.UpdateManager} The UpdateManager
27132      */
27133     getUpdateManager : function(){
27134         return this.bodyEl.getUpdateManager();
27135     },
27136
27137     /**
27138      * Set a URL to be used to load the content for this TabPanelItem.
27139      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27140      * @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)
27141      * @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)
27142      * @return {Roo.UpdateManager} The UpdateManager
27143      */
27144     setUrl : function(url, params, loadOnce){
27145         if(this.refreshDelegate){
27146             this.un('activate', this.refreshDelegate);
27147         }
27148         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27149         this.on("activate", this.refreshDelegate);
27150         return this.bodyEl.getUpdateManager();
27151     },
27152
27153     /** @private */
27154     _handleRefresh : function(url, params, loadOnce){
27155         if(!loadOnce || !this.loaded){
27156             var updater = this.bodyEl.getUpdateManager();
27157             updater.update(url, params, this._setLoaded.createDelegate(this));
27158         }
27159     },
27160
27161     /**
27162      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27163      *   Will fail silently if the setUrl method has not been called.
27164      *   This does not activate the panel, just updates its content.
27165      */
27166     refresh : function(){
27167         if(this.refreshDelegate){
27168            this.loaded = false;
27169            this.refreshDelegate();
27170         }
27171     },
27172
27173     /** @private */
27174     _setLoaded : function(){
27175         this.loaded = true;
27176     },
27177
27178     /** @private */
27179     closeClick : function(e){
27180         var o = {};
27181         e.stopEvent();
27182         this.fireEvent("beforeclose", this, o);
27183         if(o.cancel !== true){
27184             this.tabPanel.removeTab(this.id);
27185         }
27186     },
27187     /**
27188      * The text displayed in the tooltip for the close icon.
27189      * @type String
27190      */
27191     closeText : "Close this tab"
27192 });
27193
27194 /** @private */
27195 Roo.TabPanel.prototype.createStrip = function(container){
27196     var strip = document.createElement("div");
27197     strip.className = "x-tabs-wrap";
27198     container.appendChild(strip);
27199     return strip;
27200 };
27201 /** @private */
27202 Roo.TabPanel.prototype.createStripList = function(strip){
27203     // div wrapper for retard IE
27204     // returns the "tr" element.
27205     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27206         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27207         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27208     return strip.firstChild.firstChild.firstChild.firstChild;
27209 };
27210 /** @private */
27211 Roo.TabPanel.prototype.createBody = function(container){
27212     var body = document.createElement("div");
27213     Roo.id(body, "tab-body");
27214     Roo.fly(body).addClass("x-tabs-body");
27215     container.appendChild(body);
27216     return body;
27217 };
27218 /** @private */
27219 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27220     var body = Roo.getDom(id);
27221     if(!body){
27222         body = document.createElement("div");
27223         body.id = id;
27224     }
27225     Roo.fly(body).addClass("x-tabs-item-body");
27226     bodyEl.insertBefore(body, bodyEl.firstChild);
27227     return body;
27228 };
27229 /** @private */
27230 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27231     var td = document.createElement("td");
27232     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27233     //stripEl.appendChild(td);
27234     if(closable){
27235         td.className = "x-tabs-closable";
27236         if(!this.closeTpl){
27237             this.closeTpl = new Roo.Template(
27238                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27239                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27240                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27241             );
27242         }
27243         var el = this.closeTpl.overwrite(td, {"text": text});
27244         var close = el.getElementsByTagName("div")[0];
27245         var inner = el.getElementsByTagName("em")[0];
27246         return {"el": el, "close": close, "inner": inner};
27247     } else {
27248         if(!this.tabTpl){
27249             this.tabTpl = new Roo.Template(
27250                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27251                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27252             );
27253         }
27254         var el = this.tabTpl.overwrite(td, {"text": text});
27255         var inner = el.getElementsByTagName("em")[0];
27256         return {"el": el, "inner": inner};
27257     }
27258 };/*
27259  * Based on:
27260  * Ext JS Library 1.1.1
27261  * Copyright(c) 2006-2007, Ext JS, LLC.
27262  *
27263  * Originally Released Under LGPL - original licence link has changed is not relivant.
27264  *
27265  * Fork - LGPL
27266  * <script type="text/javascript">
27267  */
27268
27269 /**
27270  * @class Roo.Button
27271  * @extends Roo.util.Observable
27272  * Simple Button class
27273  * @cfg {String} text The button text
27274  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27275  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27276  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27277  * @cfg {Object} scope The scope of the handler
27278  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27279  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27280  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27281  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27282  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27283  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27284    applies if enableToggle = true)
27285  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27286  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27287   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27288  * @constructor
27289  * Create a new button
27290  * @param {Object} config The config object
27291  */
27292 Roo.Button = function(renderTo, config)
27293 {
27294     if (!config) {
27295         config = renderTo;
27296         renderTo = config.renderTo || false;
27297     }
27298     
27299     Roo.apply(this, config);
27300     this.addEvents({
27301         /**
27302              * @event click
27303              * Fires when this button is clicked
27304              * @param {Button} this
27305              * @param {EventObject} e The click event
27306              */
27307             "click" : true,
27308         /**
27309              * @event toggle
27310              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27311              * @param {Button} this
27312              * @param {Boolean} pressed
27313              */
27314             "toggle" : true,
27315         /**
27316              * @event mouseover
27317              * Fires when the mouse hovers over the button
27318              * @param {Button} this
27319              * @param {Event} e The event object
27320              */
27321         'mouseover' : true,
27322         /**
27323              * @event mouseout
27324              * Fires when the mouse exits the button
27325              * @param {Button} this
27326              * @param {Event} e The event object
27327              */
27328         'mouseout': true,
27329          /**
27330              * @event render
27331              * Fires when the button is rendered
27332              * @param {Button} this
27333              */
27334         'render': true
27335     });
27336     if(this.menu){
27337         this.menu = Roo.menu.MenuMgr.get(this.menu);
27338     }
27339     // register listeners first!!  - so render can be captured..
27340     Roo.util.Observable.call(this);
27341     if(renderTo){
27342         this.render(renderTo);
27343     }
27344     
27345   
27346 };
27347
27348 Roo.extend(Roo.Button, Roo.util.Observable, {
27349     /**
27350      * 
27351      */
27352     
27353     /**
27354      * Read-only. True if this button is hidden
27355      * @type Boolean
27356      */
27357     hidden : false,
27358     /**
27359      * Read-only. True if this button is disabled
27360      * @type Boolean
27361      */
27362     disabled : false,
27363     /**
27364      * Read-only. True if this button is pressed (only if enableToggle = true)
27365      * @type Boolean
27366      */
27367     pressed : false,
27368
27369     /**
27370      * @cfg {Number} tabIndex 
27371      * The DOM tabIndex for this button (defaults to undefined)
27372      */
27373     tabIndex : undefined,
27374
27375     /**
27376      * @cfg {Boolean} enableToggle
27377      * True to enable pressed/not pressed toggling (defaults to false)
27378      */
27379     enableToggle: false,
27380     /**
27381      * @cfg {Mixed} menu
27382      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27383      */
27384     menu : undefined,
27385     /**
27386      * @cfg {String} menuAlign
27387      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27388      */
27389     menuAlign : "tl-bl?",
27390
27391     /**
27392      * @cfg {String} iconCls
27393      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27394      */
27395     iconCls : undefined,
27396     /**
27397      * @cfg {String} type
27398      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27399      */
27400     type : 'button',
27401
27402     // private
27403     menuClassTarget: 'tr',
27404
27405     /**
27406      * @cfg {String} clickEvent
27407      * The type of event to map to the button's event handler (defaults to 'click')
27408      */
27409     clickEvent : 'click',
27410
27411     /**
27412      * @cfg {Boolean} handleMouseEvents
27413      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27414      */
27415     handleMouseEvents : true,
27416
27417     /**
27418      * @cfg {String} tooltipType
27419      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27420      */
27421     tooltipType : 'qtip',
27422
27423     /**
27424      * @cfg {String} cls
27425      * A CSS class to apply to the button's main element.
27426      */
27427     
27428     /**
27429      * @cfg {Roo.Template} template (Optional)
27430      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27431      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27432      * require code modifications if required elements (e.g. a button) aren't present.
27433      */
27434
27435     // private
27436     render : function(renderTo){
27437         var btn;
27438         if(this.hideParent){
27439             this.parentEl = Roo.get(renderTo);
27440         }
27441         if(!this.dhconfig){
27442             if(!this.template){
27443                 if(!Roo.Button.buttonTemplate){
27444                     // hideous table template
27445                     Roo.Button.buttonTemplate = new Roo.Template(
27446                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27447                         '<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>',
27448                         "</tr></tbody></table>");
27449                 }
27450                 this.template = Roo.Button.buttonTemplate;
27451             }
27452             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27453             var btnEl = btn.child("button:first");
27454             btnEl.on('focus', this.onFocus, this);
27455             btnEl.on('blur', this.onBlur, this);
27456             if(this.cls){
27457                 btn.addClass(this.cls);
27458             }
27459             if(this.icon){
27460                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27461             }
27462             if(this.iconCls){
27463                 btnEl.addClass(this.iconCls);
27464                 if(!this.cls){
27465                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27466                 }
27467             }
27468             if(this.tabIndex !== undefined){
27469                 btnEl.dom.tabIndex = this.tabIndex;
27470             }
27471             if(this.tooltip){
27472                 if(typeof this.tooltip == 'object'){
27473                     Roo.QuickTips.tips(Roo.apply({
27474                           target: btnEl.id
27475                     }, this.tooltip));
27476                 } else {
27477                     btnEl.dom[this.tooltipType] = this.tooltip;
27478                 }
27479             }
27480         }else{
27481             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27482         }
27483         this.el = btn;
27484         if(this.id){
27485             this.el.dom.id = this.el.id = this.id;
27486         }
27487         if(this.menu){
27488             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27489             this.menu.on("show", this.onMenuShow, this);
27490             this.menu.on("hide", this.onMenuHide, this);
27491         }
27492         btn.addClass("x-btn");
27493         if(Roo.isIE && !Roo.isIE7){
27494             this.autoWidth.defer(1, this);
27495         }else{
27496             this.autoWidth();
27497         }
27498         if(this.handleMouseEvents){
27499             btn.on("mouseover", this.onMouseOver, this);
27500             btn.on("mouseout", this.onMouseOut, this);
27501             btn.on("mousedown", this.onMouseDown, this);
27502         }
27503         btn.on(this.clickEvent, this.onClick, this);
27504         //btn.on("mouseup", this.onMouseUp, this);
27505         if(this.hidden){
27506             this.hide();
27507         }
27508         if(this.disabled){
27509             this.disable();
27510         }
27511         Roo.ButtonToggleMgr.register(this);
27512         if(this.pressed){
27513             this.el.addClass("x-btn-pressed");
27514         }
27515         if(this.repeat){
27516             var repeater = new Roo.util.ClickRepeater(btn,
27517                 typeof this.repeat == "object" ? this.repeat : {}
27518             );
27519             repeater.on("click", this.onClick,  this);
27520         }
27521         
27522         this.fireEvent('render', this);
27523         
27524     },
27525     /**
27526      * Returns the button's underlying element
27527      * @return {Roo.Element} The element
27528      */
27529     getEl : function(){
27530         return this.el;  
27531     },
27532     
27533     /**
27534      * Destroys this Button and removes any listeners.
27535      */
27536     destroy : function(){
27537         Roo.ButtonToggleMgr.unregister(this);
27538         this.el.removeAllListeners();
27539         this.purgeListeners();
27540         this.el.remove();
27541     },
27542
27543     // private
27544     autoWidth : function(){
27545         if(this.el){
27546             this.el.setWidth("auto");
27547             if(Roo.isIE7 && Roo.isStrict){
27548                 var ib = this.el.child('button');
27549                 if(ib && ib.getWidth() > 20){
27550                     ib.clip();
27551                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27552                 }
27553             }
27554             if(this.minWidth){
27555                 if(this.hidden){
27556                     this.el.beginMeasure();
27557                 }
27558                 if(this.el.getWidth() < this.minWidth){
27559                     this.el.setWidth(this.minWidth);
27560                 }
27561                 if(this.hidden){
27562                     this.el.endMeasure();
27563                 }
27564             }
27565         }
27566     },
27567
27568     /**
27569      * Assigns this button's click handler
27570      * @param {Function} handler The function to call when the button is clicked
27571      * @param {Object} scope (optional) Scope for the function passed in
27572      */
27573     setHandler : function(handler, scope){
27574         this.handler = handler;
27575         this.scope = scope;  
27576     },
27577     
27578     /**
27579      * Sets this button's text
27580      * @param {String} text The button text
27581      */
27582     setText : function(text){
27583         this.text = text;
27584         if(this.el){
27585             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27586         }
27587         this.autoWidth();
27588     },
27589     
27590     /**
27591      * Gets the text for this button
27592      * @return {String} The button text
27593      */
27594     getText : function(){
27595         return this.text;  
27596     },
27597     
27598     /**
27599      * Show this button
27600      */
27601     show: function(){
27602         this.hidden = false;
27603         if(this.el){
27604             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27605         }
27606     },
27607     
27608     /**
27609      * Hide this button
27610      */
27611     hide: function(){
27612         this.hidden = true;
27613         if(this.el){
27614             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27615         }
27616     },
27617     
27618     /**
27619      * Convenience function for boolean show/hide
27620      * @param {Boolean} visible True to show, false to hide
27621      */
27622     setVisible: function(visible){
27623         if(visible) {
27624             this.show();
27625         }else{
27626             this.hide();
27627         }
27628     },
27629     
27630     /**
27631      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27632      * @param {Boolean} state (optional) Force a particular state
27633      */
27634     toggle : function(state){
27635         state = state === undefined ? !this.pressed : state;
27636         if(state != this.pressed){
27637             if(state){
27638                 this.el.addClass("x-btn-pressed");
27639                 this.pressed = true;
27640                 this.fireEvent("toggle", this, true);
27641             }else{
27642                 this.el.removeClass("x-btn-pressed");
27643                 this.pressed = false;
27644                 this.fireEvent("toggle", this, false);
27645             }
27646             if(this.toggleHandler){
27647                 this.toggleHandler.call(this.scope || this, this, state);
27648             }
27649         }
27650     },
27651     
27652     /**
27653      * Focus the button
27654      */
27655     focus : function(){
27656         this.el.child('button:first').focus();
27657     },
27658     
27659     /**
27660      * Disable this button
27661      */
27662     disable : function(){
27663         if(this.el){
27664             this.el.addClass("x-btn-disabled");
27665         }
27666         this.disabled = true;
27667     },
27668     
27669     /**
27670      * Enable this button
27671      */
27672     enable : function(){
27673         if(this.el){
27674             this.el.removeClass("x-btn-disabled");
27675         }
27676         this.disabled = false;
27677     },
27678
27679     /**
27680      * Convenience function for boolean enable/disable
27681      * @param {Boolean} enabled True to enable, false to disable
27682      */
27683     setDisabled : function(v){
27684         this[v !== true ? "enable" : "disable"]();
27685     },
27686
27687     // private
27688     onClick : function(e){
27689         if(e){
27690             e.preventDefault();
27691         }
27692         if(e.button != 0){
27693             return;
27694         }
27695         if(!this.disabled){
27696             if(this.enableToggle){
27697                 this.toggle();
27698             }
27699             if(this.menu && !this.menu.isVisible()){
27700                 this.menu.show(this.el, this.menuAlign);
27701             }
27702             this.fireEvent("click", this, e);
27703             if(this.handler){
27704                 this.el.removeClass("x-btn-over");
27705                 this.handler.call(this.scope || this, this, e);
27706             }
27707         }
27708     },
27709     // private
27710     onMouseOver : function(e){
27711         if(!this.disabled){
27712             this.el.addClass("x-btn-over");
27713             this.fireEvent('mouseover', this, e);
27714         }
27715     },
27716     // private
27717     onMouseOut : function(e){
27718         if(!e.within(this.el,  true)){
27719             this.el.removeClass("x-btn-over");
27720             this.fireEvent('mouseout', this, e);
27721         }
27722     },
27723     // private
27724     onFocus : function(e){
27725         if(!this.disabled){
27726             this.el.addClass("x-btn-focus");
27727         }
27728     },
27729     // private
27730     onBlur : function(e){
27731         this.el.removeClass("x-btn-focus");
27732     },
27733     // private
27734     onMouseDown : function(e){
27735         if(!this.disabled && e.button == 0){
27736             this.el.addClass("x-btn-click");
27737             Roo.get(document).on('mouseup', this.onMouseUp, this);
27738         }
27739     },
27740     // private
27741     onMouseUp : function(e){
27742         if(e.button == 0){
27743             this.el.removeClass("x-btn-click");
27744             Roo.get(document).un('mouseup', this.onMouseUp, this);
27745         }
27746     },
27747     // private
27748     onMenuShow : function(e){
27749         this.el.addClass("x-btn-menu-active");
27750     },
27751     // private
27752     onMenuHide : function(e){
27753         this.el.removeClass("x-btn-menu-active");
27754     }   
27755 });
27756
27757 // Private utility class used by Button
27758 Roo.ButtonToggleMgr = function(){
27759    var groups = {};
27760    
27761    function toggleGroup(btn, state){
27762        if(state){
27763            var g = groups[btn.toggleGroup];
27764            for(var i = 0, l = g.length; i < l; i++){
27765                if(g[i] != btn){
27766                    g[i].toggle(false);
27767                }
27768            }
27769        }
27770    }
27771    
27772    return {
27773        register : function(btn){
27774            if(!btn.toggleGroup){
27775                return;
27776            }
27777            var g = groups[btn.toggleGroup];
27778            if(!g){
27779                g = groups[btn.toggleGroup] = [];
27780            }
27781            g.push(btn);
27782            btn.on("toggle", toggleGroup);
27783        },
27784        
27785        unregister : function(btn){
27786            if(!btn.toggleGroup){
27787                return;
27788            }
27789            var g = groups[btn.toggleGroup];
27790            if(g){
27791                g.remove(btn);
27792                btn.un("toggle", toggleGroup);
27793            }
27794        }
27795    };
27796 }();/*
27797  * Based on:
27798  * Ext JS Library 1.1.1
27799  * Copyright(c) 2006-2007, Ext JS, LLC.
27800  *
27801  * Originally Released Under LGPL - original licence link has changed is not relivant.
27802  *
27803  * Fork - LGPL
27804  * <script type="text/javascript">
27805  */
27806  
27807 /**
27808  * @class Roo.SplitButton
27809  * @extends Roo.Button
27810  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27811  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27812  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27813  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27814  * @cfg {String} arrowTooltip The title attribute of the arrow
27815  * @constructor
27816  * Create a new menu button
27817  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27818  * @param {Object} config The config object
27819  */
27820 Roo.SplitButton = function(renderTo, config){
27821     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27822     /**
27823      * @event arrowclick
27824      * Fires when this button's arrow is clicked
27825      * @param {SplitButton} this
27826      * @param {EventObject} e The click event
27827      */
27828     this.addEvents({"arrowclick":true});
27829 };
27830
27831 Roo.extend(Roo.SplitButton, Roo.Button, {
27832     render : function(renderTo){
27833         // this is one sweet looking template!
27834         var tpl = new Roo.Template(
27835             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27836             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27837             '<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>',
27838             "</tbody></table></td><td>",
27839             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27840             '<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>',
27841             "</tbody></table></td></tr></table>"
27842         );
27843         var btn = tpl.append(renderTo, [this.text, this.type], true);
27844         var btnEl = btn.child("button");
27845         if(this.cls){
27846             btn.addClass(this.cls);
27847         }
27848         if(this.icon){
27849             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27850         }
27851         if(this.iconCls){
27852             btnEl.addClass(this.iconCls);
27853             if(!this.cls){
27854                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27855             }
27856         }
27857         this.el = btn;
27858         if(this.handleMouseEvents){
27859             btn.on("mouseover", this.onMouseOver, this);
27860             btn.on("mouseout", this.onMouseOut, this);
27861             btn.on("mousedown", this.onMouseDown, this);
27862             btn.on("mouseup", this.onMouseUp, this);
27863         }
27864         btn.on(this.clickEvent, this.onClick, this);
27865         if(this.tooltip){
27866             if(typeof this.tooltip == 'object'){
27867                 Roo.QuickTips.tips(Roo.apply({
27868                       target: btnEl.id
27869                 }, this.tooltip));
27870             } else {
27871                 btnEl.dom[this.tooltipType] = this.tooltip;
27872             }
27873         }
27874         if(this.arrowTooltip){
27875             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27876         }
27877         if(this.hidden){
27878             this.hide();
27879         }
27880         if(this.disabled){
27881             this.disable();
27882         }
27883         if(this.pressed){
27884             this.el.addClass("x-btn-pressed");
27885         }
27886         if(Roo.isIE && !Roo.isIE7){
27887             this.autoWidth.defer(1, this);
27888         }else{
27889             this.autoWidth();
27890         }
27891         if(this.menu){
27892             this.menu.on("show", this.onMenuShow, this);
27893             this.menu.on("hide", this.onMenuHide, this);
27894         }
27895         this.fireEvent('render', this);
27896     },
27897
27898     // private
27899     autoWidth : function(){
27900         if(this.el){
27901             var tbl = this.el.child("table:first");
27902             var tbl2 = this.el.child("table:last");
27903             this.el.setWidth("auto");
27904             tbl.setWidth("auto");
27905             if(Roo.isIE7 && Roo.isStrict){
27906                 var ib = this.el.child('button:first');
27907                 if(ib && ib.getWidth() > 20){
27908                     ib.clip();
27909                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27910                 }
27911             }
27912             if(this.minWidth){
27913                 if(this.hidden){
27914                     this.el.beginMeasure();
27915                 }
27916                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27917                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27918                 }
27919                 if(this.hidden){
27920                     this.el.endMeasure();
27921                 }
27922             }
27923             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27924         } 
27925     },
27926     /**
27927      * Sets this button's click handler
27928      * @param {Function} handler The function to call when the button is clicked
27929      * @param {Object} scope (optional) Scope for the function passed above
27930      */
27931     setHandler : function(handler, scope){
27932         this.handler = handler;
27933         this.scope = scope;  
27934     },
27935     
27936     /**
27937      * Sets this button's arrow click handler
27938      * @param {Function} handler The function to call when the arrow is clicked
27939      * @param {Object} scope (optional) Scope for the function passed above
27940      */
27941     setArrowHandler : function(handler, scope){
27942         this.arrowHandler = handler;
27943         this.scope = scope;  
27944     },
27945     
27946     /**
27947      * Focus the button
27948      */
27949     focus : function(){
27950         if(this.el){
27951             this.el.child("button:first").focus();
27952         }
27953     },
27954
27955     // private
27956     onClick : function(e){
27957         e.preventDefault();
27958         if(!this.disabled){
27959             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27960                 if(this.menu && !this.menu.isVisible()){
27961                     this.menu.show(this.el, this.menuAlign);
27962                 }
27963                 this.fireEvent("arrowclick", this, e);
27964                 if(this.arrowHandler){
27965                     this.arrowHandler.call(this.scope || this, this, e);
27966                 }
27967             }else{
27968                 this.fireEvent("click", this, e);
27969                 if(this.handler){
27970                     this.handler.call(this.scope || this, this, e);
27971                 }
27972             }
27973         }
27974     },
27975     // private
27976     onMouseDown : function(e){
27977         if(!this.disabled){
27978             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27979         }
27980     },
27981     // private
27982     onMouseUp : function(e){
27983         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27984     }   
27985 });
27986
27987
27988 // backwards compat
27989 Roo.MenuButton = Roo.SplitButton;/*
27990  * Based on:
27991  * Ext JS Library 1.1.1
27992  * Copyright(c) 2006-2007, Ext JS, LLC.
27993  *
27994  * Originally Released Under LGPL - original licence link has changed is not relivant.
27995  *
27996  * Fork - LGPL
27997  * <script type="text/javascript">
27998  */
27999
28000 /**
28001  * @class Roo.Toolbar
28002  * Basic Toolbar class.
28003  * @constructor
28004  * Creates a new Toolbar
28005  * @param {Object} container The config object
28006  */ 
28007 Roo.Toolbar = function(container, buttons, config)
28008 {
28009     /// old consturctor format still supported..
28010     if(container instanceof Array){ // omit the container for later rendering
28011         buttons = container;
28012         config = buttons;
28013         container = null;
28014     }
28015     if (typeof(container) == 'object' && container.xtype) {
28016         config = container;
28017         container = config.container;
28018         buttons = config.buttons || []; // not really - use items!!
28019     }
28020     var xitems = [];
28021     if (config && config.items) {
28022         xitems = config.items;
28023         delete config.items;
28024     }
28025     Roo.apply(this, config);
28026     this.buttons = buttons;
28027     
28028     if(container){
28029         this.render(container);
28030     }
28031     this.xitems = xitems;
28032     Roo.each(xitems, function(b) {
28033         this.add(b);
28034     }, this);
28035     
28036 };
28037
28038 Roo.Toolbar.prototype = {
28039     /**
28040      * @cfg {Array} items
28041      * array of button configs or elements to add (will be converted to a MixedCollection)
28042      */
28043     
28044     /**
28045      * @cfg {String/HTMLElement/Element} container
28046      * The id or element that will contain the toolbar
28047      */
28048     // private
28049     render : function(ct){
28050         this.el = Roo.get(ct);
28051         if(this.cls){
28052             this.el.addClass(this.cls);
28053         }
28054         // using a table allows for vertical alignment
28055         // 100% width is needed by Safari...
28056         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28057         this.tr = this.el.child("tr", true);
28058         var autoId = 0;
28059         this.items = new Roo.util.MixedCollection(false, function(o){
28060             return o.id || ("item" + (++autoId));
28061         });
28062         if(this.buttons){
28063             this.add.apply(this, this.buttons);
28064             delete this.buttons;
28065         }
28066     },
28067
28068     /**
28069      * Adds element(s) to the toolbar -- this function takes a variable number of 
28070      * arguments of mixed type and adds them to the toolbar.
28071      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28072      * <ul>
28073      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28074      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28075      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28076      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28077      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28078      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28079      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28080      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28081      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28082      * </ul>
28083      * @param {Mixed} arg2
28084      * @param {Mixed} etc.
28085      */
28086     add : function(){
28087         var a = arguments, l = a.length;
28088         for(var i = 0; i < l; i++){
28089             this._add(a[i]);
28090         }
28091     },
28092     // private..
28093     _add : function(el) {
28094         
28095         if (el.xtype) {
28096             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28097         }
28098         
28099         if (el.applyTo){ // some kind of form field
28100             return this.addField(el);
28101         } 
28102         if (el.render){ // some kind of Toolbar.Item
28103             return this.addItem(el);
28104         }
28105         if (typeof el == "string"){ // string
28106             if(el == "separator" || el == "-"){
28107                 return this.addSeparator();
28108             }
28109             if (el == " "){
28110                 return this.addSpacer();
28111             }
28112             if(el == "->"){
28113                 return this.addFill();
28114             }
28115             return this.addText(el);
28116             
28117         }
28118         if(el.tagName){ // element
28119             return this.addElement(el);
28120         }
28121         if(typeof el == "object"){ // must be button config?
28122             return this.addButton(el);
28123         }
28124         // and now what?!?!
28125         return false;
28126         
28127     },
28128     
28129     /**
28130      * Add an Xtype element
28131      * @param {Object} xtype Xtype Object
28132      * @return {Object} created Object
28133      */
28134     addxtype : function(e){
28135         return this.add(e);  
28136     },
28137     
28138     /**
28139      * Returns the Element for this toolbar.
28140      * @return {Roo.Element}
28141      */
28142     getEl : function(){
28143         return this.el;  
28144     },
28145     
28146     /**
28147      * Adds a separator
28148      * @return {Roo.Toolbar.Item} The separator item
28149      */
28150     addSeparator : function(){
28151         return this.addItem(new Roo.Toolbar.Separator());
28152     },
28153
28154     /**
28155      * Adds a spacer element
28156      * @return {Roo.Toolbar.Spacer} The spacer item
28157      */
28158     addSpacer : function(){
28159         return this.addItem(new Roo.Toolbar.Spacer());
28160     },
28161
28162     /**
28163      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28164      * @return {Roo.Toolbar.Fill} The fill item
28165      */
28166     addFill : function(){
28167         return this.addItem(new Roo.Toolbar.Fill());
28168     },
28169
28170     /**
28171      * Adds any standard HTML element to the toolbar
28172      * @param {String/HTMLElement/Element} el The element or id of the element to add
28173      * @return {Roo.Toolbar.Item} The element's item
28174      */
28175     addElement : function(el){
28176         return this.addItem(new Roo.Toolbar.Item(el));
28177     },
28178     /**
28179      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28180      * @type Roo.util.MixedCollection  
28181      */
28182     items : false,
28183      
28184     /**
28185      * Adds any Toolbar.Item or subclass
28186      * @param {Roo.Toolbar.Item} item
28187      * @return {Roo.Toolbar.Item} The item
28188      */
28189     addItem : function(item){
28190         var td = this.nextBlock();
28191         item.render(td);
28192         this.items.add(item);
28193         return item;
28194     },
28195     
28196     /**
28197      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28198      * @param {Object/Array} config A button config or array of configs
28199      * @return {Roo.Toolbar.Button/Array}
28200      */
28201     addButton : function(config){
28202         if(config instanceof Array){
28203             var buttons = [];
28204             for(var i = 0, len = config.length; i < len; i++) {
28205                 buttons.push(this.addButton(config[i]));
28206             }
28207             return buttons;
28208         }
28209         var b = config;
28210         if(!(config instanceof Roo.Toolbar.Button)){
28211             b = config.split ?
28212                 new Roo.Toolbar.SplitButton(config) :
28213                 new Roo.Toolbar.Button(config);
28214         }
28215         var td = this.nextBlock();
28216         b.render(td);
28217         this.items.add(b);
28218         return b;
28219     },
28220     
28221     /**
28222      * Adds text to the toolbar
28223      * @param {String} text The text to add
28224      * @return {Roo.Toolbar.Item} The element's item
28225      */
28226     addText : function(text){
28227         return this.addItem(new Roo.Toolbar.TextItem(text));
28228     },
28229     
28230     /**
28231      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28232      * @param {Number} index The index where the item is to be inserted
28233      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28234      * @return {Roo.Toolbar.Button/Item}
28235      */
28236     insertButton : function(index, item){
28237         if(item instanceof Array){
28238             var buttons = [];
28239             for(var i = 0, len = item.length; i < len; i++) {
28240                buttons.push(this.insertButton(index + i, item[i]));
28241             }
28242             return buttons;
28243         }
28244         if (!(item instanceof Roo.Toolbar.Button)){
28245            item = new Roo.Toolbar.Button(item);
28246         }
28247         var td = document.createElement("td");
28248         this.tr.insertBefore(td, this.tr.childNodes[index]);
28249         item.render(td);
28250         this.items.insert(index, item);
28251         return item;
28252     },
28253     
28254     /**
28255      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28256      * @param {Object} config
28257      * @return {Roo.Toolbar.Item} The element's item
28258      */
28259     addDom : function(config, returnEl){
28260         var td = this.nextBlock();
28261         Roo.DomHelper.overwrite(td, config);
28262         var ti = new Roo.Toolbar.Item(td.firstChild);
28263         ti.render(td);
28264         this.items.add(ti);
28265         return ti;
28266     },
28267
28268     /**
28269      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28270      * @type Roo.util.MixedCollection  
28271      */
28272     fields : false,
28273     
28274     /**
28275      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28276      * Note: the field should not have been rendered yet. For a field that has already been
28277      * rendered, use {@link #addElement}.
28278      * @param {Roo.form.Field} field
28279      * @return {Roo.ToolbarItem}
28280      */
28281      
28282       
28283     addField : function(field) {
28284         if (!this.fields) {
28285             var autoId = 0;
28286             this.fields = new Roo.util.MixedCollection(false, function(o){
28287                 return o.id || ("item" + (++autoId));
28288             });
28289
28290         }
28291         
28292         var td = this.nextBlock();
28293         field.render(td);
28294         var ti = new Roo.Toolbar.Item(td.firstChild);
28295         ti.render(td);
28296         this.items.add(ti);
28297         this.fields.add(field);
28298         return ti;
28299     },
28300     /**
28301      * Hide the toolbar
28302      * @method hide
28303      */
28304      
28305       
28306     hide : function()
28307     {
28308         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28309         this.el.child('div').hide();
28310     },
28311     /**
28312      * Show the toolbar
28313      * @method show
28314      */
28315     show : function()
28316     {
28317         this.el.child('div').show();
28318     },
28319       
28320     // private
28321     nextBlock : function(){
28322         var td = document.createElement("td");
28323         this.tr.appendChild(td);
28324         return td;
28325     },
28326
28327     // private
28328     destroy : function(){
28329         if(this.items){ // rendered?
28330             Roo.destroy.apply(Roo, this.items.items);
28331         }
28332         if(this.fields){ // rendered?
28333             Roo.destroy.apply(Roo, this.fields.items);
28334         }
28335         Roo.Element.uncache(this.el, this.tr);
28336     }
28337 };
28338
28339 /**
28340  * @class Roo.Toolbar.Item
28341  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28342  * @constructor
28343  * Creates a new Item
28344  * @param {HTMLElement} el 
28345  */
28346 Roo.Toolbar.Item = function(el){
28347     this.el = Roo.getDom(el);
28348     this.id = Roo.id(this.el);
28349     this.hidden = false;
28350 };
28351
28352 Roo.Toolbar.Item.prototype = {
28353     
28354     /**
28355      * Get this item's HTML Element
28356      * @return {HTMLElement}
28357      */
28358     getEl : function(){
28359        return this.el;  
28360     },
28361
28362     // private
28363     render : function(td){
28364         this.td = td;
28365         td.appendChild(this.el);
28366     },
28367     
28368     /**
28369      * Removes and destroys this item.
28370      */
28371     destroy : function(){
28372         this.td.parentNode.removeChild(this.td);
28373     },
28374     
28375     /**
28376      * Shows this item.
28377      */
28378     show: function(){
28379         this.hidden = false;
28380         this.td.style.display = "";
28381     },
28382     
28383     /**
28384      * Hides this item.
28385      */
28386     hide: function(){
28387         this.hidden = true;
28388         this.td.style.display = "none";
28389     },
28390     
28391     /**
28392      * Convenience function for boolean show/hide.
28393      * @param {Boolean} visible true to show/false to hide
28394      */
28395     setVisible: function(visible){
28396         if(visible) {
28397             this.show();
28398         }else{
28399             this.hide();
28400         }
28401     },
28402     
28403     /**
28404      * Try to focus this item.
28405      */
28406     focus : function(){
28407         Roo.fly(this.el).focus();
28408     },
28409     
28410     /**
28411      * Disables this item.
28412      */
28413     disable : function(){
28414         Roo.fly(this.td).addClass("x-item-disabled");
28415         this.disabled = true;
28416         this.el.disabled = true;
28417     },
28418     
28419     /**
28420      * Enables this item.
28421      */
28422     enable : function(){
28423         Roo.fly(this.td).removeClass("x-item-disabled");
28424         this.disabled = false;
28425         this.el.disabled = false;
28426     }
28427 };
28428
28429
28430 /**
28431  * @class Roo.Toolbar.Separator
28432  * @extends Roo.Toolbar.Item
28433  * A simple toolbar separator class
28434  * @constructor
28435  * Creates a new Separator
28436  */
28437 Roo.Toolbar.Separator = function(){
28438     var s = document.createElement("span");
28439     s.className = "ytb-sep";
28440     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28441 };
28442 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28443     enable:Roo.emptyFn,
28444     disable:Roo.emptyFn,
28445     focus:Roo.emptyFn
28446 });
28447
28448 /**
28449  * @class Roo.Toolbar.Spacer
28450  * @extends Roo.Toolbar.Item
28451  * A simple element that adds extra horizontal space to a toolbar.
28452  * @constructor
28453  * Creates a new Spacer
28454  */
28455 Roo.Toolbar.Spacer = function(){
28456     var s = document.createElement("div");
28457     s.className = "ytb-spacer";
28458     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28459 };
28460 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28461     enable:Roo.emptyFn,
28462     disable:Roo.emptyFn,
28463     focus:Roo.emptyFn
28464 });
28465
28466 /**
28467  * @class Roo.Toolbar.Fill
28468  * @extends Roo.Toolbar.Spacer
28469  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28470  * @constructor
28471  * Creates a new Spacer
28472  */
28473 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28474     // private
28475     render : function(td){
28476         td.style.width = '100%';
28477         Roo.Toolbar.Fill.superclass.render.call(this, td);
28478     }
28479 });
28480
28481 /**
28482  * @class Roo.Toolbar.TextItem
28483  * @extends Roo.Toolbar.Item
28484  * A simple class that renders text directly into a toolbar.
28485  * @constructor
28486  * Creates a new TextItem
28487  * @param {String} text
28488  */
28489 Roo.Toolbar.TextItem = function(text){
28490     if (typeof(text) == 'object') {
28491         text = text.text;
28492     }
28493     var s = document.createElement("span");
28494     s.className = "ytb-text";
28495     s.innerHTML = text;
28496     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28497 };
28498 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28499     enable:Roo.emptyFn,
28500     disable:Roo.emptyFn,
28501     focus:Roo.emptyFn
28502 });
28503
28504 /**
28505  * @class Roo.Toolbar.Button
28506  * @extends Roo.Button
28507  * A button that renders into a toolbar.
28508  * @constructor
28509  * Creates a new Button
28510  * @param {Object} config A standard {@link Roo.Button} config object
28511  */
28512 Roo.Toolbar.Button = function(config){
28513     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28514 };
28515 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28516     render : function(td){
28517         this.td = td;
28518         Roo.Toolbar.Button.superclass.render.call(this, td);
28519     },
28520     
28521     /**
28522      * Removes and destroys this button
28523      */
28524     destroy : function(){
28525         Roo.Toolbar.Button.superclass.destroy.call(this);
28526         this.td.parentNode.removeChild(this.td);
28527     },
28528     
28529     /**
28530      * Shows this button
28531      */
28532     show: function(){
28533         this.hidden = false;
28534         this.td.style.display = "";
28535     },
28536     
28537     /**
28538      * Hides this button
28539      */
28540     hide: function(){
28541         this.hidden = true;
28542         this.td.style.display = "none";
28543     },
28544
28545     /**
28546      * Disables this item
28547      */
28548     disable : function(){
28549         Roo.fly(this.td).addClass("x-item-disabled");
28550         this.disabled = true;
28551     },
28552
28553     /**
28554      * Enables this item
28555      */
28556     enable : function(){
28557         Roo.fly(this.td).removeClass("x-item-disabled");
28558         this.disabled = false;
28559     }
28560 });
28561 // backwards compat
28562 Roo.ToolbarButton = Roo.Toolbar.Button;
28563
28564 /**
28565  * @class Roo.Toolbar.SplitButton
28566  * @extends Roo.SplitButton
28567  * A menu button that renders into a toolbar.
28568  * @constructor
28569  * Creates a new SplitButton
28570  * @param {Object} config A standard {@link Roo.SplitButton} config object
28571  */
28572 Roo.Toolbar.SplitButton = function(config){
28573     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28574 };
28575 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28576     render : function(td){
28577         this.td = td;
28578         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28579     },
28580     
28581     /**
28582      * Removes and destroys this button
28583      */
28584     destroy : function(){
28585         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28586         this.td.parentNode.removeChild(this.td);
28587     },
28588     
28589     /**
28590      * Shows this button
28591      */
28592     show: function(){
28593         this.hidden = false;
28594         this.td.style.display = "";
28595     },
28596     
28597     /**
28598      * Hides this button
28599      */
28600     hide: function(){
28601         this.hidden = true;
28602         this.td.style.display = "none";
28603     }
28604 });
28605
28606 // backwards compat
28607 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28608  * Based on:
28609  * Ext JS Library 1.1.1
28610  * Copyright(c) 2006-2007, Ext JS, LLC.
28611  *
28612  * Originally Released Under LGPL - original licence link has changed is not relivant.
28613  *
28614  * Fork - LGPL
28615  * <script type="text/javascript">
28616  */
28617  
28618 /**
28619  * @class Roo.PagingToolbar
28620  * @extends Roo.Toolbar
28621  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28622  * @constructor
28623  * Create a new PagingToolbar
28624  * @param {Object} config The config object
28625  */
28626 Roo.PagingToolbar = function(el, ds, config)
28627 {
28628     // old args format still supported... - xtype is prefered..
28629     if (typeof(el) == 'object' && el.xtype) {
28630         // created from xtype...
28631         config = el;
28632         ds = el.dataSource;
28633         el = config.container;
28634     }
28635     var items = [];
28636     if (config.items) {
28637         items = config.items;
28638         config.items = [];
28639     }
28640     
28641     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28642     this.ds = ds;
28643     this.cursor = 0;
28644     this.renderButtons(this.el);
28645     this.bind(ds);
28646     
28647     // supprot items array.
28648    
28649     Roo.each(items, function(e) {
28650         this.add(Roo.factory(e));
28651     },this);
28652     
28653 };
28654
28655 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28656     /**
28657      * @cfg {Roo.data.Store} dataSource
28658      * The underlying data store providing the paged data
28659      */
28660     /**
28661      * @cfg {String/HTMLElement/Element} container
28662      * container The id or element that will contain the toolbar
28663      */
28664     /**
28665      * @cfg {Boolean} displayInfo
28666      * True to display the displayMsg (defaults to false)
28667      */
28668     /**
28669      * @cfg {Number} pageSize
28670      * The number of records to display per page (defaults to 20)
28671      */
28672     pageSize: 20,
28673     /**
28674      * @cfg {String} displayMsg
28675      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28676      */
28677     displayMsg : 'Displaying {0} - {1} of {2}',
28678     /**
28679      * @cfg {String} emptyMsg
28680      * The message to display when no records are found (defaults to "No data to display")
28681      */
28682     emptyMsg : 'No data to display',
28683     /**
28684      * Customizable piece of the default paging text (defaults to "Page")
28685      * @type String
28686      */
28687     beforePageText : "Page",
28688     /**
28689      * Customizable piece of the default paging text (defaults to "of %0")
28690      * @type String
28691      */
28692     afterPageText : "of {0}",
28693     /**
28694      * Customizable piece of the default paging text (defaults to "First Page")
28695      * @type String
28696      */
28697     firstText : "First Page",
28698     /**
28699      * Customizable piece of the default paging text (defaults to "Previous Page")
28700      * @type String
28701      */
28702     prevText : "Previous Page",
28703     /**
28704      * Customizable piece of the default paging text (defaults to "Next Page")
28705      * @type String
28706      */
28707     nextText : "Next Page",
28708     /**
28709      * Customizable piece of the default paging text (defaults to "Last Page")
28710      * @type String
28711      */
28712     lastText : "Last Page",
28713     /**
28714      * Customizable piece of the default paging text (defaults to "Refresh")
28715      * @type String
28716      */
28717     refreshText : "Refresh",
28718
28719     // private
28720     renderButtons : function(el){
28721         Roo.PagingToolbar.superclass.render.call(this, el);
28722         this.first = this.addButton({
28723             tooltip: this.firstText,
28724             cls: "x-btn-icon x-grid-page-first",
28725             disabled: true,
28726             handler: this.onClick.createDelegate(this, ["first"])
28727         });
28728         this.prev = this.addButton({
28729             tooltip: this.prevText,
28730             cls: "x-btn-icon x-grid-page-prev",
28731             disabled: true,
28732             handler: this.onClick.createDelegate(this, ["prev"])
28733         });
28734         //this.addSeparator();
28735         this.add(this.beforePageText);
28736         this.field = Roo.get(this.addDom({
28737            tag: "input",
28738            type: "text",
28739            size: "3",
28740            value: "1",
28741            cls: "x-grid-page-number"
28742         }).el);
28743         this.field.on("keydown", this.onPagingKeydown, this);
28744         this.field.on("focus", function(){this.dom.select();});
28745         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28746         this.field.setHeight(18);
28747         //this.addSeparator();
28748         this.next = this.addButton({
28749             tooltip: this.nextText,
28750             cls: "x-btn-icon x-grid-page-next",
28751             disabled: true,
28752             handler: this.onClick.createDelegate(this, ["next"])
28753         });
28754         this.last = this.addButton({
28755             tooltip: this.lastText,
28756             cls: "x-btn-icon x-grid-page-last",
28757             disabled: true,
28758             handler: this.onClick.createDelegate(this, ["last"])
28759         });
28760         //this.addSeparator();
28761         this.loading = this.addButton({
28762             tooltip: this.refreshText,
28763             cls: "x-btn-icon x-grid-loading",
28764             handler: this.onClick.createDelegate(this, ["refresh"])
28765         });
28766
28767         if(this.displayInfo){
28768             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28769         }
28770     },
28771
28772     // private
28773     updateInfo : function(){
28774         if(this.displayEl){
28775             var count = this.ds.getCount();
28776             var msg = count == 0 ?
28777                 this.emptyMsg :
28778                 String.format(
28779                     this.displayMsg,
28780                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28781                 );
28782             this.displayEl.update(msg);
28783         }
28784     },
28785
28786     // private
28787     onLoad : function(ds, r, o){
28788        this.cursor = o.params ? o.params.start : 0;
28789        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28790
28791        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28792        this.field.dom.value = ap;
28793        this.first.setDisabled(ap == 1);
28794        this.prev.setDisabled(ap == 1);
28795        this.next.setDisabled(ap == ps);
28796        this.last.setDisabled(ap == ps);
28797        this.loading.enable();
28798        this.updateInfo();
28799     },
28800
28801     // private
28802     getPageData : function(){
28803         var total = this.ds.getTotalCount();
28804         return {
28805             total : total,
28806             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28807             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28808         };
28809     },
28810
28811     // private
28812     onLoadError : function(){
28813         this.loading.enable();
28814     },
28815
28816     // private
28817     onPagingKeydown : function(e){
28818         var k = e.getKey();
28819         var d = this.getPageData();
28820         if(k == e.RETURN){
28821             var v = this.field.dom.value, pageNum;
28822             if(!v || isNaN(pageNum = parseInt(v, 10))){
28823                 this.field.dom.value = d.activePage;
28824                 return;
28825             }
28826             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28827             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28828             e.stopEvent();
28829         }
28830         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))
28831         {
28832           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28833           this.field.dom.value = pageNum;
28834           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28835           e.stopEvent();
28836         }
28837         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28838         {
28839           var v = this.field.dom.value, pageNum; 
28840           var increment = (e.shiftKey) ? 10 : 1;
28841           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28842             increment *= -1;
28843           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28844             this.field.dom.value = d.activePage;
28845             return;
28846           }
28847           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28848           {
28849             this.field.dom.value = parseInt(v, 10) + increment;
28850             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28851             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28852           }
28853           e.stopEvent();
28854         }
28855     },
28856
28857     // private
28858     beforeLoad : function(){
28859         if(this.loading){
28860             this.loading.disable();
28861         }
28862     },
28863
28864     // private
28865     onClick : function(which){
28866         var ds = this.ds;
28867         switch(which){
28868             case "first":
28869                 ds.load({params:{start: 0, limit: this.pageSize}});
28870             break;
28871             case "prev":
28872                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28873             break;
28874             case "next":
28875                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28876             break;
28877             case "last":
28878                 var total = ds.getTotalCount();
28879                 var extra = total % this.pageSize;
28880                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28881                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28882             break;
28883             case "refresh":
28884                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28885             break;
28886         }
28887     },
28888
28889     /**
28890      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28891      * @param {Roo.data.Store} store The data store to unbind
28892      */
28893     unbind : function(ds){
28894         ds.un("beforeload", this.beforeLoad, this);
28895         ds.un("load", this.onLoad, this);
28896         ds.un("loadexception", this.onLoadError, this);
28897         ds.un("remove", this.updateInfo, this);
28898         ds.un("add", this.updateInfo, this);
28899         this.ds = undefined;
28900     },
28901
28902     /**
28903      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28904      * @param {Roo.data.Store} store The data store to bind
28905      */
28906     bind : function(ds){
28907         ds.on("beforeload", this.beforeLoad, this);
28908         ds.on("load", this.onLoad, this);
28909         ds.on("loadexception", this.onLoadError, this);
28910         ds.on("remove", this.updateInfo, this);
28911         ds.on("add", this.updateInfo, this);
28912         this.ds = ds;
28913     }
28914 });/*
28915  * Based on:
28916  * Ext JS Library 1.1.1
28917  * Copyright(c) 2006-2007, Ext JS, LLC.
28918  *
28919  * Originally Released Under LGPL - original licence link has changed is not relivant.
28920  *
28921  * Fork - LGPL
28922  * <script type="text/javascript">
28923  */
28924
28925 /**
28926  * @class Roo.Resizable
28927  * @extends Roo.util.Observable
28928  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28929  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28930  * 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
28931  * the element will be wrapped for you automatically.</p>
28932  * <p>Here is the list of valid resize handles:</p>
28933  * <pre>
28934 Value   Description
28935 ------  -------------------
28936  'n'     north
28937  's'     south
28938  'e'     east
28939  'w'     west
28940  'nw'    northwest
28941  'sw'    southwest
28942  'se'    southeast
28943  'ne'    northeast
28944  'hd'    horizontal drag
28945  'all'   all
28946 </pre>
28947  * <p>Here's an example showing the creation of a typical Resizable:</p>
28948  * <pre><code>
28949 var resizer = new Roo.Resizable("element-id", {
28950     handles: 'all',
28951     minWidth: 200,
28952     minHeight: 100,
28953     maxWidth: 500,
28954     maxHeight: 400,
28955     pinned: true
28956 });
28957 resizer.on("resize", myHandler);
28958 </code></pre>
28959  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28960  * resizer.east.setDisplayed(false);</p>
28961  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28962  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28963  * resize operation's new size (defaults to [0, 0])
28964  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28965  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28966  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28967  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28968  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28969  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28970  * @cfg {Number} width The width of the element in pixels (defaults to null)
28971  * @cfg {Number} height The height of the element in pixels (defaults to null)
28972  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28973  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28974  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28975  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28976  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28977  * in favor of the handles config option (defaults to false)
28978  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28979  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28980  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28981  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28982  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28983  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28984  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28985  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28986  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28987  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28988  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28989  * @constructor
28990  * Create a new resizable component
28991  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28992  * @param {Object} config configuration options
28993   */
28994 Roo.Resizable = function(el, config)
28995 {
28996     this.el = Roo.get(el);
28997
28998     if(config && config.wrap){
28999         config.resizeChild = this.el;
29000         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29001         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29002         this.el.setStyle("overflow", "hidden");
29003         this.el.setPositioning(config.resizeChild.getPositioning());
29004         config.resizeChild.clearPositioning();
29005         if(!config.width || !config.height){
29006             var csize = config.resizeChild.getSize();
29007             this.el.setSize(csize.width, csize.height);
29008         }
29009         if(config.pinned && !config.adjustments){
29010             config.adjustments = "auto";
29011         }
29012     }
29013
29014     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29015     this.proxy.unselectable();
29016     this.proxy.enableDisplayMode('block');
29017
29018     Roo.apply(this, config);
29019
29020     if(this.pinned){
29021         this.disableTrackOver = true;
29022         this.el.addClass("x-resizable-pinned");
29023     }
29024     // if the element isn't positioned, make it relative
29025     var position = this.el.getStyle("position");
29026     if(position != "absolute" && position != "fixed"){
29027         this.el.setStyle("position", "relative");
29028     }
29029     if(!this.handles){ // no handles passed, must be legacy style
29030         this.handles = 's,e,se';
29031         if(this.multiDirectional){
29032             this.handles += ',n,w';
29033         }
29034     }
29035     if(this.handles == "all"){
29036         this.handles = "n s e w ne nw se sw";
29037     }
29038     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29039     var ps = Roo.Resizable.positions;
29040     for(var i = 0, len = hs.length; i < len; i++){
29041         if(hs[i] && ps[hs[i]]){
29042             var pos = ps[hs[i]];
29043             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29044         }
29045     }
29046     // legacy
29047     this.corner = this.southeast;
29048     
29049     // updateBox = the box can move..
29050     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29051         this.updateBox = true;
29052     }
29053
29054     this.activeHandle = null;
29055
29056     if(this.resizeChild){
29057         if(typeof this.resizeChild == "boolean"){
29058             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29059         }else{
29060             this.resizeChild = Roo.get(this.resizeChild, true);
29061         }
29062     }
29063     
29064     if(this.adjustments == "auto"){
29065         var rc = this.resizeChild;
29066         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29067         if(rc && (hw || hn)){
29068             rc.position("relative");
29069             rc.setLeft(hw ? hw.el.getWidth() : 0);
29070             rc.setTop(hn ? hn.el.getHeight() : 0);
29071         }
29072         this.adjustments = [
29073             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29074             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29075         ];
29076     }
29077
29078     if(this.draggable){
29079         this.dd = this.dynamic ?
29080             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29081         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29082     }
29083
29084     // public events
29085     this.addEvents({
29086         /**
29087          * @event beforeresize
29088          * Fired before resize is allowed. Set enabled to false to cancel resize.
29089          * @param {Roo.Resizable} this
29090          * @param {Roo.EventObject} e The mousedown event
29091          */
29092         "beforeresize" : true,
29093         /**
29094          * @event resizing
29095          * Fired a resizing.
29096          * @param {Roo.Resizable} this
29097          * @param {Number} x The new x position
29098          * @param {Number} y The new y position
29099          * @param {Number} w The new w width
29100          * @param {Number} h The new h hight
29101          * @param {Roo.EventObject} e The mouseup event
29102          */
29103         "resizing" : true,
29104         /**
29105          * @event resize
29106          * Fired after a resize.
29107          * @param {Roo.Resizable} this
29108          * @param {Number} width The new width
29109          * @param {Number} height The new height
29110          * @param {Roo.EventObject} e The mouseup event
29111          */
29112         "resize" : true
29113     });
29114
29115     if(this.width !== null && this.height !== null){
29116         this.resizeTo(this.width, this.height);
29117     }else{
29118         this.updateChildSize();
29119     }
29120     if(Roo.isIE){
29121         this.el.dom.style.zoom = 1;
29122     }
29123     Roo.Resizable.superclass.constructor.call(this);
29124 };
29125
29126 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29127         resizeChild : false,
29128         adjustments : [0, 0],
29129         minWidth : 5,
29130         minHeight : 5,
29131         maxWidth : 10000,
29132         maxHeight : 10000,
29133         enabled : true,
29134         animate : false,
29135         duration : .35,
29136         dynamic : false,
29137         handles : false,
29138         multiDirectional : false,
29139         disableTrackOver : false,
29140         easing : 'easeOutStrong',
29141         widthIncrement : 0,
29142         heightIncrement : 0,
29143         pinned : false,
29144         width : null,
29145         height : null,
29146         preserveRatio : false,
29147         transparent: false,
29148         minX: 0,
29149         minY: 0,
29150         draggable: false,
29151
29152         /**
29153          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29154          */
29155         constrainTo: undefined,
29156         /**
29157          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29158          */
29159         resizeRegion: undefined,
29160
29161
29162     /**
29163      * Perform a manual resize
29164      * @param {Number} width
29165      * @param {Number} height
29166      */
29167     resizeTo : function(width, height){
29168         this.el.setSize(width, height);
29169         this.updateChildSize();
29170         this.fireEvent("resize", this, width, height, null);
29171     },
29172
29173     // private
29174     startSizing : function(e, handle){
29175         this.fireEvent("beforeresize", this, e);
29176         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29177
29178             if(!this.overlay){
29179                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29180                 this.overlay.unselectable();
29181                 this.overlay.enableDisplayMode("block");
29182                 this.overlay.on("mousemove", this.onMouseMove, this);
29183                 this.overlay.on("mouseup", this.onMouseUp, this);
29184             }
29185             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29186
29187             this.resizing = true;
29188             this.startBox = this.el.getBox();
29189             this.startPoint = e.getXY();
29190             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29191                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29192
29193             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29194             this.overlay.show();
29195
29196             if(this.constrainTo) {
29197                 var ct = Roo.get(this.constrainTo);
29198                 this.resizeRegion = ct.getRegion().adjust(
29199                     ct.getFrameWidth('t'),
29200                     ct.getFrameWidth('l'),
29201                     -ct.getFrameWidth('b'),
29202                     -ct.getFrameWidth('r')
29203                 );
29204             }
29205
29206             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29207             this.proxy.show();
29208             this.proxy.setBox(this.startBox);
29209             if(!this.dynamic){
29210                 this.proxy.setStyle('visibility', 'visible');
29211             }
29212         }
29213     },
29214
29215     // private
29216     onMouseDown : function(handle, e){
29217         if(this.enabled){
29218             e.stopEvent();
29219             this.activeHandle = handle;
29220             this.startSizing(e, handle);
29221         }
29222     },
29223
29224     // private
29225     onMouseUp : function(e){
29226         var size = this.resizeElement();
29227         this.resizing = false;
29228         this.handleOut();
29229         this.overlay.hide();
29230         this.proxy.hide();
29231         this.fireEvent("resize", this, size.width, size.height, e);
29232     },
29233
29234     // private
29235     updateChildSize : function(){
29236         
29237         if(this.resizeChild){
29238             var el = this.el;
29239             var child = this.resizeChild;
29240             var adj = this.adjustments;
29241             if(el.dom.offsetWidth){
29242                 var b = el.getSize(true);
29243                 child.setSize(b.width+adj[0], b.height+adj[1]);
29244             }
29245             // Second call here for IE
29246             // The first call enables instant resizing and
29247             // the second call corrects scroll bars if they
29248             // exist
29249             if(Roo.isIE){
29250                 setTimeout(function(){
29251                     if(el.dom.offsetWidth){
29252                         var b = el.getSize(true);
29253                         child.setSize(b.width+adj[0], b.height+adj[1]);
29254                     }
29255                 }, 10);
29256             }
29257         }
29258     },
29259
29260     // private
29261     snap : function(value, inc, min){
29262         if(!inc || !value) return value;
29263         var newValue = value;
29264         var m = value % inc;
29265         if(m > 0){
29266             if(m > (inc/2)){
29267                 newValue = value + (inc-m);
29268             }else{
29269                 newValue = value - m;
29270             }
29271         }
29272         return Math.max(min, newValue);
29273     },
29274
29275     // private
29276     resizeElement : function(){
29277         var box = this.proxy.getBox();
29278         if(this.updateBox){
29279             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29280         }else{
29281             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29282         }
29283         this.updateChildSize();
29284         if(!this.dynamic){
29285             this.proxy.hide();
29286         }
29287         return box;
29288     },
29289
29290     // private
29291     constrain : function(v, diff, m, mx){
29292         if(v - diff < m){
29293             diff = v - m;
29294         }else if(v - diff > mx){
29295             diff = mx - v;
29296         }
29297         return diff;
29298     },
29299
29300     // private
29301     onMouseMove : function(e){
29302         
29303         if(this.enabled){
29304             try{// try catch so if something goes wrong the user doesn't get hung
29305
29306             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29307                 return;
29308             }
29309
29310             //var curXY = this.startPoint;
29311             var curSize = this.curSize || this.startBox;
29312             var x = this.startBox.x, y = this.startBox.y;
29313             var ox = x, oy = y;
29314             var w = curSize.width, h = curSize.height;
29315             var ow = w, oh = h;
29316             var mw = this.minWidth, mh = this.minHeight;
29317             var mxw = this.maxWidth, mxh = this.maxHeight;
29318             var wi = this.widthIncrement;
29319             var hi = this.heightIncrement;
29320
29321             var eventXY = e.getXY();
29322             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29323             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29324
29325             var pos = this.activeHandle.position;
29326
29327             switch(pos){
29328                 case "east":
29329                     w += diffX;
29330                     w = Math.min(Math.max(mw, w), mxw);
29331                     break;
29332              
29333                 case "south":
29334                     h += diffY;
29335                     h = Math.min(Math.max(mh, h), mxh);
29336                     break;
29337                 case "southeast":
29338                     w += diffX;
29339                     h += diffY;
29340                     w = Math.min(Math.max(mw, w), mxw);
29341                     h = Math.min(Math.max(mh, h), mxh);
29342                     break;
29343                 case "north":
29344                     diffY = this.constrain(h, diffY, mh, mxh);
29345                     y += diffY;
29346                     h -= diffY;
29347                     break;
29348                 case "hdrag":
29349                     
29350                     if (wi) {
29351                         var adiffX = Math.abs(diffX);
29352                         var sub = (adiffX % wi); // how much 
29353                         if (sub > (wi/2)) { // far enough to snap
29354                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29355                         } else {
29356                             // remove difference.. 
29357                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29358                         }
29359                     }
29360                     x += diffX;
29361                     x = Math.max(this.minX, x);
29362                     break;
29363                 case "west":
29364                     diffX = this.constrain(w, diffX, mw, mxw);
29365                     x += diffX;
29366                     w -= diffX;
29367                     break;
29368                 case "northeast":
29369                     w += diffX;
29370                     w = Math.min(Math.max(mw, w), mxw);
29371                     diffY = this.constrain(h, diffY, mh, mxh);
29372                     y += diffY;
29373                     h -= diffY;
29374                     break;
29375                 case "northwest":
29376                     diffX = this.constrain(w, diffX, mw, mxw);
29377                     diffY = this.constrain(h, diffY, mh, mxh);
29378                     y += diffY;
29379                     h -= diffY;
29380                     x += diffX;
29381                     w -= diffX;
29382                     break;
29383                case "southwest":
29384                     diffX = this.constrain(w, diffX, mw, mxw);
29385                     h += diffY;
29386                     h = Math.min(Math.max(mh, h), mxh);
29387                     x += diffX;
29388                     w -= diffX;
29389                     break;
29390             }
29391
29392             var sw = this.snap(w, wi, mw);
29393             var sh = this.snap(h, hi, mh);
29394             if(sw != w || sh != h){
29395                 switch(pos){
29396                     case "northeast":
29397                         y -= sh - h;
29398                     break;
29399                     case "north":
29400                         y -= sh - h;
29401                         break;
29402                     case "southwest":
29403                         x -= sw - w;
29404                     break;
29405                     case "west":
29406                         x -= sw - w;
29407                         break;
29408                     case "northwest":
29409                         x -= sw - w;
29410                         y -= sh - h;
29411                     break;
29412                 }
29413                 w = sw;
29414                 h = sh;
29415             }
29416
29417             if(this.preserveRatio){
29418                 switch(pos){
29419                     case "southeast":
29420                     case "east":
29421                         h = oh * (w/ow);
29422                         h = Math.min(Math.max(mh, h), mxh);
29423                         w = ow * (h/oh);
29424                        break;
29425                     case "south":
29426                         w = ow * (h/oh);
29427                         w = Math.min(Math.max(mw, w), mxw);
29428                         h = oh * (w/ow);
29429                         break;
29430                     case "northeast":
29431                         w = ow * (h/oh);
29432                         w = Math.min(Math.max(mw, w), mxw);
29433                         h = oh * (w/ow);
29434                     break;
29435                     case "north":
29436                         var tw = w;
29437                         w = ow * (h/oh);
29438                         w = Math.min(Math.max(mw, w), mxw);
29439                         h = oh * (w/ow);
29440                         x += (tw - w) / 2;
29441                         break;
29442                     case "southwest":
29443                         h = oh * (w/ow);
29444                         h = Math.min(Math.max(mh, h), mxh);
29445                         var tw = w;
29446                         w = ow * (h/oh);
29447                         x += tw - w;
29448                         break;
29449                     case "west":
29450                         var th = h;
29451                         h = oh * (w/ow);
29452                         h = Math.min(Math.max(mh, h), mxh);
29453                         y += (th - h) / 2;
29454                         var tw = w;
29455                         w = ow * (h/oh);
29456                         x += tw - w;
29457                        break;
29458                     case "northwest":
29459                         var tw = w;
29460                         var th = h;
29461                         h = oh * (w/ow);
29462                         h = Math.min(Math.max(mh, h), mxh);
29463                         w = ow * (h/oh);
29464                         y += th - h;
29465                         x += tw - w;
29466                        break;
29467
29468                 }
29469             }
29470             if (pos == 'hdrag') {
29471                 w = ow;
29472             }
29473             this.proxy.setBounds(x, y, w, h);
29474             if(this.dynamic){
29475                 this.resizeElement();
29476             }
29477             }catch(e){}
29478         }
29479         this.fireEvent("resizing", this, x, y, w, h, e);
29480     },
29481
29482     // private
29483     handleOver : function(){
29484         if(this.enabled){
29485             this.el.addClass("x-resizable-over");
29486         }
29487     },
29488
29489     // private
29490     handleOut : function(){
29491         if(!this.resizing){
29492             this.el.removeClass("x-resizable-over");
29493         }
29494     },
29495
29496     /**
29497      * Returns the element this component is bound to.
29498      * @return {Roo.Element}
29499      */
29500     getEl : function(){
29501         return this.el;
29502     },
29503
29504     /**
29505      * Returns the resizeChild element (or null).
29506      * @return {Roo.Element}
29507      */
29508     getResizeChild : function(){
29509         return this.resizeChild;
29510     },
29511     groupHandler : function()
29512     {
29513         
29514     },
29515     /**
29516      * Destroys this resizable. If the element was wrapped and
29517      * removeEl is not true then the element remains.
29518      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29519      */
29520     destroy : function(removeEl){
29521         this.proxy.remove();
29522         if(this.overlay){
29523             this.overlay.removeAllListeners();
29524             this.overlay.remove();
29525         }
29526         var ps = Roo.Resizable.positions;
29527         for(var k in ps){
29528             if(typeof ps[k] != "function" && this[ps[k]]){
29529                 var h = this[ps[k]];
29530                 h.el.removeAllListeners();
29531                 h.el.remove();
29532             }
29533         }
29534         if(removeEl){
29535             this.el.update("");
29536             this.el.remove();
29537         }
29538     }
29539 });
29540
29541 // private
29542 // hash to map config positions to true positions
29543 Roo.Resizable.positions = {
29544     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29545     hd: "hdrag"
29546 };
29547
29548 // private
29549 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29550     if(!this.tpl){
29551         // only initialize the template if resizable is used
29552         var tpl = Roo.DomHelper.createTemplate(
29553             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29554         );
29555         tpl.compile();
29556         Roo.Resizable.Handle.prototype.tpl = tpl;
29557     }
29558     this.position = pos;
29559     this.rz = rz;
29560     // show north drag fro topdra
29561     var handlepos = pos == 'hdrag' ? 'north' : pos;
29562     
29563     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29564     if (pos == 'hdrag') {
29565         this.el.setStyle('cursor', 'pointer');
29566     }
29567     this.el.unselectable();
29568     if(transparent){
29569         this.el.setOpacity(0);
29570     }
29571     this.el.on("mousedown", this.onMouseDown, this);
29572     if(!disableTrackOver){
29573         this.el.on("mouseover", this.onMouseOver, this);
29574         this.el.on("mouseout", this.onMouseOut, this);
29575     }
29576 };
29577
29578 // private
29579 Roo.Resizable.Handle.prototype = {
29580     afterResize : function(rz){
29581         Roo.log('after?');
29582         // do nothing
29583     },
29584     // private
29585     onMouseDown : function(e){
29586         this.rz.onMouseDown(this, e);
29587     },
29588     // private
29589     onMouseOver : function(e){
29590         this.rz.handleOver(this, e);
29591     },
29592     // private
29593     onMouseOut : function(e){
29594         this.rz.handleOut(this, e);
29595     }
29596 };/*
29597  * Based on:
29598  * Ext JS Library 1.1.1
29599  * Copyright(c) 2006-2007, Ext JS, LLC.
29600  *
29601  * Originally Released Under LGPL - original licence link has changed is not relivant.
29602  *
29603  * Fork - LGPL
29604  * <script type="text/javascript">
29605  */
29606
29607 /**
29608  * @class Roo.Editor
29609  * @extends Roo.Component
29610  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29611  * @constructor
29612  * Create a new Editor
29613  * @param {Roo.form.Field} field The Field object (or descendant)
29614  * @param {Object} config The config object
29615  */
29616 Roo.Editor = function(field, config){
29617     Roo.Editor.superclass.constructor.call(this, config);
29618     this.field = field;
29619     this.addEvents({
29620         /**
29621              * @event beforestartedit
29622              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29623              * false from the handler of this event.
29624              * @param {Editor} this
29625              * @param {Roo.Element} boundEl The underlying element bound to this editor
29626              * @param {Mixed} value The field value being set
29627              */
29628         "beforestartedit" : true,
29629         /**
29630              * @event startedit
29631              * Fires when this editor is displayed
29632              * @param {Roo.Element} boundEl The underlying element bound to this editor
29633              * @param {Mixed} value The starting field value
29634              */
29635         "startedit" : true,
29636         /**
29637              * @event beforecomplete
29638              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29639              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29640              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29641              * event will not fire since no edit actually occurred.
29642              * @param {Editor} this
29643              * @param {Mixed} value The current field value
29644              * @param {Mixed} startValue The original field value
29645              */
29646         "beforecomplete" : true,
29647         /**
29648              * @event complete
29649              * Fires after editing is complete and any changed value has been written to the underlying field.
29650              * @param {Editor} this
29651              * @param {Mixed} value The current field value
29652              * @param {Mixed} startValue The original field value
29653              */
29654         "complete" : true,
29655         /**
29656          * @event specialkey
29657          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29658          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29659          * @param {Roo.form.Field} this
29660          * @param {Roo.EventObject} e The event object
29661          */
29662         "specialkey" : true
29663     });
29664 };
29665
29666 Roo.extend(Roo.Editor, Roo.Component, {
29667     /**
29668      * @cfg {Boolean/String} autosize
29669      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29670      * or "height" to adopt the height only (defaults to false)
29671      */
29672     /**
29673      * @cfg {Boolean} revertInvalid
29674      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29675      * validation fails (defaults to true)
29676      */
29677     /**
29678      * @cfg {Boolean} ignoreNoChange
29679      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29680      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29681      * will never be ignored.
29682      */
29683     /**
29684      * @cfg {Boolean} hideEl
29685      * False to keep the bound element visible while the editor is displayed (defaults to true)
29686      */
29687     /**
29688      * @cfg {Mixed} value
29689      * The data value of the underlying field (defaults to "")
29690      */
29691     value : "",
29692     /**
29693      * @cfg {String} alignment
29694      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29695      */
29696     alignment: "c-c?",
29697     /**
29698      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29699      * for bottom-right shadow (defaults to "frame")
29700      */
29701     shadow : "frame",
29702     /**
29703      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29704      */
29705     constrain : false,
29706     /**
29707      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29708      */
29709     completeOnEnter : false,
29710     /**
29711      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29712      */
29713     cancelOnEsc : false,
29714     /**
29715      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29716      */
29717     updateEl : false,
29718
29719     // private
29720     onRender : function(ct, position){
29721         this.el = new Roo.Layer({
29722             shadow: this.shadow,
29723             cls: "x-editor",
29724             parentEl : ct,
29725             shim : this.shim,
29726             shadowOffset:4,
29727             id: this.id,
29728             constrain: this.constrain
29729         });
29730         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29731         if(this.field.msgTarget != 'title'){
29732             this.field.msgTarget = 'qtip';
29733         }
29734         this.field.render(this.el);
29735         if(Roo.isGecko){
29736             this.field.el.dom.setAttribute('autocomplete', 'off');
29737         }
29738         this.field.on("specialkey", this.onSpecialKey, this);
29739         if(this.swallowKeys){
29740             this.field.el.swallowEvent(['keydown','keypress']);
29741         }
29742         this.field.show();
29743         this.field.on("blur", this.onBlur, this);
29744         if(this.field.grow){
29745             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29746         }
29747     },
29748
29749     onSpecialKey : function(field, e)
29750     {
29751         //Roo.log('editor onSpecialKey');
29752         if(this.completeOnEnter && e.getKey() == e.ENTER){
29753             e.stopEvent();
29754             this.completeEdit();
29755             return;
29756         }
29757         // do not fire special key otherwise it might hide close the editor...
29758         if(e.getKey() == e.ENTER){    
29759             return;
29760         }
29761         if(this.cancelOnEsc && e.getKey() == e.ESC){
29762             this.cancelEdit();
29763             return;
29764         } 
29765         this.fireEvent('specialkey', field, e);
29766     
29767     },
29768
29769     /**
29770      * Starts the editing process and shows the editor.
29771      * @param {String/HTMLElement/Element} el The element to edit
29772      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29773       * to the innerHTML of el.
29774      */
29775     startEdit : function(el, value){
29776         if(this.editing){
29777             this.completeEdit();
29778         }
29779         this.boundEl = Roo.get(el);
29780         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29781         if(!this.rendered){
29782             this.render(this.parentEl || document.body);
29783         }
29784         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29785             return;
29786         }
29787         this.startValue = v;
29788         this.field.setValue(v);
29789         if(this.autoSize){
29790             var sz = this.boundEl.getSize();
29791             switch(this.autoSize){
29792                 case "width":
29793                 this.setSize(sz.width,  "");
29794                 break;
29795                 case "height":
29796                 this.setSize("",  sz.height);
29797                 break;
29798                 default:
29799                 this.setSize(sz.width,  sz.height);
29800             }
29801         }
29802         this.el.alignTo(this.boundEl, this.alignment);
29803         this.editing = true;
29804         if(Roo.QuickTips){
29805             Roo.QuickTips.disable();
29806         }
29807         this.show();
29808     },
29809
29810     /**
29811      * Sets the height and width of this editor.
29812      * @param {Number} width The new width
29813      * @param {Number} height The new height
29814      */
29815     setSize : function(w, h){
29816         this.field.setSize(w, h);
29817         if(this.el){
29818             this.el.sync();
29819         }
29820     },
29821
29822     /**
29823      * Realigns the editor to the bound field based on the current alignment config value.
29824      */
29825     realign : function(){
29826         this.el.alignTo(this.boundEl, this.alignment);
29827     },
29828
29829     /**
29830      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29831      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29832      */
29833     completeEdit : function(remainVisible){
29834         if(!this.editing){
29835             return;
29836         }
29837         var v = this.getValue();
29838         if(this.revertInvalid !== false && !this.field.isValid()){
29839             v = this.startValue;
29840             this.cancelEdit(true);
29841         }
29842         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29843             this.editing = false;
29844             this.hide();
29845             return;
29846         }
29847         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29848             this.editing = false;
29849             if(this.updateEl && this.boundEl){
29850                 this.boundEl.update(v);
29851             }
29852             if(remainVisible !== true){
29853                 this.hide();
29854             }
29855             this.fireEvent("complete", this, v, this.startValue);
29856         }
29857     },
29858
29859     // private
29860     onShow : function(){
29861         this.el.show();
29862         if(this.hideEl !== false){
29863             this.boundEl.hide();
29864         }
29865         this.field.show();
29866         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29867             this.fixIEFocus = true;
29868             this.deferredFocus.defer(50, this);
29869         }else{
29870             this.field.focus();
29871         }
29872         this.fireEvent("startedit", this.boundEl, this.startValue);
29873     },
29874
29875     deferredFocus : function(){
29876         if(this.editing){
29877             this.field.focus();
29878         }
29879     },
29880
29881     /**
29882      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29883      * reverted to the original starting value.
29884      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29885      * cancel (defaults to false)
29886      */
29887     cancelEdit : function(remainVisible){
29888         if(this.editing){
29889             this.setValue(this.startValue);
29890             if(remainVisible !== true){
29891                 this.hide();
29892             }
29893         }
29894     },
29895
29896     // private
29897     onBlur : function(){
29898         if(this.allowBlur !== true && this.editing){
29899             this.completeEdit();
29900         }
29901     },
29902
29903     // private
29904     onHide : function(){
29905         if(this.editing){
29906             this.completeEdit();
29907             return;
29908         }
29909         this.field.blur();
29910         if(this.field.collapse){
29911             this.field.collapse();
29912         }
29913         this.el.hide();
29914         if(this.hideEl !== false){
29915             this.boundEl.show();
29916         }
29917         if(Roo.QuickTips){
29918             Roo.QuickTips.enable();
29919         }
29920     },
29921
29922     /**
29923      * Sets the data value of the editor
29924      * @param {Mixed} value Any valid value supported by the underlying field
29925      */
29926     setValue : function(v){
29927         this.field.setValue(v);
29928     },
29929
29930     /**
29931      * Gets the data value of the editor
29932      * @return {Mixed} The data value
29933      */
29934     getValue : function(){
29935         return this.field.getValue();
29936     }
29937 });/*
29938  * Based on:
29939  * Ext JS Library 1.1.1
29940  * Copyright(c) 2006-2007, Ext JS, LLC.
29941  *
29942  * Originally Released Under LGPL - original licence link has changed is not relivant.
29943  *
29944  * Fork - LGPL
29945  * <script type="text/javascript">
29946  */
29947  
29948 /**
29949  * @class Roo.BasicDialog
29950  * @extends Roo.util.Observable
29951  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29952  * <pre><code>
29953 var dlg = new Roo.BasicDialog("my-dlg", {
29954     height: 200,
29955     width: 300,
29956     minHeight: 100,
29957     minWidth: 150,
29958     modal: true,
29959     proxyDrag: true,
29960     shadow: true
29961 });
29962 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29963 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29964 dlg.addButton('Cancel', dlg.hide, dlg);
29965 dlg.show();
29966 </code></pre>
29967   <b>A Dialog should always be a direct child of the body element.</b>
29968  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29969  * @cfg {String} title Default text to display in the title bar (defaults to null)
29970  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29971  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29972  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29973  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29974  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29975  * (defaults to null with no animation)
29976  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29977  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29978  * property for valid values (defaults to 'all')
29979  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29980  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29981  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29982  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29983  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29984  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29985  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29986  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29987  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29988  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29989  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29990  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29991  * draggable = true (defaults to false)
29992  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29993  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29994  * shadow (defaults to false)
29995  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29996  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29997  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29998  * @cfg {Array} buttons Array of buttons
29999  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30000  * @constructor
30001  * Create a new BasicDialog.
30002  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30003  * @param {Object} config Configuration options
30004  */
30005 Roo.BasicDialog = function(el, config){
30006     this.el = Roo.get(el);
30007     var dh = Roo.DomHelper;
30008     if(!this.el && config && config.autoCreate){
30009         if(typeof config.autoCreate == "object"){
30010             if(!config.autoCreate.id){
30011                 config.autoCreate.id = el;
30012             }
30013             this.el = dh.append(document.body,
30014                         config.autoCreate, true);
30015         }else{
30016             this.el = dh.append(document.body,
30017                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30018         }
30019     }
30020     el = this.el;
30021     el.setDisplayed(true);
30022     el.hide = this.hideAction;
30023     this.id = el.id;
30024     el.addClass("x-dlg");
30025
30026     Roo.apply(this, config);
30027
30028     this.proxy = el.createProxy("x-dlg-proxy");
30029     this.proxy.hide = this.hideAction;
30030     this.proxy.setOpacity(.5);
30031     this.proxy.hide();
30032
30033     if(config.width){
30034         el.setWidth(config.width);
30035     }
30036     if(config.height){
30037         el.setHeight(config.height);
30038     }
30039     this.size = el.getSize();
30040     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30041         this.xy = [config.x,config.y];
30042     }else{
30043         this.xy = el.getCenterXY(true);
30044     }
30045     /** The header element @type Roo.Element */
30046     this.header = el.child("> .x-dlg-hd");
30047     /** The body element @type Roo.Element */
30048     this.body = el.child("> .x-dlg-bd");
30049     /** The footer element @type Roo.Element */
30050     this.footer = el.child("> .x-dlg-ft");
30051
30052     if(!this.header){
30053         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30054     }
30055     if(!this.body){
30056         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30057     }
30058
30059     this.header.unselectable();
30060     if(this.title){
30061         this.header.update(this.title);
30062     }
30063     // this element allows the dialog to be focused for keyboard event
30064     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30065     this.focusEl.swallowEvent("click", true);
30066
30067     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30068
30069     // wrap the body and footer for special rendering
30070     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30071     if(this.footer){
30072         this.bwrap.dom.appendChild(this.footer.dom);
30073     }
30074
30075     this.bg = this.el.createChild({
30076         tag: "div", cls:"x-dlg-bg",
30077         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30078     });
30079     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30080
30081
30082     if(this.autoScroll !== false && !this.autoTabs){
30083         this.body.setStyle("overflow", "auto");
30084     }
30085
30086     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30087
30088     if(this.closable !== false){
30089         this.el.addClass("x-dlg-closable");
30090         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30091         this.close.on("click", this.closeClick, this);
30092         this.close.addClassOnOver("x-dlg-close-over");
30093     }
30094     if(this.collapsible !== false){
30095         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30096         this.collapseBtn.on("click", this.collapseClick, this);
30097         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30098         this.header.on("dblclick", this.collapseClick, this);
30099     }
30100     if(this.resizable !== false){
30101         this.el.addClass("x-dlg-resizable");
30102         this.resizer = new Roo.Resizable(el, {
30103             minWidth: this.minWidth || 80,
30104             minHeight:this.minHeight || 80,
30105             handles: this.resizeHandles || "all",
30106             pinned: true
30107         });
30108         this.resizer.on("beforeresize", this.beforeResize, this);
30109         this.resizer.on("resize", this.onResize, this);
30110     }
30111     if(this.draggable !== false){
30112         el.addClass("x-dlg-draggable");
30113         if (!this.proxyDrag) {
30114             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30115         }
30116         else {
30117             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30118         }
30119         dd.setHandleElId(this.header.id);
30120         dd.endDrag = this.endMove.createDelegate(this);
30121         dd.startDrag = this.startMove.createDelegate(this);
30122         dd.onDrag = this.onDrag.createDelegate(this);
30123         dd.scroll = false;
30124         this.dd = dd;
30125     }
30126     if(this.modal){
30127         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30128         this.mask.enableDisplayMode("block");
30129         this.mask.hide();
30130         this.el.addClass("x-dlg-modal");
30131     }
30132     if(this.shadow){
30133         this.shadow = new Roo.Shadow({
30134             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30135             offset : this.shadowOffset
30136         });
30137     }else{
30138         this.shadowOffset = 0;
30139     }
30140     if(Roo.useShims && this.shim !== false){
30141         this.shim = this.el.createShim();
30142         this.shim.hide = this.hideAction;
30143         this.shim.hide();
30144     }else{
30145         this.shim = false;
30146     }
30147     if(this.autoTabs){
30148         this.initTabs();
30149     }
30150     if (this.buttons) { 
30151         var bts= this.buttons;
30152         this.buttons = [];
30153         Roo.each(bts, function(b) {
30154             this.addButton(b);
30155         }, this);
30156     }
30157     
30158     
30159     this.addEvents({
30160         /**
30161          * @event keydown
30162          * Fires when a key is pressed
30163          * @param {Roo.BasicDialog} this
30164          * @param {Roo.EventObject} e
30165          */
30166         "keydown" : true,
30167         /**
30168          * @event move
30169          * Fires when this dialog is moved by the user.
30170          * @param {Roo.BasicDialog} this
30171          * @param {Number} x The new page X
30172          * @param {Number} y The new page Y
30173          */
30174         "move" : true,
30175         /**
30176          * @event resize
30177          * Fires when this dialog is resized by the user.
30178          * @param {Roo.BasicDialog} this
30179          * @param {Number} width The new width
30180          * @param {Number} height The new height
30181          */
30182         "resize" : true,
30183         /**
30184          * @event beforehide
30185          * Fires before this dialog is hidden.
30186          * @param {Roo.BasicDialog} this
30187          */
30188         "beforehide" : true,
30189         /**
30190          * @event hide
30191          * Fires when this dialog is hidden.
30192          * @param {Roo.BasicDialog} this
30193          */
30194         "hide" : true,
30195         /**
30196          * @event beforeshow
30197          * Fires before this dialog is shown.
30198          * @param {Roo.BasicDialog} this
30199          */
30200         "beforeshow" : true,
30201         /**
30202          * @event show
30203          * Fires when this dialog is shown.
30204          * @param {Roo.BasicDialog} this
30205          */
30206         "show" : true
30207     });
30208     el.on("keydown", this.onKeyDown, this);
30209     el.on("mousedown", this.toFront, this);
30210     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30211     this.el.hide();
30212     Roo.DialogManager.register(this);
30213     Roo.BasicDialog.superclass.constructor.call(this);
30214 };
30215
30216 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30217     shadowOffset: Roo.isIE ? 6 : 5,
30218     minHeight: 80,
30219     minWidth: 200,
30220     minButtonWidth: 75,
30221     defaultButton: null,
30222     buttonAlign: "right",
30223     tabTag: 'div',
30224     firstShow: true,
30225
30226     /**
30227      * Sets the dialog title text
30228      * @param {String} text The title text to display
30229      * @return {Roo.BasicDialog} this
30230      */
30231     setTitle : function(text){
30232         this.header.update(text);
30233         return this;
30234     },
30235
30236     // private
30237     closeClick : function(){
30238         this.hide();
30239     },
30240
30241     // private
30242     collapseClick : function(){
30243         this[this.collapsed ? "expand" : "collapse"]();
30244     },
30245
30246     /**
30247      * Collapses the dialog to its minimized state (only the title bar is visible).
30248      * Equivalent to the user clicking the collapse dialog button.
30249      */
30250     collapse : function(){
30251         if(!this.collapsed){
30252             this.collapsed = true;
30253             this.el.addClass("x-dlg-collapsed");
30254             this.restoreHeight = this.el.getHeight();
30255             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30256         }
30257     },
30258
30259     /**
30260      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30261      * clicking the expand dialog button.
30262      */
30263     expand : function(){
30264         if(this.collapsed){
30265             this.collapsed = false;
30266             this.el.removeClass("x-dlg-collapsed");
30267             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30268         }
30269     },
30270
30271     /**
30272      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30273      * @return {Roo.TabPanel} The tabs component
30274      */
30275     initTabs : function(){
30276         var tabs = this.getTabs();
30277         while(tabs.getTab(0)){
30278             tabs.removeTab(0);
30279         }
30280         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30281             var dom = el.dom;
30282             tabs.addTab(Roo.id(dom), dom.title);
30283             dom.title = "";
30284         });
30285         tabs.activate(0);
30286         return tabs;
30287     },
30288
30289     // private
30290     beforeResize : function(){
30291         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30292     },
30293
30294     // private
30295     onResize : function(){
30296         this.refreshSize();
30297         this.syncBodyHeight();
30298         this.adjustAssets();
30299         this.focus();
30300         this.fireEvent("resize", this, this.size.width, this.size.height);
30301     },
30302
30303     // private
30304     onKeyDown : function(e){
30305         if(this.isVisible()){
30306             this.fireEvent("keydown", this, e);
30307         }
30308     },
30309
30310     /**
30311      * Resizes the dialog.
30312      * @param {Number} width
30313      * @param {Number} height
30314      * @return {Roo.BasicDialog} this
30315      */
30316     resizeTo : function(width, height){
30317         this.el.setSize(width, height);
30318         this.size = {width: width, height: height};
30319         this.syncBodyHeight();
30320         if(this.fixedcenter){
30321             this.center();
30322         }
30323         if(this.isVisible()){
30324             this.constrainXY();
30325             this.adjustAssets();
30326         }
30327         this.fireEvent("resize", this, width, height);
30328         return this;
30329     },
30330
30331
30332     /**
30333      * Resizes the dialog to fit the specified content size.
30334      * @param {Number} width
30335      * @param {Number} height
30336      * @return {Roo.BasicDialog} this
30337      */
30338     setContentSize : function(w, h){
30339         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30340         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30341         //if(!this.el.isBorderBox()){
30342             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30343             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30344         //}
30345         if(this.tabs){
30346             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30347             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30348         }
30349         this.resizeTo(w, h);
30350         return this;
30351     },
30352
30353     /**
30354      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30355      * executed in response to a particular key being pressed while the dialog is active.
30356      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30357      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30358      * @param {Function} fn The function to call
30359      * @param {Object} scope (optional) The scope of the function
30360      * @return {Roo.BasicDialog} this
30361      */
30362     addKeyListener : function(key, fn, scope){
30363         var keyCode, shift, ctrl, alt;
30364         if(typeof key == "object" && !(key instanceof Array)){
30365             keyCode = key["key"];
30366             shift = key["shift"];
30367             ctrl = key["ctrl"];
30368             alt = key["alt"];
30369         }else{
30370             keyCode = key;
30371         }
30372         var handler = function(dlg, e){
30373             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30374                 var k = e.getKey();
30375                 if(keyCode instanceof Array){
30376                     for(var i = 0, len = keyCode.length; i < len; i++){
30377                         if(keyCode[i] == k){
30378                           fn.call(scope || window, dlg, k, e);
30379                           return;
30380                         }
30381                     }
30382                 }else{
30383                     if(k == keyCode){
30384                         fn.call(scope || window, dlg, k, e);
30385                     }
30386                 }
30387             }
30388         };
30389         this.on("keydown", handler);
30390         return this;
30391     },
30392
30393     /**
30394      * Returns the TabPanel component (creates it if it doesn't exist).
30395      * Note: If you wish to simply check for the existence of tabs without creating them,
30396      * check for a null 'tabs' property.
30397      * @return {Roo.TabPanel} The tabs component
30398      */
30399     getTabs : function(){
30400         if(!this.tabs){
30401             this.el.addClass("x-dlg-auto-tabs");
30402             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30403             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30404         }
30405         return this.tabs;
30406     },
30407
30408     /**
30409      * Adds a button to the footer section of the dialog.
30410      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30411      * object or a valid Roo.DomHelper element config
30412      * @param {Function} handler The function called when the button is clicked
30413      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30414      * @return {Roo.Button} The new button
30415      */
30416     addButton : function(config, handler, scope){
30417         var dh = Roo.DomHelper;
30418         if(!this.footer){
30419             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30420         }
30421         if(!this.btnContainer){
30422             var tb = this.footer.createChild({
30423
30424                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30425                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30426             }, null, true);
30427             this.btnContainer = tb.firstChild.firstChild.firstChild;
30428         }
30429         var bconfig = {
30430             handler: handler,
30431             scope: scope,
30432             minWidth: this.minButtonWidth,
30433             hideParent:true
30434         };
30435         if(typeof config == "string"){
30436             bconfig.text = config;
30437         }else{
30438             if(config.tag){
30439                 bconfig.dhconfig = config;
30440             }else{
30441                 Roo.apply(bconfig, config);
30442             }
30443         }
30444         var fc = false;
30445         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30446             bconfig.position = Math.max(0, bconfig.position);
30447             fc = this.btnContainer.childNodes[bconfig.position];
30448         }
30449          
30450         var btn = new Roo.Button(
30451             fc ? 
30452                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30453                 : this.btnContainer.appendChild(document.createElement("td")),
30454             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30455             bconfig
30456         );
30457         this.syncBodyHeight();
30458         if(!this.buttons){
30459             /**
30460              * Array of all the buttons that have been added to this dialog via addButton
30461              * @type Array
30462              */
30463             this.buttons = [];
30464         }
30465         this.buttons.push(btn);
30466         return btn;
30467     },
30468
30469     /**
30470      * Sets the default button to be focused when the dialog is displayed.
30471      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30472      * @return {Roo.BasicDialog} this
30473      */
30474     setDefaultButton : function(btn){
30475         this.defaultButton = btn;
30476         return this;
30477     },
30478
30479     // private
30480     getHeaderFooterHeight : function(safe){
30481         var height = 0;
30482         if(this.header){
30483            height += this.header.getHeight();
30484         }
30485         if(this.footer){
30486            var fm = this.footer.getMargins();
30487             height += (this.footer.getHeight()+fm.top+fm.bottom);
30488         }
30489         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30490         height += this.centerBg.getPadding("tb");
30491         return height;
30492     },
30493
30494     // private
30495     syncBodyHeight : function()
30496     {
30497         var bd = this.body, // the text
30498             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30499             bw = this.bwrap;
30500         var height = this.size.height - this.getHeaderFooterHeight(false);
30501         bd.setHeight(height-bd.getMargins("tb"));
30502         var hh = this.header.getHeight();
30503         var h = this.size.height-hh;
30504         cb.setHeight(h);
30505         
30506         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30507         bw.setHeight(h-cb.getPadding("tb"));
30508         
30509         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30510         bd.setWidth(bw.getWidth(true));
30511         if(this.tabs){
30512             this.tabs.syncHeight();
30513             if(Roo.isIE){
30514                 this.tabs.el.repaint();
30515             }
30516         }
30517     },
30518
30519     /**
30520      * Restores the previous state of the dialog if Roo.state is configured.
30521      * @return {Roo.BasicDialog} this
30522      */
30523     restoreState : function(){
30524         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30525         if(box && box.width){
30526             this.xy = [box.x, box.y];
30527             this.resizeTo(box.width, box.height);
30528         }
30529         return this;
30530     },
30531
30532     // private
30533     beforeShow : function(){
30534         this.expand();
30535         if(this.fixedcenter){
30536             this.xy = this.el.getCenterXY(true);
30537         }
30538         if(this.modal){
30539             Roo.get(document.body).addClass("x-body-masked");
30540             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30541             this.mask.show();
30542         }
30543         this.constrainXY();
30544     },
30545
30546     // private
30547     animShow : function(){
30548         var b = Roo.get(this.animateTarget).getBox();
30549         this.proxy.setSize(b.width, b.height);
30550         this.proxy.setLocation(b.x, b.y);
30551         this.proxy.show();
30552         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30553                     true, .35, this.showEl.createDelegate(this));
30554     },
30555
30556     /**
30557      * Shows the dialog.
30558      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30559      * @return {Roo.BasicDialog} this
30560      */
30561     show : function(animateTarget){
30562         if (this.fireEvent("beforeshow", this) === false){
30563             return;
30564         }
30565         if(this.syncHeightBeforeShow){
30566             this.syncBodyHeight();
30567         }else if(this.firstShow){
30568             this.firstShow = false;
30569             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30570         }
30571         this.animateTarget = animateTarget || this.animateTarget;
30572         if(!this.el.isVisible()){
30573             this.beforeShow();
30574             if(this.animateTarget && Roo.get(this.animateTarget)){
30575                 this.animShow();
30576             }else{
30577                 this.showEl();
30578             }
30579         }
30580         return this;
30581     },
30582
30583     // private
30584     showEl : function(){
30585         this.proxy.hide();
30586         this.el.setXY(this.xy);
30587         this.el.show();
30588         this.adjustAssets(true);
30589         this.toFront();
30590         this.focus();
30591         // IE peekaboo bug - fix found by Dave Fenwick
30592         if(Roo.isIE){
30593             this.el.repaint();
30594         }
30595         this.fireEvent("show", this);
30596     },
30597
30598     /**
30599      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30600      * dialog itself will receive focus.
30601      */
30602     focus : function(){
30603         if(this.defaultButton){
30604             this.defaultButton.focus();
30605         }else{
30606             this.focusEl.focus();
30607         }
30608     },
30609
30610     // private
30611     constrainXY : function(){
30612         if(this.constraintoviewport !== false){
30613             if(!this.viewSize){
30614                 if(this.container){
30615                     var s = this.container.getSize();
30616                     this.viewSize = [s.width, s.height];
30617                 }else{
30618                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30619                 }
30620             }
30621             var s = Roo.get(this.container||document).getScroll();
30622
30623             var x = this.xy[0], y = this.xy[1];
30624             var w = this.size.width, h = this.size.height;
30625             var vw = this.viewSize[0], vh = this.viewSize[1];
30626             // only move it if it needs it
30627             var moved = false;
30628             // first validate right/bottom
30629             if(x + w > vw+s.left){
30630                 x = vw - w;
30631                 moved = true;
30632             }
30633             if(y + h > vh+s.top){
30634                 y = vh - h;
30635                 moved = true;
30636             }
30637             // then make sure top/left isn't negative
30638             if(x < s.left){
30639                 x = s.left;
30640                 moved = true;
30641             }
30642             if(y < s.top){
30643                 y = s.top;
30644                 moved = true;
30645             }
30646             if(moved){
30647                 // cache xy
30648                 this.xy = [x, y];
30649                 if(this.isVisible()){
30650                     this.el.setLocation(x, y);
30651                     this.adjustAssets();
30652                 }
30653             }
30654         }
30655     },
30656
30657     // private
30658     onDrag : function(){
30659         if(!this.proxyDrag){
30660             this.xy = this.el.getXY();
30661             this.adjustAssets();
30662         }
30663     },
30664
30665     // private
30666     adjustAssets : function(doShow){
30667         var x = this.xy[0], y = this.xy[1];
30668         var w = this.size.width, h = this.size.height;
30669         if(doShow === true){
30670             if(this.shadow){
30671                 this.shadow.show(this.el);
30672             }
30673             if(this.shim){
30674                 this.shim.show();
30675             }
30676         }
30677         if(this.shadow && this.shadow.isVisible()){
30678             this.shadow.show(this.el);
30679         }
30680         if(this.shim && this.shim.isVisible()){
30681             this.shim.setBounds(x, y, w, h);
30682         }
30683     },
30684
30685     // private
30686     adjustViewport : function(w, h){
30687         if(!w || !h){
30688             w = Roo.lib.Dom.getViewWidth();
30689             h = Roo.lib.Dom.getViewHeight();
30690         }
30691         // cache the size
30692         this.viewSize = [w, h];
30693         if(this.modal && this.mask.isVisible()){
30694             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30695             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30696         }
30697         if(this.isVisible()){
30698             this.constrainXY();
30699         }
30700     },
30701
30702     /**
30703      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30704      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30705      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30706      */
30707     destroy : function(removeEl){
30708         if(this.isVisible()){
30709             this.animateTarget = null;
30710             this.hide();
30711         }
30712         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30713         if(this.tabs){
30714             this.tabs.destroy(removeEl);
30715         }
30716         Roo.destroy(
30717              this.shim,
30718              this.proxy,
30719              this.resizer,
30720              this.close,
30721              this.mask
30722         );
30723         if(this.dd){
30724             this.dd.unreg();
30725         }
30726         if(this.buttons){
30727            for(var i = 0, len = this.buttons.length; i < len; i++){
30728                this.buttons[i].destroy();
30729            }
30730         }
30731         this.el.removeAllListeners();
30732         if(removeEl === true){
30733             this.el.update("");
30734             this.el.remove();
30735         }
30736         Roo.DialogManager.unregister(this);
30737     },
30738
30739     // private
30740     startMove : function(){
30741         if(this.proxyDrag){
30742             this.proxy.show();
30743         }
30744         if(this.constraintoviewport !== false){
30745             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30746         }
30747     },
30748
30749     // private
30750     endMove : function(){
30751         if(!this.proxyDrag){
30752             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30753         }else{
30754             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30755             this.proxy.hide();
30756         }
30757         this.refreshSize();
30758         this.adjustAssets();
30759         this.focus();
30760         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30761     },
30762
30763     /**
30764      * Brings this dialog to the front of any other visible dialogs
30765      * @return {Roo.BasicDialog} this
30766      */
30767     toFront : function(){
30768         Roo.DialogManager.bringToFront(this);
30769         return this;
30770     },
30771
30772     /**
30773      * Sends this dialog to the back (under) of any other visible dialogs
30774      * @return {Roo.BasicDialog} this
30775      */
30776     toBack : function(){
30777         Roo.DialogManager.sendToBack(this);
30778         return this;
30779     },
30780
30781     /**
30782      * Centers this dialog in the viewport
30783      * @return {Roo.BasicDialog} this
30784      */
30785     center : function(){
30786         var xy = this.el.getCenterXY(true);
30787         this.moveTo(xy[0], xy[1]);
30788         return this;
30789     },
30790
30791     /**
30792      * Moves the dialog's top-left corner to the specified point
30793      * @param {Number} x
30794      * @param {Number} y
30795      * @return {Roo.BasicDialog} this
30796      */
30797     moveTo : function(x, y){
30798         this.xy = [x,y];
30799         if(this.isVisible()){
30800             this.el.setXY(this.xy);
30801             this.adjustAssets();
30802         }
30803         return this;
30804     },
30805
30806     /**
30807      * Aligns the dialog to the specified element
30808      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30809      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30810      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30811      * @return {Roo.BasicDialog} this
30812      */
30813     alignTo : function(element, position, offsets){
30814         this.xy = this.el.getAlignToXY(element, position, offsets);
30815         if(this.isVisible()){
30816             this.el.setXY(this.xy);
30817             this.adjustAssets();
30818         }
30819         return this;
30820     },
30821
30822     /**
30823      * Anchors an element to another element and realigns it when the window is resized.
30824      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30825      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30826      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30827      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30828      * is a number, it is used as the buffer delay (defaults to 50ms).
30829      * @return {Roo.BasicDialog} this
30830      */
30831     anchorTo : function(el, alignment, offsets, monitorScroll){
30832         var action = function(){
30833             this.alignTo(el, alignment, offsets);
30834         };
30835         Roo.EventManager.onWindowResize(action, this);
30836         var tm = typeof monitorScroll;
30837         if(tm != 'undefined'){
30838             Roo.EventManager.on(window, 'scroll', action, this,
30839                 {buffer: tm == 'number' ? monitorScroll : 50});
30840         }
30841         action.call(this);
30842         return this;
30843     },
30844
30845     /**
30846      * Returns true if the dialog is visible
30847      * @return {Boolean}
30848      */
30849     isVisible : function(){
30850         return this.el.isVisible();
30851     },
30852
30853     // private
30854     animHide : function(callback){
30855         var b = Roo.get(this.animateTarget).getBox();
30856         this.proxy.show();
30857         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30858         this.el.hide();
30859         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30860                     this.hideEl.createDelegate(this, [callback]));
30861     },
30862
30863     /**
30864      * Hides the dialog.
30865      * @param {Function} callback (optional) Function to call when the dialog is hidden
30866      * @return {Roo.BasicDialog} this
30867      */
30868     hide : function(callback){
30869         if (this.fireEvent("beforehide", this) === false){
30870             return;
30871         }
30872         if(this.shadow){
30873             this.shadow.hide();
30874         }
30875         if(this.shim) {
30876           this.shim.hide();
30877         }
30878         // sometimes animateTarget seems to get set.. causing problems...
30879         // this just double checks..
30880         if(this.animateTarget && Roo.get(this.animateTarget)) {
30881            this.animHide(callback);
30882         }else{
30883             this.el.hide();
30884             this.hideEl(callback);
30885         }
30886         return this;
30887     },
30888
30889     // private
30890     hideEl : function(callback){
30891         this.proxy.hide();
30892         if(this.modal){
30893             this.mask.hide();
30894             Roo.get(document.body).removeClass("x-body-masked");
30895         }
30896         this.fireEvent("hide", this);
30897         if(typeof callback == "function"){
30898             callback();
30899         }
30900     },
30901
30902     // private
30903     hideAction : function(){
30904         this.setLeft("-10000px");
30905         this.setTop("-10000px");
30906         this.setStyle("visibility", "hidden");
30907     },
30908
30909     // private
30910     refreshSize : function(){
30911         this.size = this.el.getSize();
30912         this.xy = this.el.getXY();
30913         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30914     },
30915
30916     // private
30917     // z-index is managed by the DialogManager and may be overwritten at any time
30918     setZIndex : function(index){
30919         if(this.modal){
30920             this.mask.setStyle("z-index", index);
30921         }
30922         if(this.shim){
30923             this.shim.setStyle("z-index", ++index);
30924         }
30925         if(this.shadow){
30926             this.shadow.setZIndex(++index);
30927         }
30928         this.el.setStyle("z-index", ++index);
30929         if(this.proxy){
30930             this.proxy.setStyle("z-index", ++index);
30931         }
30932         if(this.resizer){
30933             this.resizer.proxy.setStyle("z-index", ++index);
30934         }
30935
30936         this.lastZIndex = index;
30937     },
30938
30939     /**
30940      * Returns the element for this dialog
30941      * @return {Roo.Element} The underlying dialog Element
30942      */
30943     getEl : function(){
30944         return this.el;
30945     }
30946 });
30947
30948 /**
30949  * @class Roo.DialogManager
30950  * Provides global access to BasicDialogs that have been created and
30951  * support for z-indexing (layering) multiple open dialogs.
30952  */
30953 Roo.DialogManager = function(){
30954     var list = {};
30955     var accessList = [];
30956     var front = null;
30957
30958     // private
30959     var sortDialogs = function(d1, d2){
30960         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30961     };
30962
30963     // private
30964     var orderDialogs = function(){
30965         accessList.sort(sortDialogs);
30966         var seed = Roo.DialogManager.zseed;
30967         for(var i = 0, len = accessList.length; i < len; i++){
30968             var dlg = accessList[i];
30969             if(dlg){
30970                 dlg.setZIndex(seed + (i*10));
30971             }
30972         }
30973     };
30974
30975     return {
30976         /**
30977          * The starting z-index for BasicDialogs (defaults to 9000)
30978          * @type Number The z-index value
30979          */
30980         zseed : 9000,
30981
30982         // private
30983         register : function(dlg){
30984             list[dlg.id] = dlg;
30985             accessList.push(dlg);
30986         },
30987
30988         // private
30989         unregister : function(dlg){
30990             delete list[dlg.id];
30991             var i=0;
30992             var len=0;
30993             if(!accessList.indexOf){
30994                 for(  i = 0, len = accessList.length; i < len; i++){
30995                     if(accessList[i] == dlg){
30996                         accessList.splice(i, 1);
30997                         return;
30998                     }
30999                 }
31000             }else{
31001                  i = accessList.indexOf(dlg);
31002                 if(i != -1){
31003                     accessList.splice(i, 1);
31004                 }
31005             }
31006         },
31007
31008         /**
31009          * Gets a registered dialog by id
31010          * @param {String/Object} id The id of the dialog or a dialog
31011          * @return {Roo.BasicDialog} this
31012          */
31013         get : function(id){
31014             return typeof id == "object" ? id : list[id];
31015         },
31016
31017         /**
31018          * Brings the specified dialog to the front
31019          * @param {String/Object} dlg The id of the dialog or a dialog
31020          * @return {Roo.BasicDialog} this
31021          */
31022         bringToFront : function(dlg){
31023             dlg = this.get(dlg);
31024             if(dlg != front){
31025                 front = dlg;
31026                 dlg._lastAccess = new Date().getTime();
31027                 orderDialogs();
31028             }
31029             return dlg;
31030         },
31031
31032         /**
31033          * Sends the specified dialog to the back
31034          * @param {String/Object} dlg The id of the dialog or a dialog
31035          * @return {Roo.BasicDialog} this
31036          */
31037         sendToBack : function(dlg){
31038             dlg = this.get(dlg);
31039             dlg._lastAccess = -(new Date().getTime());
31040             orderDialogs();
31041             return dlg;
31042         },
31043
31044         /**
31045          * Hides all dialogs
31046          */
31047         hideAll : function(){
31048             for(var id in list){
31049                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31050                     list[id].hide();
31051                 }
31052             }
31053         }
31054     };
31055 }();
31056
31057 /**
31058  * @class Roo.LayoutDialog
31059  * @extends Roo.BasicDialog
31060  * Dialog which provides adjustments for working with a layout in a Dialog.
31061  * Add your necessary layout config options to the dialog's config.<br>
31062  * Example usage (including a nested layout):
31063  * <pre><code>
31064 if(!dialog){
31065     dialog = new Roo.LayoutDialog("download-dlg", {
31066         modal: true,
31067         width:600,
31068         height:450,
31069         shadow:true,
31070         minWidth:500,
31071         minHeight:350,
31072         autoTabs:true,
31073         proxyDrag:true,
31074         // layout config merges with the dialog config
31075         center:{
31076             tabPosition: "top",
31077             alwaysShowTabs: true
31078         }
31079     });
31080     dialog.addKeyListener(27, dialog.hide, dialog);
31081     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31082     dialog.addButton("Build It!", this.getDownload, this);
31083
31084     // we can even add nested layouts
31085     var innerLayout = new Roo.BorderLayout("dl-inner", {
31086         east: {
31087             initialSize: 200,
31088             autoScroll:true,
31089             split:true
31090         },
31091         center: {
31092             autoScroll:true
31093         }
31094     });
31095     innerLayout.beginUpdate();
31096     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31097     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31098     innerLayout.endUpdate(true);
31099
31100     var layout = dialog.getLayout();
31101     layout.beginUpdate();
31102     layout.add("center", new Roo.ContentPanel("standard-panel",
31103                         {title: "Download the Source", fitToFrame:true}));
31104     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31105                {title: "Build your own roo.js"}));
31106     layout.getRegion("center").showPanel(sp);
31107     layout.endUpdate();
31108 }
31109 </code></pre>
31110     * @constructor
31111     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31112     * @param {Object} config configuration options
31113   */
31114 Roo.LayoutDialog = function(el, cfg){
31115     
31116     var config=  cfg;
31117     if (typeof(cfg) == 'undefined') {
31118         config = Roo.apply({}, el);
31119         // not sure why we use documentElement here.. - it should always be body.
31120         // IE7 borks horribly if we use documentElement.
31121         // webkit also does not like documentElement - it creates a body element...
31122         el = Roo.get( document.body || document.documentElement ).createChild();
31123         //config.autoCreate = true;
31124     }
31125     
31126     
31127     config.autoTabs = false;
31128     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31129     this.body.setStyle({overflow:"hidden", position:"relative"});
31130     this.layout = new Roo.BorderLayout(this.body.dom, config);
31131     this.layout.monitorWindowResize = false;
31132     this.el.addClass("x-dlg-auto-layout");
31133     // fix case when center region overwrites center function
31134     this.center = Roo.BasicDialog.prototype.center;
31135     this.on("show", this.layout.layout, this.layout, true);
31136     if (config.items) {
31137         var xitems = config.items;
31138         delete config.items;
31139         Roo.each(xitems, this.addxtype, this);
31140     }
31141     
31142     
31143 };
31144 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31145     /**
31146      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31147      * @deprecated
31148      */
31149     endUpdate : function(){
31150         this.layout.endUpdate();
31151     },
31152
31153     /**
31154      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31155      *  @deprecated
31156      */
31157     beginUpdate : function(){
31158         this.layout.beginUpdate();
31159     },
31160
31161     /**
31162      * Get the BorderLayout for this dialog
31163      * @return {Roo.BorderLayout}
31164      */
31165     getLayout : function(){
31166         return this.layout;
31167     },
31168
31169     showEl : function(){
31170         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31171         if(Roo.isIE7){
31172             this.layout.layout();
31173         }
31174     },
31175
31176     // private
31177     // Use the syncHeightBeforeShow config option to control this automatically
31178     syncBodyHeight : function(){
31179         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31180         if(this.layout){this.layout.layout();}
31181     },
31182     
31183       /**
31184      * Add an xtype element (actually adds to the layout.)
31185      * @return {Object} xdata xtype object data.
31186      */
31187     
31188     addxtype : function(c) {
31189         return this.layout.addxtype(c);
31190     }
31191 });/*
31192  * Based on:
31193  * Ext JS Library 1.1.1
31194  * Copyright(c) 2006-2007, Ext JS, LLC.
31195  *
31196  * Originally Released Under LGPL - original licence link has changed is not relivant.
31197  *
31198  * Fork - LGPL
31199  * <script type="text/javascript">
31200  */
31201  
31202 /**
31203  * @class Roo.MessageBox
31204  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31205  * Example usage:
31206  *<pre><code>
31207 // Basic alert:
31208 Roo.Msg.alert('Status', 'Changes saved successfully.');
31209
31210 // Prompt for user data:
31211 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31212     if (btn == 'ok'){
31213         // process text value...
31214     }
31215 });
31216
31217 // Show a dialog using config options:
31218 Roo.Msg.show({
31219    title:'Save Changes?',
31220    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31221    buttons: Roo.Msg.YESNOCANCEL,
31222    fn: processResult,
31223    animEl: 'elId'
31224 });
31225 </code></pre>
31226  * @singleton
31227  */
31228 Roo.MessageBox = function(){
31229     var dlg, opt, mask, waitTimer;
31230     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31231     var buttons, activeTextEl, bwidth;
31232
31233     // private
31234     var handleButton = function(button){
31235         dlg.hide();
31236         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31237     };
31238
31239     // private
31240     var handleHide = function(){
31241         if(opt && opt.cls){
31242             dlg.el.removeClass(opt.cls);
31243         }
31244         if(waitTimer){
31245             Roo.TaskMgr.stop(waitTimer);
31246             waitTimer = null;
31247         }
31248     };
31249
31250     // private
31251     var updateButtons = function(b){
31252         var width = 0;
31253         if(!b){
31254             buttons["ok"].hide();
31255             buttons["cancel"].hide();
31256             buttons["yes"].hide();
31257             buttons["no"].hide();
31258             dlg.footer.dom.style.display = 'none';
31259             return width;
31260         }
31261         dlg.footer.dom.style.display = '';
31262         for(var k in buttons){
31263             if(typeof buttons[k] != "function"){
31264                 if(b[k]){
31265                     buttons[k].show();
31266                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31267                     width += buttons[k].el.getWidth()+15;
31268                 }else{
31269                     buttons[k].hide();
31270                 }
31271             }
31272         }
31273         return width;
31274     };
31275
31276     // private
31277     var handleEsc = function(d, k, e){
31278         if(opt && opt.closable !== false){
31279             dlg.hide();
31280         }
31281         if(e){
31282             e.stopEvent();
31283         }
31284     };
31285
31286     return {
31287         /**
31288          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31289          * @return {Roo.BasicDialog} The BasicDialog element
31290          */
31291         getDialog : function(){
31292            if(!dlg){
31293                 dlg = new Roo.BasicDialog("x-msg-box", {
31294                     autoCreate : true,
31295                     shadow: true,
31296                     draggable: true,
31297                     resizable:false,
31298                     constraintoviewport:false,
31299                     fixedcenter:true,
31300                     collapsible : false,
31301                     shim:true,
31302                     modal: true,
31303                     width:400, height:100,
31304                     buttonAlign:"center",
31305                     closeClick : function(){
31306                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31307                             handleButton("no");
31308                         }else{
31309                             handleButton("cancel");
31310                         }
31311                     }
31312                 });
31313                 dlg.on("hide", handleHide);
31314                 mask = dlg.mask;
31315                 dlg.addKeyListener(27, handleEsc);
31316                 buttons = {};
31317                 var bt = this.buttonText;
31318                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31319                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31320                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31321                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31322                 bodyEl = dlg.body.createChild({
31323
31324                     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>'
31325                 });
31326                 msgEl = bodyEl.dom.firstChild;
31327                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31328                 textboxEl.enableDisplayMode();
31329                 textboxEl.addKeyListener([10,13], function(){
31330                     if(dlg.isVisible() && opt && opt.buttons){
31331                         if(opt.buttons.ok){
31332                             handleButton("ok");
31333                         }else if(opt.buttons.yes){
31334                             handleButton("yes");
31335                         }
31336                     }
31337                 });
31338                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31339                 textareaEl.enableDisplayMode();
31340                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31341                 progressEl.enableDisplayMode();
31342                 var pf = progressEl.dom.firstChild;
31343                 if (pf) {
31344                     pp = Roo.get(pf.firstChild);
31345                     pp.setHeight(pf.offsetHeight);
31346                 }
31347                 
31348             }
31349             return dlg;
31350         },
31351
31352         /**
31353          * Updates the message box body text
31354          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31355          * the XHTML-compliant non-breaking space character '&amp;#160;')
31356          * @return {Roo.MessageBox} This message box
31357          */
31358         updateText : function(text){
31359             if(!dlg.isVisible() && !opt.width){
31360                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31361             }
31362             msgEl.innerHTML = text || '&#160;';
31363       
31364             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31365             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31366             var w = Math.max(
31367                     Math.min(opt.width || cw , this.maxWidth), 
31368                     Math.max(opt.minWidth || this.minWidth, bwidth)
31369             );
31370             if(opt.prompt){
31371                 activeTextEl.setWidth(w);
31372             }
31373             if(dlg.isVisible()){
31374                 dlg.fixedcenter = false;
31375             }
31376             // to big, make it scroll. = But as usual stupid IE does not support
31377             // !important..
31378             
31379             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31380                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31381                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31382             } else {
31383                 bodyEl.dom.style.height = '';
31384                 bodyEl.dom.style.overflowY = '';
31385             }
31386             if (cw > w) {
31387                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31388             } else {
31389                 bodyEl.dom.style.overflowX = '';
31390             }
31391             
31392             dlg.setContentSize(w, bodyEl.getHeight());
31393             if(dlg.isVisible()){
31394                 dlg.fixedcenter = true;
31395             }
31396             return this;
31397         },
31398
31399         /**
31400          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31401          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31402          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31403          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31404          * @return {Roo.MessageBox} This message box
31405          */
31406         updateProgress : function(value, text){
31407             if(text){
31408                 this.updateText(text);
31409             }
31410             if (pp) { // weird bug on my firefox - for some reason this is not defined
31411                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31412             }
31413             return this;
31414         },        
31415
31416         /**
31417          * Returns true if the message box is currently displayed
31418          * @return {Boolean} True if the message box is visible, else false
31419          */
31420         isVisible : function(){
31421             return dlg && dlg.isVisible();  
31422         },
31423
31424         /**
31425          * Hides the message box if it is displayed
31426          */
31427         hide : function(){
31428             if(this.isVisible()){
31429                 dlg.hide();
31430             }  
31431         },
31432
31433         /**
31434          * Displays a new message box, or reinitializes an existing message box, based on the config options
31435          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31436          * The following config object properties are supported:
31437          * <pre>
31438 Property    Type             Description
31439 ----------  ---------------  ------------------------------------------------------------------------------------
31440 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31441                                    closes (defaults to undefined)
31442 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31443                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31444 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31445                                    progress and wait dialogs will ignore this property and always hide the
31446                                    close button as they can only be closed programmatically.
31447 cls               String           A custom CSS class to apply to the message box element
31448 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31449                                    displayed (defaults to 75)
31450 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31451                                    function will be btn (the name of the button that was clicked, if applicable,
31452                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31453                                    Progress and wait dialogs will ignore this option since they do not respond to
31454                                    user actions and can only be closed programmatically, so any required function
31455                                    should be called by the same code after it closes the dialog.
31456 icon              String           A CSS class that provides a background image to be used as an icon for
31457                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31458 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31459 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31460 modal             Boolean          False to allow user interaction with the page while the message box is
31461                                    displayed (defaults to true)
31462 msg               String           A string that will replace the existing message box body text (defaults
31463                                    to the XHTML-compliant non-breaking space character '&#160;')
31464 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31465 progress          Boolean          True to display a progress bar (defaults to false)
31466 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31467 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31468 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31469 title             String           The title text
31470 value             String           The string value to set into the active textbox element if displayed
31471 wait              Boolean          True to display a progress bar (defaults to false)
31472 width             Number           The width of the dialog in pixels
31473 </pre>
31474          *
31475          * Example usage:
31476          * <pre><code>
31477 Roo.Msg.show({
31478    title: 'Address',
31479    msg: 'Please enter your address:',
31480    width: 300,
31481    buttons: Roo.MessageBox.OKCANCEL,
31482    multiline: true,
31483    fn: saveAddress,
31484    animEl: 'addAddressBtn'
31485 });
31486 </code></pre>
31487          * @param {Object} config Configuration options
31488          * @return {Roo.MessageBox} This message box
31489          */
31490         show : function(options)
31491         {
31492             
31493             // this causes nightmares if you show one dialog after another
31494             // especially on callbacks..
31495              
31496             if(this.isVisible()){
31497                 
31498                 this.hide();
31499                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31500                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31501                 Roo.log("New Dialog Message:" +  options.msg )
31502                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31503                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31504                 
31505             }
31506             var d = this.getDialog();
31507             opt = options;
31508             d.setTitle(opt.title || "&#160;");
31509             d.close.setDisplayed(opt.closable !== false);
31510             activeTextEl = textboxEl;
31511             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31512             if(opt.prompt){
31513                 if(opt.multiline){
31514                     textboxEl.hide();
31515                     textareaEl.show();
31516                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31517                         opt.multiline : this.defaultTextHeight);
31518                     activeTextEl = textareaEl;
31519                 }else{
31520                     textboxEl.show();
31521                     textareaEl.hide();
31522                 }
31523             }else{
31524                 textboxEl.hide();
31525                 textareaEl.hide();
31526             }
31527             progressEl.setDisplayed(opt.progress === true);
31528             this.updateProgress(0);
31529             activeTextEl.dom.value = opt.value || "";
31530             if(opt.prompt){
31531                 dlg.setDefaultButton(activeTextEl);
31532             }else{
31533                 var bs = opt.buttons;
31534                 var db = null;
31535                 if(bs && bs.ok){
31536                     db = buttons["ok"];
31537                 }else if(bs && bs.yes){
31538                     db = buttons["yes"];
31539                 }
31540                 dlg.setDefaultButton(db);
31541             }
31542             bwidth = updateButtons(opt.buttons);
31543             this.updateText(opt.msg);
31544             if(opt.cls){
31545                 d.el.addClass(opt.cls);
31546             }
31547             d.proxyDrag = opt.proxyDrag === true;
31548             d.modal = opt.modal !== false;
31549             d.mask = opt.modal !== false ? mask : false;
31550             if(!d.isVisible()){
31551                 // force it to the end of the z-index stack so it gets a cursor in FF
31552                 document.body.appendChild(dlg.el.dom);
31553                 d.animateTarget = null;
31554                 d.show(options.animEl);
31555             }
31556             return this;
31557         },
31558
31559         /**
31560          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31561          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31562          * and closing the message box when the process is complete.
31563          * @param {String} title The title bar text
31564          * @param {String} msg The message box body text
31565          * @return {Roo.MessageBox} This message box
31566          */
31567         progress : function(title, msg){
31568             this.show({
31569                 title : title,
31570                 msg : msg,
31571                 buttons: false,
31572                 progress:true,
31573                 closable:false,
31574                 minWidth: this.minProgressWidth,
31575                 modal : true
31576             });
31577             return this;
31578         },
31579
31580         /**
31581          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31582          * If a callback function is passed it will be called after the user clicks the button, and the
31583          * id of the button that was clicked will be passed as the only parameter to the callback
31584          * (could also be the top-right close button).
31585          * @param {String} title The title bar text
31586          * @param {String} msg The message box body text
31587          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31588          * @param {Object} scope (optional) The scope of the callback function
31589          * @return {Roo.MessageBox} This message box
31590          */
31591         alert : function(title, msg, fn, scope){
31592             this.show({
31593                 title : title,
31594                 msg : msg,
31595                 buttons: this.OK,
31596                 fn: fn,
31597                 scope : scope,
31598                 modal : true
31599             });
31600             return this;
31601         },
31602
31603         /**
31604          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31605          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31606          * You are responsible for closing the message box when the process is complete.
31607          * @param {String} msg The message box body text
31608          * @param {String} title (optional) The title bar text
31609          * @return {Roo.MessageBox} This message box
31610          */
31611         wait : function(msg, title){
31612             this.show({
31613                 title : title,
31614                 msg : msg,
31615                 buttons: false,
31616                 closable:false,
31617                 progress:true,
31618                 modal:true,
31619                 width:300,
31620                 wait:true
31621             });
31622             waitTimer = Roo.TaskMgr.start({
31623                 run: function(i){
31624                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31625                 },
31626                 interval: 1000
31627             });
31628             return this;
31629         },
31630
31631         /**
31632          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31633          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31634          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31635          * @param {String} title The title bar text
31636          * @param {String} msg The message box body text
31637          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31638          * @param {Object} scope (optional) The scope of the callback function
31639          * @return {Roo.MessageBox} This message box
31640          */
31641         confirm : function(title, msg, fn, scope){
31642             this.show({
31643                 title : title,
31644                 msg : msg,
31645                 buttons: this.YESNO,
31646                 fn: fn,
31647                 scope : scope,
31648                 modal : true
31649             });
31650             return this;
31651         },
31652
31653         /**
31654          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31655          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31656          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31657          * (could also be the top-right close button) and the text that was entered will be passed as the two
31658          * parameters to the callback.
31659          * @param {String} title The title bar text
31660          * @param {String} msg The message box body text
31661          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31662          * @param {Object} scope (optional) The scope of the callback function
31663          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31664          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31665          * @return {Roo.MessageBox} This message box
31666          */
31667         prompt : function(title, msg, fn, scope, multiline){
31668             this.show({
31669                 title : title,
31670                 msg : msg,
31671                 buttons: this.OKCANCEL,
31672                 fn: fn,
31673                 minWidth:250,
31674                 scope : scope,
31675                 prompt:true,
31676                 multiline: multiline,
31677                 modal : true
31678             });
31679             return this;
31680         },
31681
31682         /**
31683          * Button config that displays a single OK button
31684          * @type Object
31685          */
31686         OK : {ok:true},
31687         /**
31688          * Button config that displays Yes and No buttons
31689          * @type Object
31690          */
31691         YESNO : {yes:true, no:true},
31692         /**
31693          * Button config that displays OK and Cancel buttons
31694          * @type Object
31695          */
31696         OKCANCEL : {ok:true, cancel:true},
31697         /**
31698          * Button config that displays Yes, No and Cancel buttons
31699          * @type Object
31700          */
31701         YESNOCANCEL : {yes:true, no:true, cancel:true},
31702
31703         /**
31704          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31705          * @type Number
31706          */
31707         defaultTextHeight : 75,
31708         /**
31709          * The maximum width in pixels of the message box (defaults to 600)
31710          * @type Number
31711          */
31712         maxWidth : 600,
31713         /**
31714          * The minimum width in pixels of the message box (defaults to 100)
31715          * @type Number
31716          */
31717         minWidth : 100,
31718         /**
31719          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31720          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31721          * @type Number
31722          */
31723         minProgressWidth : 250,
31724         /**
31725          * An object containing the default button text strings that can be overriden for localized language support.
31726          * Supported properties are: ok, cancel, yes and no.
31727          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31728          * @type Object
31729          */
31730         buttonText : {
31731             ok : "OK",
31732             cancel : "Cancel",
31733             yes : "Yes",
31734             no : "No"
31735         }
31736     };
31737 }();
31738
31739 /**
31740  * Shorthand for {@link Roo.MessageBox}
31741  */
31742 Roo.Msg = Roo.MessageBox;/*
31743  * Based on:
31744  * Ext JS Library 1.1.1
31745  * Copyright(c) 2006-2007, Ext JS, LLC.
31746  *
31747  * Originally Released Under LGPL - original licence link has changed is not relivant.
31748  *
31749  * Fork - LGPL
31750  * <script type="text/javascript">
31751  */
31752 /**
31753  * @class Roo.QuickTips
31754  * Provides attractive and customizable tooltips for any element.
31755  * @singleton
31756  */
31757 Roo.QuickTips = function(){
31758     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31759     var ce, bd, xy, dd;
31760     var visible = false, disabled = true, inited = false;
31761     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31762     
31763     var onOver = function(e){
31764         if(disabled){
31765             return;
31766         }
31767         var t = e.getTarget();
31768         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31769             return;
31770         }
31771         if(ce && t == ce.el){
31772             clearTimeout(hideProc);
31773             return;
31774         }
31775         if(t && tagEls[t.id]){
31776             tagEls[t.id].el = t;
31777             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31778             return;
31779         }
31780         var ttp, et = Roo.fly(t);
31781         var ns = cfg.namespace;
31782         if(tm.interceptTitles && t.title){
31783             ttp = t.title;
31784             t.qtip = ttp;
31785             t.removeAttribute("title");
31786             e.preventDefault();
31787         }else{
31788             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31789         }
31790         if(ttp){
31791             showProc = show.defer(tm.showDelay, tm, [{
31792                 el: t, 
31793                 text: ttp, 
31794                 width: et.getAttributeNS(ns, cfg.width),
31795                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31796                 title: et.getAttributeNS(ns, cfg.title),
31797                     cls: et.getAttributeNS(ns, cfg.cls)
31798             }]);
31799         }
31800     };
31801     
31802     var onOut = function(e){
31803         clearTimeout(showProc);
31804         var t = e.getTarget();
31805         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31806             hideProc = setTimeout(hide, tm.hideDelay);
31807         }
31808     };
31809     
31810     var onMove = function(e){
31811         if(disabled){
31812             return;
31813         }
31814         xy = e.getXY();
31815         xy[1] += 18;
31816         if(tm.trackMouse && ce){
31817             el.setXY(xy);
31818         }
31819     };
31820     
31821     var onDown = function(e){
31822         clearTimeout(showProc);
31823         clearTimeout(hideProc);
31824         if(!e.within(el)){
31825             if(tm.hideOnClick){
31826                 hide();
31827                 tm.disable();
31828                 tm.enable.defer(100, tm);
31829             }
31830         }
31831     };
31832     
31833     var getPad = function(){
31834         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31835     };
31836
31837     var show = function(o){
31838         if(disabled){
31839             return;
31840         }
31841         clearTimeout(dismissProc);
31842         ce = o;
31843         if(removeCls){ // in case manually hidden
31844             el.removeClass(removeCls);
31845             removeCls = null;
31846         }
31847         if(ce.cls){
31848             el.addClass(ce.cls);
31849             removeCls = ce.cls;
31850         }
31851         if(ce.title){
31852             tipTitle.update(ce.title);
31853             tipTitle.show();
31854         }else{
31855             tipTitle.update('');
31856             tipTitle.hide();
31857         }
31858         el.dom.style.width  = tm.maxWidth+'px';
31859         //tipBody.dom.style.width = '';
31860         tipBodyText.update(o.text);
31861         var p = getPad(), w = ce.width;
31862         if(!w){
31863             var td = tipBodyText.dom;
31864             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31865             if(aw > tm.maxWidth){
31866                 w = tm.maxWidth;
31867             }else if(aw < tm.minWidth){
31868                 w = tm.minWidth;
31869             }else{
31870                 w = aw;
31871             }
31872         }
31873         //tipBody.setWidth(w);
31874         el.setWidth(parseInt(w, 10) + p);
31875         if(ce.autoHide === false){
31876             close.setDisplayed(true);
31877             if(dd){
31878                 dd.unlock();
31879             }
31880         }else{
31881             close.setDisplayed(false);
31882             if(dd){
31883                 dd.lock();
31884             }
31885         }
31886         if(xy){
31887             el.avoidY = xy[1]-18;
31888             el.setXY(xy);
31889         }
31890         if(tm.animate){
31891             el.setOpacity(.1);
31892             el.setStyle("visibility", "visible");
31893             el.fadeIn({callback: afterShow});
31894         }else{
31895             afterShow();
31896         }
31897     };
31898     
31899     var afterShow = function(){
31900         if(ce){
31901             el.show();
31902             esc.enable();
31903             if(tm.autoDismiss && ce.autoHide !== false){
31904                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31905             }
31906         }
31907     };
31908     
31909     var hide = function(noanim){
31910         clearTimeout(dismissProc);
31911         clearTimeout(hideProc);
31912         ce = null;
31913         if(el.isVisible()){
31914             esc.disable();
31915             if(noanim !== true && tm.animate){
31916                 el.fadeOut({callback: afterHide});
31917             }else{
31918                 afterHide();
31919             } 
31920         }
31921     };
31922     
31923     var afterHide = function(){
31924         el.hide();
31925         if(removeCls){
31926             el.removeClass(removeCls);
31927             removeCls = null;
31928         }
31929     };
31930     
31931     return {
31932         /**
31933         * @cfg {Number} minWidth
31934         * The minimum width of the quick tip (defaults to 40)
31935         */
31936        minWidth : 40,
31937         /**
31938         * @cfg {Number} maxWidth
31939         * The maximum width of the quick tip (defaults to 300)
31940         */
31941        maxWidth : 300,
31942         /**
31943         * @cfg {Boolean} interceptTitles
31944         * True to automatically use the element's DOM title value if available (defaults to false)
31945         */
31946        interceptTitles : false,
31947         /**
31948         * @cfg {Boolean} trackMouse
31949         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31950         */
31951        trackMouse : false,
31952         /**
31953         * @cfg {Boolean} hideOnClick
31954         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31955         */
31956        hideOnClick : true,
31957         /**
31958         * @cfg {Number} showDelay
31959         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31960         */
31961        showDelay : 500,
31962         /**
31963         * @cfg {Number} hideDelay
31964         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31965         */
31966        hideDelay : 200,
31967         /**
31968         * @cfg {Boolean} autoHide
31969         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31970         * Used in conjunction with hideDelay.
31971         */
31972        autoHide : true,
31973         /**
31974         * @cfg {Boolean}
31975         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31976         * (defaults to true).  Used in conjunction with autoDismissDelay.
31977         */
31978        autoDismiss : true,
31979         /**
31980         * @cfg {Number}
31981         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31982         */
31983        autoDismissDelay : 5000,
31984        /**
31985         * @cfg {Boolean} animate
31986         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31987         */
31988        animate : false,
31989
31990        /**
31991         * @cfg {String} title
31992         * Title text to display (defaults to '').  This can be any valid HTML markup.
31993         */
31994         title: '',
31995        /**
31996         * @cfg {String} text
31997         * Body text to display (defaults to '').  This can be any valid HTML markup.
31998         */
31999         text : '',
32000        /**
32001         * @cfg {String} cls
32002         * A CSS class to apply to the base quick tip element (defaults to '').
32003         */
32004         cls : '',
32005        /**
32006         * @cfg {Number} width
32007         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32008         * minWidth or maxWidth.
32009         */
32010         width : null,
32011
32012     /**
32013      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32014      * or display QuickTips in a page.
32015      */
32016        init : function(){
32017           tm = Roo.QuickTips;
32018           cfg = tm.tagConfig;
32019           if(!inited){
32020               if(!Roo.isReady){ // allow calling of init() before onReady
32021                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32022                   return;
32023               }
32024               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32025               el.fxDefaults = {stopFx: true};
32026               // maximum custom styling
32027               //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>');
32028               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>');              
32029               tipTitle = el.child('h3');
32030               tipTitle.enableDisplayMode("block");
32031               tipBody = el.child('div.x-tip-bd');
32032               tipBodyText = el.child('div.x-tip-bd-inner');
32033               //bdLeft = el.child('div.x-tip-bd-left');
32034               //bdRight = el.child('div.x-tip-bd-right');
32035               close = el.child('div.x-tip-close');
32036               close.enableDisplayMode("block");
32037               close.on("click", hide);
32038               var d = Roo.get(document);
32039               d.on("mousedown", onDown);
32040               d.on("mouseover", onOver);
32041               d.on("mouseout", onOut);
32042               d.on("mousemove", onMove);
32043               esc = d.addKeyListener(27, hide);
32044               esc.disable();
32045               if(Roo.dd.DD){
32046                   dd = el.initDD("default", null, {
32047                       onDrag : function(){
32048                           el.sync();  
32049                       }
32050                   });
32051                   dd.setHandleElId(tipTitle.id);
32052                   dd.lock();
32053               }
32054               inited = true;
32055           }
32056           this.enable(); 
32057        },
32058
32059     /**
32060      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32061      * are supported:
32062      * <pre>
32063 Property    Type                   Description
32064 ----------  ---------------------  ------------------------------------------------------------------------
32065 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32066      * </ul>
32067      * @param {Object} config The config object
32068      */
32069        register : function(config){
32070            var cs = config instanceof Array ? config : arguments;
32071            for(var i = 0, len = cs.length; i < len; i++) {
32072                var c = cs[i];
32073                var target = c.target;
32074                if(target){
32075                    if(target instanceof Array){
32076                        for(var j = 0, jlen = target.length; j < jlen; j++){
32077                            tagEls[target[j]] = c;
32078                        }
32079                    }else{
32080                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32081                    }
32082                }
32083            }
32084        },
32085
32086     /**
32087      * Removes this quick tip from its element and destroys it.
32088      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32089      */
32090        unregister : function(el){
32091            delete tagEls[Roo.id(el)];
32092        },
32093
32094     /**
32095      * Enable this quick tip.
32096      */
32097        enable : function(){
32098            if(inited && disabled){
32099                locks.pop();
32100                if(locks.length < 1){
32101                    disabled = false;
32102                }
32103            }
32104        },
32105
32106     /**
32107      * Disable this quick tip.
32108      */
32109        disable : function(){
32110           disabled = true;
32111           clearTimeout(showProc);
32112           clearTimeout(hideProc);
32113           clearTimeout(dismissProc);
32114           if(ce){
32115               hide(true);
32116           }
32117           locks.push(1);
32118        },
32119
32120     /**
32121      * Returns true if the quick tip is enabled, else false.
32122      */
32123        isEnabled : function(){
32124             return !disabled;
32125        },
32126
32127         // private
32128        tagConfig : {
32129            namespace : "ext",
32130            attribute : "qtip",
32131            width : "width",
32132            target : "target",
32133            title : "qtitle",
32134            hide : "hide",
32135            cls : "qclass"
32136        }
32137    };
32138 }();
32139
32140 // backwards compat
32141 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32142  * Based on:
32143  * Ext JS Library 1.1.1
32144  * Copyright(c) 2006-2007, Ext JS, LLC.
32145  *
32146  * Originally Released Under LGPL - original licence link has changed is not relivant.
32147  *
32148  * Fork - LGPL
32149  * <script type="text/javascript">
32150  */
32151  
32152
32153 /**
32154  * @class Roo.tree.TreePanel
32155  * @extends Roo.data.Tree
32156
32157  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32158  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32159  * @cfg {Boolean} enableDD true to enable drag and drop
32160  * @cfg {Boolean} enableDrag true to enable just drag
32161  * @cfg {Boolean} enableDrop true to enable just drop
32162  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32163  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32164  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32165  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32166  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32167  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32168  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32169  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32170  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32171  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32172  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32173  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32174  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32175  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32176  * @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>
32177  * @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>
32178  * 
32179  * @constructor
32180  * @param {String/HTMLElement/Element} el The container element
32181  * @param {Object} config
32182  */
32183 Roo.tree.TreePanel = function(el, config){
32184     var root = false;
32185     var loader = false;
32186     if (config.root) {
32187         root = config.root;
32188         delete config.root;
32189     }
32190     if (config.loader) {
32191         loader = config.loader;
32192         delete config.loader;
32193     }
32194     
32195     Roo.apply(this, config);
32196     Roo.tree.TreePanel.superclass.constructor.call(this);
32197     this.el = Roo.get(el);
32198     this.el.addClass('x-tree');
32199     //console.log(root);
32200     if (root) {
32201         this.setRootNode( Roo.factory(root, Roo.tree));
32202     }
32203     if (loader) {
32204         this.loader = Roo.factory(loader, Roo.tree);
32205     }
32206    /**
32207     * Read-only. The id of the container element becomes this TreePanel's id.
32208     */
32209     this.id = this.el.id;
32210     this.addEvents({
32211         /**
32212         * @event beforeload
32213         * Fires before a node is loaded, return false to cancel
32214         * @param {Node} node The node being loaded
32215         */
32216         "beforeload" : true,
32217         /**
32218         * @event load
32219         * Fires when a node is loaded
32220         * @param {Node} node The node that was loaded
32221         */
32222         "load" : true,
32223         /**
32224         * @event textchange
32225         * Fires when the text for a node is changed
32226         * @param {Node} node The node
32227         * @param {String} text The new text
32228         * @param {String} oldText The old text
32229         */
32230         "textchange" : true,
32231         /**
32232         * @event beforeexpand
32233         * Fires before a node is expanded, return false to cancel.
32234         * @param {Node} node The node
32235         * @param {Boolean} deep
32236         * @param {Boolean} anim
32237         */
32238         "beforeexpand" : true,
32239         /**
32240         * @event beforecollapse
32241         * Fires before a node is collapsed, return false to cancel.
32242         * @param {Node} node The node
32243         * @param {Boolean} deep
32244         * @param {Boolean} anim
32245         */
32246         "beforecollapse" : true,
32247         /**
32248         * @event expand
32249         * Fires when a node is expanded
32250         * @param {Node} node The node
32251         */
32252         "expand" : true,
32253         /**
32254         * @event disabledchange
32255         * Fires when the disabled status of a node changes
32256         * @param {Node} node The node
32257         * @param {Boolean} disabled
32258         */
32259         "disabledchange" : true,
32260         /**
32261         * @event collapse
32262         * Fires when a node is collapsed
32263         * @param {Node} node The node
32264         */
32265         "collapse" : true,
32266         /**
32267         * @event beforeclick
32268         * Fires before click processing on a node. Return false to cancel the default action.
32269         * @param {Node} node The node
32270         * @param {Roo.EventObject} e The event object
32271         */
32272         "beforeclick":true,
32273         /**
32274         * @event checkchange
32275         * Fires when a node with a checkbox's checked property changes
32276         * @param {Node} this This node
32277         * @param {Boolean} checked
32278         */
32279         "checkchange":true,
32280         /**
32281         * @event click
32282         * Fires when a node is clicked
32283         * @param {Node} node The node
32284         * @param {Roo.EventObject} e The event object
32285         */
32286         "click":true,
32287         /**
32288         * @event dblclick
32289         * Fires when a node is double clicked
32290         * @param {Node} node The node
32291         * @param {Roo.EventObject} e The event object
32292         */
32293         "dblclick":true,
32294         /**
32295         * @event contextmenu
32296         * Fires when a node is right clicked
32297         * @param {Node} node The node
32298         * @param {Roo.EventObject} e The event object
32299         */
32300         "contextmenu":true,
32301         /**
32302         * @event beforechildrenrendered
32303         * Fires right before the child nodes for a node are rendered
32304         * @param {Node} node The node
32305         */
32306         "beforechildrenrendered":true,
32307         /**
32308         * @event startdrag
32309         * Fires when a node starts being dragged
32310         * @param {Roo.tree.TreePanel} this
32311         * @param {Roo.tree.TreeNode} node
32312         * @param {event} e The raw browser event
32313         */ 
32314        "startdrag" : true,
32315        /**
32316         * @event enddrag
32317         * Fires when a drag operation is complete
32318         * @param {Roo.tree.TreePanel} this
32319         * @param {Roo.tree.TreeNode} node
32320         * @param {event} e The raw browser event
32321         */
32322        "enddrag" : true,
32323        /**
32324         * @event dragdrop
32325         * Fires when a dragged node is dropped on a valid DD target
32326         * @param {Roo.tree.TreePanel} this
32327         * @param {Roo.tree.TreeNode} node
32328         * @param {DD} dd The dd it was dropped on
32329         * @param {event} e The raw browser event
32330         */
32331        "dragdrop" : true,
32332        /**
32333         * @event beforenodedrop
32334         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32335         * passed to handlers has the following properties:<br />
32336         * <ul style="padding:5px;padding-left:16px;">
32337         * <li>tree - The TreePanel</li>
32338         * <li>target - The node being targeted for the drop</li>
32339         * <li>data - The drag data from the drag source</li>
32340         * <li>point - The point of the drop - append, above or below</li>
32341         * <li>source - The drag source</li>
32342         * <li>rawEvent - Raw mouse event</li>
32343         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32344         * to be inserted by setting them on this object.</li>
32345         * <li>cancel - Set this to true to cancel the drop.</li>
32346         * </ul>
32347         * @param {Object} dropEvent
32348         */
32349        "beforenodedrop" : true,
32350        /**
32351         * @event nodedrop
32352         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32353         * passed to handlers has the following properties:<br />
32354         * <ul style="padding:5px;padding-left:16px;">
32355         * <li>tree - The TreePanel</li>
32356         * <li>target - The node being targeted for the drop</li>
32357         * <li>data - The drag data from the drag source</li>
32358         * <li>point - The point of the drop - append, above or below</li>
32359         * <li>source - The drag source</li>
32360         * <li>rawEvent - Raw mouse event</li>
32361         * <li>dropNode - Dropped node(s).</li>
32362         * </ul>
32363         * @param {Object} dropEvent
32364         */
32365        "nodedrop" : true,
32366         /**
32367         * @event nodedragover
32368         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32369         * passed to handlers has the following properties:<br />
32370         * <ul style="padding:5px;padding-left:16px;">
32371         * <li>tree - The TreePanel</li>
32372         * <li>target - The node being targeted for the drop</li>
32373         * <li>data - The drag data from the drag source</li>
32374         * <li>point - The point of the drop - append, above or below</li>
32375         * <li>source - The drag source</li>
32376         * <li>rawEvent - Raw mouse event</li>
32377         * <li>dropNode - Drop node(s) provided by the source.</li>
32378         * <li>cancel - Set this to true to signal drop not allowed.</li>
32379         * </ul>
32380         * @param {Object} dragOverEvent
32381         */
32382        "nodedragover" : true
32383         
32384     });
32385     if(this.singleExpand){
32386        this.on("beforeexpand", this.restrictExpand, this);
32387     }
32388     if (this.editor) {
32389         this.editor.tree = this;
32390         this.editor = Roo.factory(this.editor, Roo.tree);
32391     }
32392     
32393     if (this.selModel) {
32394         this.selModel = Roo.factory(this.selModel, Roo.tree);
32395     }
32396    
32397 };
32398 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32399     rootVisible : true,
32400     animate: Roo.enableFx,
32401     lines : true,
32402     enableDD : false,
32403     hlDrop : Roo.enableFx,
32404   
32405     renderer: false,
32406     
32407     rendererTip: false,
32408     // private
32409     restrictExpand : function(node){
32410         var p = node.parentNode;
32411         if(p){
32412             if(p.expandedChild && p.expandedChild.parentNode == p){
32413                 p.expandedChild.collapse();
32414             }
32415             p.expandedChild = node;
32416         }
32417     },
32418
32419     // private override
32420     setRootNode : function(node){
32421         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32422         if(!this.rootVisible){
32423             node.ui = new Roo.tree.RootTreeNodeUI(node);
32424         }
32425         return node;
32426     },
32427
32428     /**
32429      * Returns the container element for this TreePanel
32430      */
32431     getEl : function(){
32432         return this.el;
32433     },
32434
32435     /**
32436      * Returns the default TreeLoader for this TreePanel
32437      */
32438     getLoader : function(){
32439         return this.loader;
32440     },
32441
32442     /**
32443      * Expand all nodes
32444      */
32445     expandAll : function(){
32446         this.root.expand(true);
32447     },
32448
32449     /**
32450      * Collapse all nodes
32451      */
32452     collapseAll : function(){
32453         this.root.collapse(true);
32454     },
32455
32456     /**
32457      * Returns the selection model used by this TreePanel
32458      */
32459     getSelectionModel : function(){
32460         if(!this.selModel){
32461             this.selModel = new Roo.tree.DefaultSelectionModel();
32462         }
32463         return this.selModel;
32464     },
32465
32466     /**
32467      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32468      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32469      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32470      * @return {Array}
32471      */
32472     getChecked : function(a, startNode){
32473         startNode = startNode || this.root;
32474         var r = [];
32475         var f = function(){
32476             if(this.attributes.checked){
32477                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32478             }
32479         }
32480         startNode.cascade(f);
32481         return r;
32482     },
32483
32484     /**
32485      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32486      * @param {String} path
32487      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32488      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32489      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32490      */
32491     expandPath : function(path, attr, callback){
32492         attr = attr || "id";
32493         var keys = path.split(this.pathSeparator);
32494         var curNode = this.root;
32495         if(curNode.attributes[attr] != keys[1]){ // invalid root
32496             if(callback){
32497                 callback(false, null);
32498             }
32499             return;
32500         }
32501         var index = 1;
32502         var f = function(){
32503             if(++index == keys.length){
32504                 if(callback){
32505                     callback(true, curNode);
32506                 }
32507                 return;
32508             }
32509             var c = curNode.findChild(attr, keys[index]);
32510             if(!c){
32511                 if(callback){
32512                     callback(false, curNode);
32513                 }
32514                 return;
32515             }
32516             curNode = c;
32517             c.expand(false, false, f);
32518         };
32519         curNode.expand(false, false, f);
32520     },
32521
32522     /**
32523      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32524      * @param {String} path
32525      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32526      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32527      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32528      */
32529     selectPath : function(path, attr, callback){
32530         attr = attr || "id";
32531         var keys = path.split(this.pathSeparator);
32532         var v = keys.pop();
32533         if(keys.length > 0){
32534             var f = function(success, node){
32535                 if(success && node){
32536                     var n = node.findChild(attr, v);
32537                     if(n){
32538                         n.select();
32539                         if(callback){
32540                             callback(true, n);
32541                         }
32542                     }else if(callback){
32543                         callback(false, n);
32544                     }
32545                 }else{
32546                     if(callback){
32547                         callback(false, n);
32548                     }
32549                 }
32550             };
32551             this.expandPath(keys.join(this.pathSeparator), attr, f);
32552         }else{
32553             this.root.select();
32554             if(callback){
32555                 callback(true, this.root);
32556             }
32557         }
32558     },
32559
32560     getTreeEl : function(){
32561         return this.el;
32562     },
32563
32564     /**
32565      * Trigger rendering of this TreePanel
32566      */
32567     render : function(){
32568         if (this.innerCt) {
32569             return this; // stop it rendering more than once!!
32570         }
32571         
32572         this.innerCt = this.el.createChild({tag:"ul",
32573                cls:"x-tree-root-ct " +
32574                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32575
32576         if(this.containerScroll){
32577             Roo.dd.ScrollManager.register(this.el);
32578         }
32579         if((this.enableDD || this.enableDrop) && !this.dropZone){
32580            /**
32581             * The dropZone used by this tree if drop is enabled
32582             * @type Roo.tree.TreeDropZone
32583             */
32584              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32585                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32586            });
32587         }
32588         if((this.enableDD || this.enableDrag) && !this.dragZone){
32589            /**
32590             * The dragZone used by this tree if drag is enabled
32591             * @type Roo.tree.TreeDragZone
32592             */
32593             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32594                ddGroup: this.ddGroup || "TreeDD",
32595                scroll: this.ddScroll
32596            });
32597         }
32598         this.getSelectionModel().init(this);
32599         if (!this.root) {
32600             Roo.log("ROOT not set in tree");
32601             return this;
32602         }
32603         this.root.render();
32604         if(!this.rootVisible){
32605             this.root.renderChildren();
32606         }
32607         return this;
32608     }
32609 });/*
32610  * Based on:
32611  * Ext JS Library 1.1.1
32612  * Copyright(c) 2006-2007, Ext JS, LLC.
32613  *
32614  * Originally Released Under LGPL - original licence link has changed is not relivant.
32615  *
32616  * Fork - LGPL
32617  * <script type="text/javascript">
32618  */
32619  
32620
32621 /**
32622  * @class Roo.tree.DefaultSelectionModel
32623  * @extends Roo.util.Observable
32624  * The default single selection for a TreePanel.
32625  * @param {Object} cfg Configuration
32626  */
32627 Roo.tree.DefaultSelectionModel = function(cfg){
32628    this.selNode = null;
32629    
32630    
32631    
32632    this.addEvents({
32633        /**
32634         * @event selectionchange
32635         * Fires when the selected node changes
32636         * @param {DefaultSelectionModel} this
32637         * @param {TreeNode} node the new selection
32638         */
32639        "selectionchange" : true,
32640
32641        /**
32642         * @event beforeselect
32643         * Fires before the selected node changes, return false to cancel the change
32644         * @param {DefaultSelectionModel} this
32645         * @param {TreeNode} node the new selection
32646         * @param {TreeNode} node the old selection
32647         */
32648        "beforeselect" : true
32649    });
32650    
32651     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32652 };
32653
32654 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32655     init : function(tree){
32656         this.tree = tree;
32657         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32658         tree.on("click", this.onNodeClick, this);
32659     },
32660     
32661     onNodeClick : function(node, e){
32662         if (e.ctrlKey && this.selNode == node)  {
32663             this.unselect(node);
32664             return;
32665         }
32666         this.select(node);
32667     },
32668     
32669     /**
32670      * Select a node.
32671      * @param {TreeNode} node The node to select
32672      * @return {TreeNode} The selected node
32673      */
32674     select : function(node){
32675         var last = this.selNode;
32676         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32677             if(last){
32678                 last.ui.onSelectedChange(false);
32679             }
32680             this.selNode = node;
32681             node.ui.onSelectedChange(true);
32682             this.fireEvent("selectionchange", this, node, last);
32683         }
32684         return node;
32685     },
32686     
32687     /**
32688      * Deselect a node.
32689      * @param {TreeNode} node The node to unselect
32690      */
32691     unselect : function(node){
32692         if(this.selNode == node){
32693             this.clearSelections();
32694         }    
32695     },
32696     
32697     /**
32698      * Clear all selections
32699      */
32700     clearSelections : function(){
32701         var n = this.selNode;
32702         if(n){
32703             n.ui.onSelectedChange(false);
32704             this.selNode = null;
32705             this.fireEvent("selectionchange", this, null);
32706         }
32707         return n;
32708     },
32709     
32710     /**
32711      * Get the selected node
32712      * @return {TreeNode} The selected node
32713      */
32714     getSelectedNode : function(){
32715         return this.selNode;    
32716     },
32717     
32718     /**
32719      * Returns true if the node is selected
32720      * @param {TreeNode} node The node to check
32721      * @return {Boolean}
32722      */
32723     isSelected : function(node){
32724         return this.selNode == node;  
32725     },
32726
32727     /**
32728      * Selects the node above the selected node in the tree, intelligently walking the nodes
32729      * @return TreeNode The new selection
32730      */
32731     selectPrevious : function(){
32732         var s = this.selNode || this.lastSelNode;
32733         if(!s){
32734             return null;
32735         }
32736         var ps = s.previousSibling;
32737         if(ps){
32738             if(!ps.isExpanded() || ps.childNodes.length < 1){
32739                 return this.select(ps);
32740             } else{
32741                 var lc = ps.lastChild;
32742                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32743                     lc = lc.lastChild;
32744                 }
32745                 return this.select(lc);
32746             }
32747         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32748             return this.select(s.parentNode);
32749         }
32750         return null;
32751     },
32752
32753     /**
32754      * Selects the node above the selected node in the tree, intelligently walking the nodes
32755      * @return TreeNode The new selection
32756      */
32757     selectNext : function(){
32758         var s = this.selNode || this.lastSelNode;
32759         if(!s){
32760             return null;
32761         }
32762         if(s.firstChild && s.isExpanded()){
32763              return this.select(s.firstChild);
32764          }else if(s.nextSibling){
32765              return this.select(s.nextSibling);
32766          }else if(s.parentNode){
32767             var newS = null;
32768             s.parentNode.bubble(function(){
32769                 if(this.nextSibling){
32770                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32771                     return false;
32772                 }
32773             });
32774             return newS;
32775          }
32776         return null;
32777     },
32778
32779     onKeyDown : function(e){
32780         var s = this.selNode || this.lastSelNode;
32781         // undesirable, but required
32782         var sm = this;
32783         if(!s){
32784             return;
32785         }
32786         var k = e.getKey();
32787         switch(k){
32788              case e.DOWN:
32789                  e.stopEvent();
32790                  this.selectNext();
32791              break;
32792              case e.UP:
32793                  e.stopEvent();
32794                  this.selectPrevious();
32795              break;
32796              case e.RIGHT:
32797                  e.preventDefault();
32798                  if(s.hasChildNodes()){
32799                      if(!s.isExpanded()){
32800                          s.expand();
32801                      }else if(s.firstChild){
32802                          this.select(s.firstChild, e);
32803                      }
32804                  }
32805              break;
32806              case e.LEFT:
32807                  e.preventDefault();
32808                  if(s.hasChildNodes() && s.isExpanded()){
32809                      s.collapse();
32810                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32811                      this.select(s.parentNode, e);
32812                  }
32813              break;
32814         };
32815     }
32816 });
32817
32818 /**
32819  * @class Roo.tree.MultiSelectionModel
32820  * @extends Roo.util.Observable
32821  * Multi selection for a TreePanel.
32822  * @param {Object} cfg Configuration
32823  */
32824 Roo.tree.MultiSelectionModel = function(){
32825    this.selNodes = [];
32826    this.selMap = {};
32827    this.addEvents({
32828        /**
32829         * @event selectionchange
32830         * Fires when the selected nodes change
32831         * @param {MultiSelectionModel} this
32832         * @param {Array} nodes Array of the selected nodes
32833         */
32834        "selectionchange" : true
32835    });
32836    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32837    
32838 };
32839
32840 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32841     init : function(tree){
32842         this.tree = tree;
32843         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32844         tree.on("click", this.onNodeClick, this);
32845     },
32846     
32847     onNodeClick : function(node, e){
32848         this.select(node, e, e.ctrlKey);
32849     },
32850     
32851     /**
32852      * Select a node.
32853      * @param {TreeNode} node The node to select
32854      * @param {EventObject} e (optional) An event associated with the selection
32855      * @param {Boolean} keepExisting True to retain existing selections
32856      * @return {TreeNode} The selected node
32857      */
32858     select : function(node, e, keepExisting){
32859         if(keepExisting !== true){
32860             this.clearSelections(true);
32861         }
32862         if(this.isSelected(node)){
32863             this.lastSelNode = node;
32864             return node;
32865         }
32866         this.selNodes.push(node);
32867         this.selMap[node.id] = node;
32868         this.lastSelNode = node;
32869         node.ui.onSelectedChange(true);
32870         this.fireEvent("selectionchange", this, this.selNodes);
32871         return node;
32872     },
32873     
32874     /**
32875      * Deselect a node.
32876      * @param {TreeNode} node The node to unselect
32877      */
32878     unselect : function(node){
32879         if(this.selMap[node.id]){
32880             node.ui.onSelectedChange(false);
32881             var sn = this.selNodes;
32882             var index = -1;
32883             if(sn.indexOf){
32884                 index = sn.indexOf(node);
32885             }else{
32886                 for(var i = 0, len = sn.length; i < len; i++){
32887                     if(sn[i] == node){
32888                         index = i;
32889                         break;
32890                     }
32891                 }
32892             }
32893             if(index != -1){
32894                 this.selNodes.splice(index, 1);
32895             }
32896             delete this.selMap[node.id];
32897             this.fireEvent("selectionchange", this, this.selNodes);
32898         }
32899     },
32900     
32901     /**
32902      * Clear all selections
32903      */
32904     clearSelections : function(suppressEvent){
32905         var sn = this.selNodes;
32906         if(sn.length > 0){
32907             for(var i = 0, len = sn.length; i < len; i++){
32908                 sn[i].ui.onSelectedChange(false);
32909             }
32910             this.selNodes = [];
32911             this.selMap = {};
32912             if(suppressEvent !== true){
32913                 this.fireEvent("selectionchange", this, this.selNodes);
32914             }
32915         }
32916     },
32917     
32918     /**
32919      * Returns true if the node is selected
32920      * @param {TreeNode} node The node to check
32921      * @return {Boolean}
32922      */
32923     isSelected : function(node){
32924         return this.selMap[node.id] ? true : false;  
32925     },
32926     
32927     /**
32928      * Returns an array of the selected nodes
32929      * @return {Array}
32930      */
32931     getSelectedNodes : function(){
32932         return this.selNodes;    
32933     },
32934
32935     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32936
32937     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32938
32939     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32940 });/*
32941  * Based on:
32942  * Ext JS Library 1.1.1
32943  * Copyright(c) 2006-2007, Ext JS, LLC.
32944  *
32945  * Originally Released Under LGPL - original licence link has changed is not relivant.
32946  *
32947  * Fork - LGPL
32948  * <script type="text/javascript">
32949  */
32950  
32951 /**
32952  * @class Roo.tree.TreeNode
32953  * @extends Roo.data.Node
32954  * @cfg {String} text The text for this node
32955  * @cfg {Boolean} expanded true to start the node expanded
32956  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32957  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32958  * @cfg {Boolean} disabled true to start the node disabled
32959  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32960  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32961  * @cfg {String} cls A css class to be added to the node
32962  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32963  * @cfg {String} href URL of the link used for the node (defaults to #)
32964  * @cfg {String} hrefTarget target frame for the link
32965  * @cfg {String} qtip An Ext QuickTip for the node
32966  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32967  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32968  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32969  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32970  * (defaults to undefined with no checkbox rendered)
32971  * @constructor
32972  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32973  */
32974 Roo.tree.TreeNode = function(attributes){
32975     attributes = attributes || {};
32976     if(typeof attributes == "string"){
32977         attributes = {text: attributes};
32978     }
32979     this.childrenRendered = false;
32980     this.rendered = false;
32981     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32982     this.expanded = attributes.expanded === true;
32983     this.isTarget = attributes.isTarget !== false;
32984     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32985     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32986
32987     /**
32988      * Read-only. The text for this node. To change it use setText().
32989      * @type String
32990      */
32991     this.text = attributes.text;
32992     /**
32993      * True if this node is disabled.
32994      * @type Boolean
32995      */
32996     this.disabled = attributes.disabled === true;
32997
32998     this.addEvents({
32999         /**
33000         * @event textchange
33001         * Fires when the text for this node is changed
33002         * @param {Node} this This node
33003         * @param {String} text The new text
33004         * @param {String} oldText The old text
33005         */
33006         "textchange" : true,
33007         /**
33008         * @event beforeexpand
33009         * Fires before this node is expanded, return false to cancel.
33010         * @param {Node} this This node
33011         * @param {Boolean} deep
33012         * @param {Boolean} anim
33013         */
33014         "beforeexpand" : true,
33015         /**
33016         * @event beforecollapse
33017         * Fires before this node is collapsed, return false to cancel.
33018         * @param {Node} this This node
33019         * @param {Boolean} deep
33020         * @param {Boolean} anim
33021         */
33022         "beforecollapse" : true,
33023         /**
33024         * @event expand
33025         * Fires when this node is expanded
33026         * @param {Node} this This node
33027         */
33028         "expand" : true,
33029         /**
33030         * @event disabledchange
33031         * Fires when the disabled status of this node changes
33032         * @param {Node} this This node
33033         * @param {Boolean} disabled
33034         */
33035         "disabledchange" : true,
33036         /**
33037         * @event collapse
33038         * Fires when this node is collapsed
33039         * @param {Node} this This node
33040         */
33041         "collapse" : true,
33042         /**
33043         * @event beforeclick
33044         * Fires before click processing. Return false to cancel the default action.
33045         * @param {Node} this This node
33046         * @param {Roo.EventObject} e The event object
33047         */
33048         "beforeclick":true,
33049         /**
33050         * @event checkchange
33051         * Fires when a node with a checkbox's checked property changes
33052         * @param {Node} this This node
33053         * @param {Boolean} checked
33054         */
33055         "checkchange":true,
33056         /**
33057         * @event click
33058         * Fires when this node is clicked
33059         * @param {Node} this This node
33060         * @param {Roo.EventObject} e The event object
33061         */
33062         "click":true,
33063         /**
33064         * @event dblclick
33065         * Fires when this node is double clicked
33066         * @param {Node} this This node
33067         * @param {Roo.EventObject} e The event object
33068         */
33069         "dblclick":true,
33070         /**
33071         * @event contextmenu
33072         * Fires when this node is right clicked
33073         * @param {Node} this This node
33074         * @param {Roo.EventObject} e The event object
33075         */
33076         "contextmenu":true,
33077         /**
33078         * @event beforechildrenrendered
33079         * Fires right before the child nodes for this node are rendered
33080         * @param {Node} this This node
33081         */
33082         "beforechildrenrendered":true
33083     });
33084
33085     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33086
33087     /**
33088      * Read-only. The UI for this node
33089      * @type TreeNodeUI
33090      */
33091     this.ui = new uiClass(this);
33092     
33093     // finally support items[]
33094     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33095         return;
33096     }
33097     
33098     
33099     Roo.each(this.attributes.items, function(c) {
33100         this.appendChild(Roo.factory(c,Roo.Tree));
33101     }, this);
33102     delete this.attributes.items;
33103     
33104     
33105     
33106 };
33107 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33108     preventHScroll: true,
33109     /**
33110      * Returns true if this node is expanded
33111      * @return {Boolean}
33112      */
33113     isExpanded : function(){
33114         return this.expanded;
33115     },
33116
33117     /**
33118      * Returns the UI object for this node
33119      * @return {TreeNodeUI}
33120      */
33121     getUI : function(){
33122         return this.ui;
33123     },
33124
33125     // private override
33126     setFirstChild : function(node){
33127         var of = this.firstChild;
33128         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33129         if(this.childrenRendered && of && node != of){
33130             of.renderIndent(true, true);
33131         }
33132         if(this.rendered){
33133             this.renderIndent(true, true);
33134         }
33135     },
33136
33137     // private override
33138     setLastChild : function(node){
33139         var ol = this.lastChild;
33140         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33141         if(this.childrenRendered && ol && node != ol){
33142             ol.renderIndent(true, true);
33143         }
33144         if(this.rendered){
33145             this.renderIndent(true, true);
33146         }
33147     },
33148
33149     // these methods are overridden to provide lazy rendering support
33150     // private override
33151     appendChild : function()
33152     {
33153         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33154         if(node && this.childrenRendered){
33155             node.render();
33156         }
33157         this.ui.updateExpandIcon();
33158         return node;
33159     },
33160
33161     // private override
33162     removeChild : function(node){
33163         this.ownerTree.getSelectionModel().unselect(node);
33164         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33165         // if it's been rendered remove dom node
33166         if(this.childrenRendered){
33167             node.ui.remove();
33168         }
33169         if(this.childNodes.length < 1){
33170             this.collapse(false, false);
33171         }else{
33172             this.ui.updateExpandIcon();
33173         }
33174         if(!this.firstChild) {
33175             this.childrenRendered = false;
33176         }
33177         return node;
33178     },
33179
33180     // private override
33181     insertBefore : function(node, refNode){
33182         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33183         if(newNode && refNode && this.childrenRendered){
33184             node.render();
33185         }
33186         this.ui.updateExpandIcon();
33187         return newNode;
33188     },
33189
33190     /**
33191      * Sets the text for this node
33192      * @param {String} text
33193      */
33194     setText : function(text){
33195         var oldText = this.text;
33196         this.text = text;
33197         this.attributes.text = text;
33198         if(this.rendered){ // event without subscribing
33199             this.ui.onTextChange(this, text, oldText);
33200         }
33201         this.fireEvent("textchange", this, text, oldText);
33202     },
33203
33204     /**
33205      * Triggers selection of this node
33206      */
33207     select : function(){
33208         this.getOwnerTree().getSelectionModel().select(this);
33209     },
33210
33211     /**
33212      * Triggers deselection of this node
33213      */
33214     unselect : function(){
33215         this.getOwnerTree().getSelectionModel().unselect(this);
33216     },
33217
33218     /**
33219      * Returns true if this node is selected
33220      * @return {Boolean}
33221      */
33222     isSelected : function(){
33223         return this.getOwnerTree().getSelectionModel().isSelected(this);
33224     },
33225
33226     /**
33227      * Expand this node.
33228      * @param {Boolean} deep (optional) True to expand all children as well
33229      * @param {Boolean} anim (optional) false to cancel the default animation
33230      * @param {Function} callback (optional) A callback to be called when
33231      * expanding this node completes (does not wait for deep expand to complete).
33232      * Called with 1 parameter, this node.
33233      */
33234     expand : function(deep, anim, callback){
33235         if(!this.expanded){
33236             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33237                 return;
33238             }
33239             if(!this.childrenRendered){
33240                 this.renderChildren();
33241             }
33242             this.expanded = true;
33243             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33244                 this.ui.animExpand(function(){
33245                     this.fireEvent("expand", this);
33246                     if(typeof callback == "function"){
33247                         callback(this);
33248                     }
33249                     if(deep === true){
33250                         this.expandChildNodes(true);
33251                     }
33252                 }.createDelegate(this));
33253                 return;
33254             }else{
33255                 this.ui.expand();
33256                 this.fireEvent("expand", this);
33257                 if(typeof callback == "function"){
33258                     callback(this);
33259                 }
33260             }
33261         }else{
33262            if(typeof callback == "function"){
33263                callback(this);
33264            }
33265         }
33266         if(deep === true){
33267             this.expandChildNodes(true);
33268         }
33269     },
33270
33271     isHiddenRoot : function(){
33272         return this.isRoot && !this.getOwnerTree().rootVisible;
33273     },
33274
33275     /**
33276      * Collapse this node.
33277      * @param {Boolean} deep (optional) True to collapse all children as well
33278      * @param {Boolean} anim (optional) false to cancel the default animation
33279      */
33280     collapse : function(deep, anim){
33281         if(this.expanded && !this.isHiddenRoot()){
33282             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33283                 return;
33284             }
33285             this.expanded = false;
33286             if((this.getOwnerTree().animate && anim !== false) || anim){
33287                 this.ui.animCollapse(function(){
33288                     this.fireEvent("collapse", this);
33289                     if(deep === true){
33290                         this.collapseChildNodes(true);
33291                     }
33292                 }.createDelegate(this));
33293                 return;
33294             }else{
33295                 this.ui.collapse();
33296                 this.fireEvent("collapse", this);
33297             }
33298         }
33299         if(deep === true){
33300             var cs = this.childNodes;
33301             for(var i = 0, len = cs.length; i < len; i++) {
33302                 cs[i].collapse(true, false);
33303             }
33304         }
33305     },
33306
33307     // private
33308     delayedExpand : function(delay){
33309         if(!this.expandProcId){
33310             this.expandProcId = this.expand.defer(delay, this);
33311         }
33312     },
33313
33314     // private
33315     cancelExpand : function(){
33316         if(this.expandProcId){
33317             clearTimeout(this.expandProcId);
33318         }
33319         this.expandProcId = false;
33320     },
33321
33322     /**
33323      * Toggles expanded/collapsed state of the node
33324      */
33325     toggle : function(){
33326         if(this.expanded){
33327             this.collapse();
33328         }else{
33329             this.expand();
33330         }
33331     },
33332
33333     /**
33334      * Ensures all parent nodes are expanded
33335      */
33336     ensureVisible : function(callback){
33337         var tree = this.getOwnerTree();
33338         tree.expandPath(this.parentNode.getPath(), false, function(){
33339             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33340             Roo.callback(callback);
33341         }.createDelegate(this));
33342     },
33343
33344     /**
33345      * Expand all child nodes
33346      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33347      */
33348     expandChildNodes : function(deep){
33349         var cs = this.childNodes;
33350         for(var i = 0, len = cs.length; i < len; i++) {
33351                 cs[i].expand(deep);
33352         }
33353     },
33354
33355     /**
33356      * Collapse all child nodes
33357      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33358      */
33359     collapseChildNodes : function(deep){
33360         var cs = this.childNodes;
33361         for(var i = 0, len = cs.length; i < len; i++) {
33362                 cs[i].collapse(deep);
33363         }
33364     },
33365
33366     /**
33367      * Disables this node
33368      */
33369     disable : function(){
33370         this.disabled = true;
33371         this.unselect();
33372         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33373             this.ui.onDisableChange(this, true);
33374         }
33375         this.fireEvent("disabledchange", this, true);
33376     },
33377
33378     /**
33379      * Enables this node
33380      */
33381     enable : function(){
33382         this.disabled = false;
33383         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33384             this.ui.onDisableChange(this, false);
33385         }
33386         this.fireEvent("disabledchange", this, false);
33387     },
33388
33389     // private
33390     renderChildren : function(suppressEvent){
33391         if(suppressEvent !== false){
33392             this.fireEvent("beforechildrenrendered", this);
33393         }
33394         var cs = this.childNodes;
33395         for(var i = 0, len = cs.length; i < len; i++){
33396             cs[i].render(true);
33397         }
33398         this.childrenRendered = true;
33399     },
33400
33401     // private
33402     sort : function(fn, scope){
33403         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33404         if(this.childrenRendered){
33405             var cs = this.childNodes;
33406             for(var i = 0, len = cs.length; i < len; i++){
33407                 cs[i].render(true);
33408             }
33409         }
33410     },
33411
33412     // private
33413     render : function(bulkRender){
33414         this.ui.render(bulkRender);
33415         if(!this.rendered){
33416             this.rendered = true;
33417             if(this.expanded){
33418                 this.expanded = false;
33419                 this.expand(false, false);
33420             }
33421         }
33422     },
33423
33424     // private
33425     renderIndent : function(deep, refresh){
33426         if(refresh){
33427             this.ui.childIndent = null;
33428         }
33429         this.ui.renderIndent();
33430         if(deep === true && this.childrenRendered){
33431             var cs = this.childNodes;
33432             for(var i = 0, len = cs.length; i < len; i++){
33433                 cs[i].renderIndent(true, refresh);
33434             }
33435         }
33436     }
33437 });/*
33438  * Based on:
33439  * Ext JS Library 1.1.1
33440  * Copyright(c) 2006-2007, Ext JS, LLC.
33441  *
33442  * Originally Released Under LGPL - original licence link has changed is not relivant.
33443  *
33444  * Fork - LGPL
33445  * <script type="text/javascript">
33446  */
33447  
33448 /**
33449  * @class Roo.tree.AsyncTreeNode
33450  * @extends Roo.tree.TreeNode
33451  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33452  * @constructor
33453  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33454  */
33455  Roo.tree.AsyncTreeNode = function(config){
33456     this.loaded = false;
33457     this.loading = false;
33458     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33459     /**
33460     * @event beforeload
33461     * Fires before this node is loaded, return false to cancel
33462     * @param {Node} this This node
33463     */
33464     this.addEvents({'beforeload':true, 'load': true});
33465     /**
33466     * @event load
33467     * Fires when this node is loaded
33468     * @param {Node} this This node
33469     */
33470     /**
33471      * The loader used by this node (defaults to using the tree's defined loader)
33472      * @type TreeLoader
33473      * @property loader
33474      */
33475 };
33476 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33477     expand : function(deep, anim, callback){
33478         if(this.loading){ // if an async load is already running, waiting til it's done
33479             var timer;
33480             var f = function(){
33481                 if(!this.loading){ // done loading
33482                     clearInterval(timer);
33483                     this.expand(deep, anim, callback);
33484                 }
33485             }.createDelegate(this);
33486             timer = setInterval(f, 200);
33487             return;
33488         }
33489         if(!this.loaded){
33490             if(this.fireEvent("beforeload", this) === false){
33491                 return;
33492             }
33493             this.loading = true;
33494             this.ui.beforeLoad(this);
33495             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33496             if(loader){
33497                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33498                 return;
33499             }
33500         }
33501         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33502     },
33503     
33504     /**
33505      * Returns true if this node is currently loading
33506      * @return {Boolean}
33507      */
33508     isLoading : function(){
33509         return this.loading;  
33510     },
33511     
33512     loadComplete : function(deep, anim, callback){
33513         this.loading = false;
33514         this.loaded = true;
33515         this.ui.afterLoad(this);
33516         this.fireEvent("load", this);
33517         this.expand(deep, anim, callback);
33518     },
33519     
33520     /**
33521      * Returns true if this node has been loaded
33522      * @return {Boolean}
33523      */
33524     isLoaded : function(){
33525         return this.loaded;
33526     },
33527     
33528     hasChildNodes : function(){
33529         if(!this.isLeaf() && !this.loaded){
33530             return true;
33531         }else{
33532             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33533         }
33534     },
33535
33536     /**
33537      * Trigger a reload for this node
33538      * @param {Function} callback
33539      */
33540     reload : function(callback){
33541         this.collapse(false, false);
33542         while(this.firstChild){
33543             this.removeChild(this.firstChild);
33544         }
33545         this.childrenRendered = false;
33546         this.loaded = false;
33547         if(this.isHiddenRoot()){
33548             this.expanded = false;
33549         }
33550         this.expand(false, false, callback);
33551     }
33552 });/*
33553  * Based on:
33554  * Ext JS Library 1.1.1
33555  * Copyright(c) 2006-2007, Ext JS, LLC.
33556  *
33557  * Originally Released Under LGPL - original licence link has changed is not relivant.
33558  *
33559  * Fork - LGPL
33560  * <script type="text/javascript">
33561  */
33562  
33563 /**
33564  * @class Roo.tree.TreeNodeUI
33565  * @constructor
33566  * @param {Object} node The node to render
33567  * The TreeNode UI implementation is separate from the
33568  * tree implementation. Unless you are customizing the tree UI,
33569  * you should never have to use this directly.
33570  */
33571 Roo.tree.TreeNodeUI = function(node){
33572     this.node = node;
33573     this.rendered = false;
33574     this.animating = false;
33575     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33576 };
33577
33578 Roo.tree.TreeNodeUI.prototype = {
33579     removeChild : function(node){
33580         if(this.rendered){
33581             this.ctNode.removeChild(node.ui.getEl());
33582         }
33583     },
33584
33585     beforeLoad : function(){
33586          this.addClass("x-tree-node-loading");
33587     },
33588
33589     afterLoad : function(){
33590          this.removeClass("x-tree-node-loading");
33591     },
33592
33593     onTextChange : function(node, text, oldText){
33594         if(this.rendered){
33595             this.textNode.innerHTML = text;
33596         }
33597     },
33598
33599     onDisableChange : function(node, state){
33600         this.disabled = state;
33601         if(state){
33602             this.addClass("x-tree-node-disabled");
33603         }else{
33604             this.removeClass("x-tree-node-disabled");
33605         }
33606     },
33607
33608     onSelectedChange : function(state){
33609         if(state){
33610             this.focus();
33611             this.addClass("x-tree-selected");
33612         }else{
33613             //this.blur();
33614             this.removeClass("x-tree-selected");
33615         }
33616     },
33617
33618     onMove : function(tree, node, oldParent, newParent, index, refNode){
33619         this.childIndent = null;
33620         if(this.rendered){
33621             var targetNode = newParent.ui.getContainer();
33622             if(!targetNode){//target not rendered
33623                 this.holder = document.createElement("div");
33624                 this.holder.appendChild(this.wrap);
33625                 return;
33626             }
33627             var insertBefore = refNode ? refNode.ui.getEl() : null;
33628             if(insertBefore){
33629                 targetNode.insertBefore(this.wrap, insertBefore);
33630             }else{
33631                 targetNode.appendChild(this.wrap);
33632             }
33633             this.node.renderIndent(true);
33634         }
33635     },
33636
33637     addClass : function(cls){
33638         if(this.elNode){
33639             Roo.fly(this.elNode).addClass(cls);
33640         }
33641     },
33642
33643     removeClass : function(cls){
33644         if(this.elNode){
33645             Roo.fly(this.elNode).removeClass(cls);
33646         }
33647     },
33648
33649     remove : function(){
33650         if(this.rendered){
33651             this.holder = document.createElement("div");
33652             this.holder.appendChild(this.wrap);
33653         }
33654     },
33655
33656     fireEvent : function(){
33657         return this.node.fireEvent.apply(this.node, arguments);
33658     },
33659
33660     initEvents : function(){
33661         this.node.on("move", this.onMove, this);
33662         var E = Roo.EventManager;
33663         var a = this.anchor;
33664
33665         var el = Roo.fly(a, '_treeui');
33666
33667         if(Roo.isOpera){ // opera render bug ignores the CSS
33668             el.setStyle("text-decoration", "none");
33669         }
33670
33671         el.on("click", this.onClick, this);
33672         el.on("dblclick", this.onDblClick, this);
33673
33674         if(this.checkbox){
33675             Roo.EventManager.on(this.checkbox,
33676                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33677         }
33678
33679         el.on("contextmenu", this.onContextMenu, this);
33680
33681         var icon = Roo.fly(this.iconNode);
33682         icon.on("click", this.onClick, this);
33683         icon.on("dblclick", this.onDblClick, this);
33684         icon.on("contextmenu", this.onContextMenu, this);
33685         E.on(this.ecNode, "click", this.ecClick, this, true);
33686
33687         if(this.node.disabled){
33688             this.addClass("x-tree-node-disabled");
33689         }
33690         if(this.node.hidden){
33691             this.addClass("x-tree-node-disabled");
33692         }
33693         var ot = this.node.getOwnerTree();
33694         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33695         if(dd && (!this.node.isRoot || ot.rootVisible)){
33696             Roo.dd.Registry.register(this.elNode, {
33697                 node: this.node,
33698                 handles: this.getDDHandles(),
33699                 isHandle: false
33700             });
33701         }
33702     },
33703
33704     getDDHandles : function(){
33705         return [this.iconNode, this.textNode];
33706     },
33707
33708     hide : function(){
33709         if(this.rendered){
33710             this.wrap.style.display = "none";
33711         }
33712     },
33713
33714     show : function(){
33715         if(this.rendered){
33716             this.wrap.style.display = "";
33717         }
33718     },
33719
33720     onContextMenu : function(e){
33721         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33722             e.preventDefault();
33723             this.focus();
33724             this.fireEvent("contextmenu", this.node, e);
33725         }
33726     },
33727
33728     onClick : function(e){
33729         if(this.dropping){
33730             e.stopEvent();
33731             return;
33732         }
33733         if(this.fireEvent("beforeclick", this.node, e) !== false){
33734             if(!this.disabled && this.node.attributes.href){
33735                 this.fireEvent("click", this.node, e);
33736                 return;
33737             }
33738             e.preventDefault();
33739             if(this.disabled){
33740                 return;
33741             }
33742
33743             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33744                 this.node.toggle();
33745             }
33746
33747             this.fireEvent("click", this.node, e);
33748         }else{
33749             e.stopEvent();
33750         }
33751     },
33752
33753     onDblClick : function(e){
33754         e.preventDefault();
33755         if(this.disabled){
33756             return;
33757         }
33758         if(this.checkbox){
33759             this.toggleCheck();
33760         }
33761         if(!this.animating && this.node.hasChildNodes()){
33762             this.node.toggle();
33763         }
33764         this.fireEvent("dblclick", this.node, e);
33765     },
33766
33767     onCheckChange : function(){
33768         var checked = this.checkbox.checked;
33769         this.node.attributes.checked = checked;
33770         this.fireEvent('checkchange', this.node, checked);
33771     },
33772
33773     ecClick : function(e){
33774         if(!this.animating && this.node.hasChildNodes()){
33775             this.node.toggle();
33776         }
33777     },
33778
33779     startDrop : function(){
33780         this.dropping = true;
33781     },
33782
33783     // delayed drop so the click event doesn't get fired on a drop
33784     endDrop : function(){
33785        setTimeout(function(){
33786            this.dropping = false;
33787        }.createDelegate(this), 50);
33788     },
33789
33790     expand : function(){
33791         this.updateExpandIcon();
33792         this.ctNode.style.display = "";
33793     },
33794
33795     focus : function(){
33796         if(!this.node.preventHScroll){
33797             try{this.anchor.focus();
33798             }catch(e){}
33799         }else if(!Roo.isIE){
33800             try{
33801                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33802                 var l = noscroll.scrollLeft;
33803                 this.anchor.focus();
33804                 noscroll.scrollLeft = l;
33805             }catch(e){}
33806         }
33807     },
33808
33809     toggleCheck : function(value){
33810         var cb = this.checkbox;
33811         if(cb){
33812             cb.checked = (value === undefined ? !cb.checked : value);
33813         }
33814     },
33815
33816     blur : function(){
33817         try{
33818             this.anchor.blur();
33819         }catch(e){}
33820     },
33821
33822     animExpand : function(callback){
33823         var ct = Roo.get(this.ctNode);
33824         ct.stopFx();
33825         if(!this.node.hasChildNodes()){
33826             this.updateExpandIcon();
33827             this.ctNode.style.display = "";
33828             Roo.callback(callback);
33829             return;
33830         }
33831         this.animating = true;
33832         this.updateExpandIcon();
33833
33834         ct.slideIn('t', {
33835            callback : function(){
33836                this.animating = false;
33837                Roo.callback(callback);
33838             },
33839             scope: this,
33840             duration: this.node.ownerTree.duration || .25
33841         });
33842     },
33843
33844     highlight : function(){
33845         var tree = this.node.getOwnerTree();
33846         Roo.fly(this.wrap).highlight(
33847             tree.hlColor || "C3DAF9",
33848             {endColor: tree.hlBaseColor}
33849         );
33850     },
33851
33852     collapse : function(){
33853         this.updateExpandIcon();
33854         this.ctNode.style.display = "none";
33855     },
33856
33857     animCollapse : function(callback){
33858         var ct = Roo.get(this.ctNode);
33859         ct.enableDisplayMode('block');
33860         ct.stopFx();
33861
33862         this.animating = true;
33863         this.updateExpandIcon();
33864
33865         ct.slideOut('t', {
33866             callback : function(){
33867                this.animating = false;
33868                Roo.callback(callback);
33869             },
33870             scope: this,
33871             duration: this.node.ownerTree.duration || .25
33872         });
33873     },
33874
33875     getContainer : function(){
33876         return this.ctNode;
33877     },
33878
33879     getEl : function(){
33880         return this.wrap;
33881     },
33882
33883     appendDDGhost : function(ghostNode){
33884         ghostNode.appendChild(this.elNode.cloneNode(true));
33885     },
33886
33887     getDDRepairXY : function(){
33888         return Roo.lib.Dom.getXY(this.iconNode);
33889     },
33890
33891     onRender : function(){
33892         this.render();
33893     },
33894
33895     render : function(bulkRender){
33896         var n = this.node, a = n.attributes;
33897         var targetNode = n.parentNode ?
33898               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33899
33900         if(!this.rendered){
33901             this.rendered = true;
33902
33903             this.renderElements(n, a, targetNode, bulkRender);
33904
33905             if(a.qtip){
33906                if(this.textNode.setAttributeNS){
33907                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33908                    if(a.qtipTitle){
33909                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33910                    }
33911                }else{
33912                    this.textNode.setAttribute("ext:qtip", a.qtip);
33913                    if(a.qtipTitle){
33914                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33915                    }
33916                }
33917             }else if(a.qtipCfg){
33918                 a.qtipCfg.target = Roo.id(this.textNode);
33919                 Roo.QuickTips.register(a.qtipCfg);
33920             }
33921             this.initEvents();
33922             if(!this.node.expanded){
33923                 this.updateExpandIcon();
33924             }
33925         }else{
33926             if(bulkRender === true) {
33927                 targetNode.appendChild(this.wrap);
33928             }
33929         }
33930     },
33931
33932     renderElements : function(n, a, targetNode, bulkRender)
33933     {
33934         // add some indent caching, this helps performance when rendering a large tree
33935         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33936         var t = n.getOwnerTree();
33937         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33938         if (typeof(n.attributes.html) != 'undefined') {
33939             txt = n.attributes.html;
33940         }
33941         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33942         var cb = typeof a.checked == 'boolean';
33943         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33944         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33945             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33946             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33947             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33948             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33949             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33950              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33951                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33952             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33953             "</li>"];
33954
33955         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33956             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33957                                 n.nextSibling.ui.getEl(), buf.join(""));
33958         }else{
33959             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33960         }
33961
33962         this.elNode = this.wrap.childNodes[0];
33963         this.ctNode = this.wrap.childNodes[1];
33964         var cs = this.elNode.childNodes;
33965         this.indentNode = cs[0];
33966         this.ecNode = cs[1];
33967         this.iconNode = cs[2];
33968         var index = 3;
33969         if(cb){
33970             this.checkbox = cs[3];
33971             index++;
33972         }
33973         this.anchor = cs[index];
33974         this.textNode = cs[index].firstChild;
33975     },
33976
33977     getAnchor : function(){
33978         return this.anchor;
33979     },
33980
33981     getTextEl : function(){
33982         return this.textNode;
33983     },
33984
33985     getIconEl : function(){
33986         return this.iconNode;
33987     },
33988
33989     isChecked : function(){
33990         return this.checkbox ? this.checkbox.checked : false;
33991     },
33992
33993     updateExpandIcon : function(){
33994         if(this.rendered){
33995             var n = this.node, c1, c2;
33996             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33997             var hasChild = n.hasChildNodes();
33998             if(hasChild){
33999                 if(n.expanded){
34000                     cls += "-minus";
34001                     c1 = "x-tree-node-collapsed";
34002                     c2 = "x-tree-node-expanded";
34003                 }else{
34004                     cls += "-plus";
34005                     c1 = "x-tree-node-expanded";
34006                     c2 = "x-tree-node-collapsed";
34007                 }
34008                 if(this.wasLeaf){
34009                     this.removeClass("x-tree-node-leaf");
34010                     this.wasLeaf = false;
34011                 }
34012                 if(this.c1 != c1 || this.c2 != c2){
34013                     Roo.fly(this.elNode).replaceClass(c1, c2);
34014                     this.c1 = c1; this.c2 = c2;
34015                 }
34016             }else{
34017                 // this changes non-leafs into leafs if they have no children.
34018                 // it's not very rational behaviour..
34019                 
34020                 if(!this.wasLeaf && this.node.leaf){
34021                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34022                     delete this.c1;
34023                     delete this.c2;
34024                     this.wasLeaf = true;
34025                 }
34026             }
34027             var ecc = "x-tree-ec-icon "+cls;
34028             if(this.ecc != ecc){
34029                 this.ecNode.className = ecc;
34030                 this.ecc = ecc;
34031             }
34032         }
34033     },
34034
34035     getChildIndent : function(){
34036         if(!this.childIndent){
34037             var buf = [];
34038             var p = this.node;
34039             while(p){
34040                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34041                     if(!p.isLast()) {
34042                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34043                     } else {
34044                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34045                     }
34046                 }
34047                 p = p.parentNode;
34048             }
34049             this.childIndent = buf.join("");
34050         }
34051         return this.childIndent;
34052     },
34053
34054     renderIndent : function(){
34055         if(this.rendered){
34056             var indent = "";
34057             var p = this.node.parentNode;
34058             if(p){
34059                 indent = p.ui.getChildIndent();
34060             }
34061             if(this.indentMarkup != indent){ // don't rerender if not required
34062                 this.indentNode.innerHTML = indent;
34063                 this.indentMarkup = indent;
34064             }
34065             this.updateExpandIcon();
34066         }
34067     }
34068 };
34069
34070 Roo.tree.RootTreeNodeUI = function(){
34071     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34072 };
34073 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34074     render : function(){
34075         if(!this.rendered){
34076             var targetNode = this.node.ownerTree.innerCt.dom;
34077             this.node.expanded = true;
34078             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34079             this.wrap = this.ctNode = targetNode.firstChild;
34080         }
34081     },
34082     collapse : function(){
34083     },
34084     expand : function(){
34085     }
34086 });/*
34087  * Based on:
34088  * Ext JS Library 1.1.1
34089  * Copyright(c) 2006-2007, Ext JS, LLC.
34090  *
34091  * Originally Released Under LGPL - original licence link has changed is not relivant.
34092  *
34093  * Fork - LGPL
34094  * <script type="text/javascript">
34095  */
34096 /**
34097  * @class Roo.tree.TreeLoader
34098  * @extends Roo.util.Observable
34099  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34100  * nodes from a specified URL. The response must be a javascript Array definition
34101  * who's elements are node definition objects. eg:
34102  * <pre><code>
34103 {  success : true,
34104    data :      [
34105    
34106     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34107     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34108     ]
34109 }
34110
34111
34112 </code></pre>
34113  * <br><br>
34114  * The old style respose with just an array is still supported, but not recommended.
34115  * <br><br>
34116  *
34117  * A server request is sent, and child nodes are loaded only when a node is expanded.
34118  * The loading node's id is passed to the server under the parameter name "node" to
34119  * enable the server to produce the correct child nodes.
34120  * <br><br>
34121  * To pass extra parameters, an event handler may be attached to the "beforeload"
34122  * event, and the parameters specified in the TreeLoader's baseParams property:
34123  * <pre><code>
34124     myTreeLoader.on("beforeload", function(treeLoader, node) {
34125         this.baseParams.category = node.attributes.category;
34126     }, this);
34127 </code></pre><
34128  * This would pass an HTTP parameter called "category" to the server containing
34129  * the value of the Node's "category" attribute.
34130  * @constructor
34131  * Creates a new Treeloader.
34132  * @param {Object} config A config object containing config properties.
34133  */
34134 Roo.tree.TreeLoader = function(config){
34135     this.baseParams = {};
34136     this.requestMethod = "POST";
34137     Roo.apply(this, config);
34138
34139     this.addEvents({
34140     
34141         /**
34142          * @event beforeload
34143          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34144          * @param {Object} This TreeLoader object.
34145          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34146          * @param {Object} callback The callback function specified in the {@link #load} call.
34147          */
34148         beforeload : true,
34149         /**
34150          * @event load
34151          * Fires when the node has been successfuly loaded.
34152          * @param {Object} This TreeLoader object.
34153          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34154          * @param {Object} response The response object containing the data from the server.
34155          */
34156         load : true,
34157         /**
34158          * @event loadexception
34159          * Fires if the network request failed.
34160          * @param {Object} This TreeLoader object.
34161          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34162          * @param {Object} response The response object containing the data from the server.
34163          */
34164         loadexception : true,
34165         /**
34166          * @event create
34167          * Fires before a node is created, enabling you to return custom Node types 
34168          * @param {Object} This TreeLoader object.
34169          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34170          */
34171         create : true
34172     });
34173
34174     Roo.tree.TreeLoader.superclass.constructor.call(this);
34175 };
34176
34177 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34178     /**
34179     * @cfg {String} dataUrl The URL from which to request a Json string which
34180     * specifies an array of node definition object representing the child nodes
34181     * to be loaded.
34182     */
34183     /**
34184     * @cfg {String} requestMethod either GET or POST
34185     * defaults to POST (due to BC)
34186     * to be loaded.
34187     */
34188     /**
34189     * @cfg {Object} baseParams (optional) An object containing properties which
34190     * specify HTTP parameters to be passed to each request for child nodes.
34191     */
34192     /**
34193     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34194     * created by this loader. If the attributes sent by the server have an attribute in this object,
34195     * they take priority.
34196     */
34197     /**
34198     * @cfg {Object} uiProviders (optional) An object containing properties which
34199     * 
34200     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34201     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34202     * <i>uiProvider</i> attribute of a returned child node is a string rather
34203     * than a reference to a TreeNodeUI implementation, this that string value
34204     * is used as a property name in the uiProviders object. You can define the provider named
34205     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34206     */
34207     uiProviders : {},
34208
34209     /**
34210     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34211     * child nodes before loading.
34212     */
34213     clearOnLoad : true,
34214
34215     /**
34216     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34217     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34218     * Grid query { data : [ .....] }
34219     */
34220     
34221     root : false,
34222      /**
34223     * @cfg {String} queryParam (optional) 
34224     * Name of the query as it will be passed on the querystring (defaults to 'node')
34225     * eg. the request will be ?node=[id]
34226     */
34227     
34228     
34229     queryParam: false,
34230     
34231     /**
34232      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34233      * This is called automatically when a node is expanded, but may be used to reload
34234      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34235      * @param {Roo.tree.TreeNode} node
34236      * @param {Function} callback
34237      */
34238     load : function(node, callback){
34239         if(this.clearOnLoad){
34240             while(node.firstChild){
34241                 node.removeChild(node.firstChild);
34242             }
34243         }
34244         if(node.attributes.children){ // preloaded json children
34245             var cs = node.attributes.children;
34246             for(var i = 0, len = cs.length; i < len; i++){
34247                 node.appendChild(this.createNode(cs[i]));
34248             }
34249             if(typeof callback == "function"){
34250                 callback();
34251             }
34252         }else if(this.dataUrl){
34253             this.requestData(node, callback);
34254         }
34255     },
34256
34257     getParams: function(node){
34258         var buf = [], bp = this.baseParams;
34259         for(var key in bp){
34260             if(typeof bp[key] != "function"){
34261                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34262             }
34263         }
34264         var n = this.queryParam === false ? 'node' : this.queryParam;
34265         buf.push(n + "=", encodeURIComponent(node.id));
34266         return buf.join("");
34267     },
34268
34269     requestData : function(node, callback){
34270         if(this.fireEvent("beforeload", this, node, callback) !== false){
34271             this.transId = Roo.Ajax.request({
34272                 method:this.requestMethod,
34273                 url: this.dataUrl||this.url,
34274                 success: this.handleResponse,
34275                 failure: this.handleFailure,
34276                 scope: this,
34277                 argument: {callback: callback, node: node},
34278                 params: this.getParams(node)
34279             });
34280         }else{
34281             // if the load is cancelled, make sure we notify
34282             // the node that we are done
34283             if(typeof callback == "function"){
34284                 callback();
34285             }
34286         }
34287     },
34288
34289     isLoading : function(){
34290         return this.transId ? true : false;
34291     },
34292
34293     abort : function(){
34294         if(this.isLoading()){
34295             Roo.Ajax.abort(this.transId);
34296         }
34297     },
34298
34299     // private
34300     createNode : function(attr)
34301     {
34302         // apply baseAttrs, nice idea Corey!
34303         if(this.baseAttrs){
34304             Roo.applyIf(attr, this.baseAttrs);
34305         }
34306         if(this.applyLoader !== false){
34307             attr.loader = this;
34308         }
34309         // uiProvider = depreciated..
34310         
34311         if(typeof(attr.uiProvider) == 'string'){
34312            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34313                 /**  eval:var:attr */ eval(attr.uiProvider);
34314         }
34315         if(typeof(this.uiProviders['default']) != 'undefined') {
34316             attr.uiProvider = this.uiProviders['default'];
34317         }
34318         
34319         this.fireEvent('create', this, attr);
34320         
34321         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34322         return(attr.leaf ?
34323                         new Roo.tree.TreeNode(attr) :
34324                         new Roo.tree.AsyncTreeNode(attr));
34325     },
34326
34327     processResponse : function(response, node, callback)
34328     {
34329         var json = response.responseText;
34330         try {
34331             
34332             var o = Roo.decode(json);
34333             
34334             if (this.root === false && typeof(o.success) != undefined) {
34335                 this.root = 'data'; // the default behaviour for list like data..
34336                 }
34337                 
34338             if (this.root !== false &&  !o.success) {
34339                 // it's a failure condition.
34340                 var a = response.argument;
34341                 this.fireEvent("loadexception", this, a.node, response);
34342                 Roo.log("Load failed - should have a handler really");
34343                 return;
34344             }
34345             
34346             
34347             
34348             if (this.root !== false) {
34349                  o = o[this.root];
34350             }
34351             
34352             for(var i = 0, len = o.length; i < len; i++){
34353                 var n = this.createNode(o[i]);
34354                 if(n){
34355                     node.appendChild(n);
34356                 }
34357             }
34358             if(typeof callback == "function"){
34359                 callback(this, node);
34360             }
34361         }catch(e){
34362             this.handleFailure(response);
34363         }
34364     },
34365
34366     handleResponse : function(response){
34367         this.transId = false;
34368         var a = response.argument;
34369         this.processResponse(response, a.node, a.callback);
34370         this.fireEvent("load", this, a.node, response);
34371     },
34372
34373     handleFailure : function(response)
34374     {
34375         // should handle failure better..
34376         this.transId = false;
34377         var a = response.argument;
34378         this.fireEvent("loadexception", this, a.node, response);
34379         if(typeof a.callback == "function"){
34380             a.callback(this, a.node);
34381         }
34382     }
34383 });/*
34384  * Based on:
34385  * Ext JS Library 1.1.1
34386  * Copyright(c) 2006-2007, Ext JS, LLC.
34387  *
34388  * Originally Released Under LGPL - original licence link has changed is not relivant.
34389  *
34390  * Fork - LGPL
34391  * <script type="text/javascript">
34392  */
34393
34394 /**
34395 * @class Roo.tree.TreeFilter
34396 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34397 * @param {TreePanel} tree
34398 * @param {Object} config (optional)
34399  */
34400 Roo.tree.TreeFilter = function(tree, config){
34401     this.tree = tree;
34402     this.filtered = {};
34403     Roo.apply(this, config);
34404 };
34405
34406 Roo.tree.TreeFilter.prototype = {
34407     clearBlank:false,
34408     reverse:false,
34409     autoClear:false,
34410     remove:false,
34411
34412      /**
34413      * Filter the data by a specific attribute.
34414      * @param {String/RegExp} value Either string that the attribute value
34415      * should start with or a RegExp to test against the attribute
34416      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34417      * @param {TreeNode} startNode (optional) The node to start the filter at.
34418      */
34419     filter : function(value, attr, startNode){
34420         attr = attr || "text";
34421         var f;
34422         if(typeof value == "string"){
34423             var vlen = value.length;
34424             // auto clear empty filter
34425             if(vlen == 0 && this.clearBlank){
34426                 this.clear();
34427                 return;
34428             }
34429             value = value.toLowerCase();
34430             f = function(n){
34431                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34432             };
34433         }else if(value.exec){ // regex?
34434             f = function(n){
34435                 return value.test(n.attributes[attr]);
34436             };
34437         }else{
34438             throw 'Illegal filter type, must be string or regex';
34439         }
34440         this.filterBy(f, null, startNode);
34441         },
34442
34443     /**
34444      * Filter by a function. The passed function will be called with each
34445      * node in the tree (or from the startNode). If the function returns true, the node is kept
34446      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34447      * @param {Function} fn The filter function
34448      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34449      */
34450     filterBy : function(fn, scope, startNode){
34451         startNode = startNode || this.tree.root;
34452         if(this.autoClear){
34453             this.clear();
34454         }
34455         var af = this.filtered, rv = this.reverse;
34456         var f = function(n){
34457             if(n == startNode){
34458                 return true;
34459             }
34460             if(af[n.id]){
34461                 return false;
34462             }
34463             var m = fn.call(scope || n, n);
34464             if(!m || rv){
34465                 af[n.id] = n;
34466                 n.ui.hide();
34467                 return false;
34468             }
34469             return true;
34470         };
34471         startNode.cascade(f);
34472         if(this.remove){
34473            for(var id in af){
34474                if(typeof id != "function"){
34475                    var n = af[id];
34476                    if(n && n.parentNode){
34477                        n.parentNode.removeChild(n);
34478                    }
34479                }
34480            }
34481         }
34482     },
34483
34484     /**
34485      * Clears the current filter. Note: with the "remove" option
34486      * set a filter cannot be cleared.
34487      */
34488     clear : function(){
34489         var t = this.tree;
34490         var af = this.filtered;
34491         for(var id in af){
34492             if(typeof id != "function"){
34493                 var n = af[id];
34494                 if(n){
34495                     n.ui.show();
34496                 }
34497             }
34498         }
34499         this.filtered = {};
34500     }
34501 };
34502 /*
34503  * Based on:
34504  * Ext JS Library 1.1.1
34505  * Copyright(c) 2006-2007, Ext JS, LLC.
34506  *
34507  * Originally Released Under LGPL - original licence link has changed is not relivant.
34508  *
34509  * Fork - LGPL
34510  * <script type="text/javascript">
34511  */
34512  
34513
34514 /**
34515  * @class Roo.tree.TreeSorter
34516  * Provides sorting of nodes in a TreePanel
34517  * 
34518  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34519  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34520  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34521  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34522  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34523  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34524  * @constructor
34525  * @param {TreePanel} tree
34526  * @param {Object} config
34527  */
34528 Roo.tree.TreeSorter = function(tree, config){
34529     Roo.apply(this, config);
34530     tree.on("beforechildrenrendered", this.doSort, this);
34531     tree.on("append", this.updateSort, this);
34532     tree.on("insert", this.updateSort, this);
34533     
34534     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34535     var p = this.property || "text";
34536     var sortType = this.sortType;
34537     var fs = this.folderSort;
34538     var cs = this.caseSensitive === true;
34539     var leafAttr = this.leafAttr || 'leaf';
34540
34541     this.sortFn = function(n1, n2){
34542         if(fs){
34543             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34544                 return 1;
34545             }
34546             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34547                 return -1;
34548             }
34549         }
34550         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34551         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34552         if(v1 < v2){
34553                         return dsc ? +1 : -1;
34554                 }else if(v1 > v2){
34555                         return dsc ? -1 : +1;
34556         }else{
34557                 return 0;
34558         }
34559     };
34560 };
34561
34562 Roo.tree.TreeSorter.prototype = {
34563     doSort : function(node){
34564         node.sort(this.sortFn);
34565     },
34566     
34567     compareNodes : function(n1, n2){
34568         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34569     },
34570     
34571     updateSort : function(tree, node){
34572         if(node.childrenRendered){
34573             this.doSort.defer(1, this, [node]);
34574         }
34575     }
34576 };/*
34577  * Based on:
34578  * Ext JS Library 1.1.1
34579  * Copyright(c) 2006-2007, Ext JS, LLC.
34580  *
34581  * Originally Released Under LGPL - original licence link has changed is not relivant.
34582  *
34583  * Fork - LGPL
34584  * <script type="text/javascript">
34585  */
34586
34587 if(Roo.dd.DropZone){
34588     
34589 Roo.tree.TreeDropZone = function(tree, config){
34590     this.allowParentInsert = false;
34591     this.allowContainerDrop = false;
34592     this.appendOnly = false;
34593     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34594     this.tree = tree;
34595     this.lastInsertClass = "x-tree-no-status";
34596     this.dragOverData = {};
34597 };
34598
34599 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34600     ddGroup : "TreeDD",
34601     scroll:  true,
34602     
34603     expandDelay : 1000,
34604     
34605     expandNode : function(node){
34606         if(node.hasChildNodes() && !node.isExpanded()){
34607             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34608         }
34609     },
34610     
34611     queueExpand : function(node){
34612         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34613     },
34614     
34615     cancelExpand : function(){
34616         if(this.expandProcId){
34617             clearTimeout(this.expandProcId);
34618             this.expandProcId = false;
34619         }
34620     },
34621     
34622     isValidDropPoint : function(n, pt, dd, e, data){
34623         if(!n || !data){ return false; }
34624         var targetNode = n.node;
34625         var dropNode = data.node;
34626         // default drop rules
34627         if(!(targetNode && targetNode.isTarget && pt)){
34628             return false;
34629         }
34630         if(pt == "append" && targetNode.allowChildren === false){
34631             return false;
34632         }
34633         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34634             return false;
34635         }
34636         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34637             return false;
34638         }
34639         // reuse the object
34640         var overEvent = this.dragOverData;
34641         overEvent.tree = this.tree;
34642         overEvent.target = targetNode;
34643         overEvent.data = data;
34644         overEvent.point = pt;
34645         overEvent.source = dd;
34646         overEvent.rawEvent = e;
34647         overEvent.dropNode = dropNode;
34648         overEvent.cancel = false;  
34649         var result = this.tree.fireEvent("nodedragover", overEvent);
34650         return overEvent.cancel === false && result !== false;
34651     },
34652     
34653     getDropPoint : function(e, n, dd)
34654     {
34655         var tn = n.node;
34656         if(tn.isRoot){
34657             return tn.allowChildren !== false ? "append" : false; // always append for root
34658         }
34659         var dragEl = n.ddel;
34660         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34661         var y = Roo.lib.Event.getPageY(e);
34662         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34663         
34664         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34665         var noAppend = tn.allowChildren === false;
34666         if(this.appendOnly || tn.parentNode.allowChildren === false){
34667             return noAppend ? false : "append";
34668         }
34669         var noBelow = false;
34670         if(!this.allowParentInsert){
34671             noBelow = tn.hasChildNodes() && tn.isExpanded();
34672         }
34673         var q = (b - t) / (noAppend ? 2 : 3);
34674         if(y >= t && y < (t + q)){
34675             return "above";
34676         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34677             return "below";
34678         }else{
34679             return "append";
34680         }
34681     },
34682     
34683     onNodeEnter : function(n, dd, e, data)
34684     {
34685         this.cancelExpand();
34686     },
34687     
34688     onNodeOver : function(n, dd, e, data)
34689     {
34690        
34691         var pt = this.getDropPoint(e, n, dd);
34692         var node = n.node;
34693         
34694         // auto node expand check
34695         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34696             this.queueExpand(node);
34697         }else if(pt != "append"){
34698             this.cancelExpand();
34699         }
34700         
34701         // set the insert point style on the target node
34702         var returnCls = this.dropNotAllowed;
34703         if(this.isValidDropPoint(n, pt, dd, e, data)){
34704            if(pt){
34705                var el = n.ddel;
34706                var cls;
34707                if(pt == "above"){
34708                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34709                    cls = "x-tree-drag-insert-above";
34710                }else if(pt == "below"){
34711                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34712                    cls = "x-tree-drag-insert-below";
34713                }else{
34714                    returnCls = "x-tree-drop-ok-append";
34715                    cls = "x-tree-drag-append";
34716                }
34717                if(this.lastInsertClass != cls){
34718                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34719                    this.lastInsertClass = cls;
34720                }
34721            }
34722        }
34723        return returnCls;
34724     },
34725     
34726     onNodeOut : function(n, dd, e, data){
34727         
34728         this.cancelExpand();
34729         this.removeDropIndicators(n);
34730     },
34731     
34732     onNodeDrop : function(n, dd, e, data){
34733         var point = this.getDropPoint(e, n, dd);
34734         var targetNode = n.node;
34735         targetNode.ui.startDrop();
34736         if(!this.isValidDropPoint(n, point, dd, e, data)){
34737             targetNode.ui.endDrop();
34738             return false;
34739         }
34740         // first try to find the drop node
34741         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34742         var dropEvent = {
34743             tree : this.tree,
34744             target: targetNode,
34745             data: data,
34746             point: point,
34747             source: dd,
34748             rawEvent: e,
34749             dropNode: dropNode,
34750             cancel: !dropNode   
34751         };
34752         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34753         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34754             targetNode.ui.endDrop();
34755             return false;
34756         }
34757         // allow target changing
34758         targetNode = dropEvent.target;
34759         if(point == "append" && !targetNode.isExpanded()){
34760             targetNode.expand(false, null, function(){
34761                 this.completeDrop(dropEvent);
34762             }.createDelegate(this));
34763         }else{
34764             this.completeDrop(dropEvent);
34765         }
34766         return true;
34767     },
34768     
34769     completeDrop : function(de){
34770         var ns = de.dropNode, p = de.point, t = de.target;
34771         if(!(ns instanceof Array)){
34772             ns = [ns];
34773         }
34774         var n;
34775         for(var i = 0, len = ns.length; i < len; i++){
34776             n = ns[i];
34777             if(p == "above"){
34778                 t.parentNode.insertBefore(n, t);
34779             }else if(p == "below"){
34780                 t.parentNode.insertBefore(n, t.nextSibling);
34781             }else{
34782                 t.appendChild(n);
34783             }
34784         }
34785         n.ui.focus();
34786         if(this.tree.hlDrop){
34787             n.ui.highlight();
34788         }
34789         t.ui.endDrop();
34790         this.tree.fireEvent("nodedrop", de);
34791     },
34792     
34793     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34794         if(this.tree.hlDrop){
34795             dropNode.ui.focus();
34796             dropNode.ui.highlight();
34797         }
34798         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34799     },
34800     
34801     getTree : function(){
34802         return this.tree;
34803     },
34804     
34805     removeDropIndicators : function(n){
34806         if(n && n.ddel){
34807             var el = n.ddel;
34808             Roo.fly(el).removeClass([
34809                     "x-tree-drag-insert-above",
34810                     "x-tree-drag-insert-below",
34811                     "x-tree-drag-append"]);
34812             this.lastInsertClass = "_noclass";
34813         }
34814     },
34815     
34816     beforeDragDrop : function(target, e, id){
34817         this.cancelExpand();
34818         return true;
34819     },
34820     
34821     afterRepair : function(data){
34822         if(data && Roo.enableFx){
34823             data.node.ui.highlight();
34824         }
34825         this.hideProxy();
34826     } 
34827     
34828 });
34829
34830 }
34831 /*
34832  * Based on:
34833  * Ext JS Library 1.1.1
34834  * Copyright(c) 2006-2007, Ext JS, LLC.
34835  *
34836  * Originally Released Under LGPL - original licence link has changed is not relivant.
34837  *
34838  * Fork - LGPL
34839  * <script type="text/javascript">
34840  */
34841  
34842
34843 if(Roo.dd.DragZone){
34844 Roo.tree.TreeDragZone = function(tree, config){
34845     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34846     this.tree = tree;
34847 };
34848
34849 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34850     ddGroup : "TreeDD",
34851    
34852     onBeforeDrag : function(data, e){
34853         var n = data.node;
34854         return n && n.draggable && !n.disabled;
34855     },
34856      
34857     
34858     onInitDrag : function(e){
34859         var data = this.dragData;
34860         this.tree.getSelectionModel().select(data.node);
34861         this.proxy.update("");
34862         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34863         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34864     },
34865     
34866     getRepairXY : function(e, data){
34867         return data.node.ui.getDDRepairXY();
34868     },
34869     
34870     onEndDrag : function(data, e){
34871         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34872         
34873         
34874     },
34875     
34876     onValidDrop : function(dd, e, id){
34877         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34878         this.hideProxy();
34879     },
34880     
34881     beforeInvalidDrop : function(e, id){
34882         // this scrolls the original position back into view
34883         var sm = this.tree.getSelectionModel();
34884         sm.clearSelections();
34885         sm.select(this.dragData.node);
34886     }
34887 });
34888 }/*
34889  * Based on:
34890  * Ext JS Library 1.1.1
34891  * Copyright(c) 2006-2007, Ext JS, LLC.
34892  *
34893  * Originally Released Under LGPL - original licence link has changed is not relivant.
34894  *
34895  * Fork - LGPL
34896  * <script type="text/javascript">
34897  */
34898 /**
34899  * @class Roo.tree.TreeEditor
34900  * @extends Roo.Editor
34901  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34902  * as the editor field.
34903  * @constructor
34904  * @param {Object} config (used to be the tree panel.)
34905  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34906  * 
34907  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34908  * @cfg {Roo.form.TextField|Object} field The field configuration
34909  *
34910  * 
34911  */
34912 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34913     var tree = config;
34914     var field;
34915     if (oldconfig) { // old style..
34916         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34917     } else {
34918         // new style..
34919         tree = config.tree;
34920         config.field = config.field  || {};
34921         config.field.xtype = 'TextField';
34922         field = Roo.factory(config.field, Roo.form);
34923     }
34924     config = config || {};
34925     
34926     
34927     this.addEvents({
34928         /**
34929          * @event beforenodeedit
34930          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34931          * false from the handler of this event.
34932          * @param {Editor} this
34933          * @param {Roo.tree.Node} node 
34934          */
34935         "beforenodeedit" : true
34936     });
34937     
34938     //Roo.log(config);
34939     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34940
34941     this.tree = tree;
34942
34943     tree.on('beforeclick', this.beforeNodeClick, this);
34944     tree.getTreeEl().on('mousedown', this.hide, this);
34945     this.on('complete', this.updateNode, this);
34946     this.on('beforestartedit', this.fitToTree, this);
34947     this.on('startedit', this.bindScroll, this, {delay:10});
34948     this.on('specialkey', this.onSpecialKey, this);
34949 };
34950
34951 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34952     /**
34953      * @cfg {String} alignment
34954      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34955      */
34956     alignment: "l-l",
34957     // inherit
34958     autoSize: false,
34959     /**
34960      * @cfg {Boolean} hideEl
34961      * True to hide the bound element while the editor is displayed (defaults to false)
34962      */
34963     hideEl : false,
34964     /**
34965      * @cfg {String} cls
34966      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34967      */
34968     cls: "x-small-editor x-tree-editor",
34969     /**
34970      * @cfg {Boolean} shim
34971      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34972      */
34973     shim:false,
34974     // inherit
34975     shadow:"frame",
34976     /**
34977      * @cfg {Number} maxWidth
34978      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34979      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34980      * scroll and client offsets into account prior to each edit.
34981      */
34982     maxWidth: 250,
34983
34984     editDelay : 350,
34985
34986     // private
34987     fitToTree : function(ed, el){
34988         var td = this.tree.getTreeEl().dom, nd = el.dom;
34989         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34990             td.scrollLeft = nd.offsetLeft;
34991         }
34992         var w = Math.min(
34993                 this.maxWidth,
34994                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34995         this.setSize(w, '');
34996         
34997         return this.fireEvent('beforenodeedit', this, this.editNode);
34998         
34999     },
35000
35001     // private
35002     triggerEdit : function(node){
35003         this.completeEdit();
35004         this.editNode = node;
35005         this.startEdit(node.ui.textNode, node.text);
35006     },
35007
35008     // private
35009     bindScroll : function(){
35010         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35011     },
35012
35013     // private
35014     beforeNodeClick : function(node, e){
35015         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35016         this.lastClick = new Date();
35017         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35018             e.stopEvent();
35019             this.triggerEdit(node);
35020             return false;
35021         }
35022         return true;
35023     },
35024
35025     // private
35026     updateNode : function(ed, value){
35027         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35028         this.editNode.setText(value);
35029     },
35030
35031     // private
35032     onHide : function(){
35033         Roo.tree.TreeEditor.superclass.onHide.call(this);
35034         if(this.editNode){
35035             this.editNode.ui.focus();
35036         }
35037     },
35038
35039     // private
35040     onSpecialKey : function(field, e){
35041         var k = e.getKey();
35042         if(k == e.ESC){
35043             e.stopEvent();
35044             this.cancelEdit();
35045         }else if(k == e.ENTER && !e.hasModifier()){
35046             e.stopEvent();
35047             this.completeEdit();
35048         }
35049     }
35050 });//<Script type="text/javascript">
35051 /*
35052  * Based on:
35053  * Ext JS Library 1.1.1
35054  * Copyright(c) 2006-2007, Ext JS, LLC.
35055  *
35056  * Originally Released Under LGPL - original licence link has changed is not relivant.
35057  *
35058  * Fork - LGPL
35059  * <script type="text/javascript">
35060  */
35061  
35062 /**
35063  * Not documented??? - probably should be...
35064  */
35065
35066 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35067     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35068     
35069     renderElements : function(n, a, targetNode, bulkRender){
35070         //consel.log("renderElements?");
35071         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35072
35073         var t = n.getOwnerTree();
35074         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35075         
35076         var cols = t.columns;
35077         var bw = t.borderWidth;
35078         var c = cols[0];
35079         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35080          var cb = typeof a.checked == "boolean";
35081         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35082         var colcls = 'x-t-' + tid + '-c0';
35083         var buf = [
35084             '<li class="x-tree-node">',
35085             
35086                 
35087                 '<div class="x-tree-node-el ', a.cls,'">',
35088                     // extran...
35089                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35090                 
35091                 
35092                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35093                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35094                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35095                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35096                            (a.iconCls ? ' '+a.iconCls : ''),
35097                            '" unselectable="on" />',
35098                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35099                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35100                              
35101                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35102                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35103                             '<span unselectable="on" qtip="' + tx + '">',
35104                              tx,
35105                              '</span></a>' ,
35106                     '</div>',
35107                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35108                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35109                  ];
35110         for(var i = 1, len = cols.length; i < len; i++){
35111             c = cols[i];
35112             colcls = 'x-t-' + tid + '-c' +i;
35113             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35114             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35115                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35116                       "</div>");
35117          }
35118          
35119          buf.push(
35120             '</a>',
35121             '<div class="x-clear"></div></div>',
35122             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35123             "</li>");
35124         
35125         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35126             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35127                                 n.nextSibling.ui.getEl(), buf.join(""));
35128         }else{
35129             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35130         }
35131         var el = this.wrap.firstChild;
35132         this.elRow = el;
35133         this.elNode = el.firstChild;
35134         this.ranchor = el.childNodes[1];
35135         this.ctNode = this.wrap.childNodes[1];
35136         var cs = el.firstChild.childNodes;
35137         this.indentNode = cs[0];
35138         this.ecNode = cs[1];
35139         this.iconNode = cs[2];
35140         var index = 3;
35141         if(cb){
35142             this.checkbox = cs[3];
35143             index++;
35144         }
35145         this.anchor = cs[index];
35146         
35147         this.textNode = cs[index].firstChild;
35148         
35149         //el.on("click", this.onClick, this);
35150         //el.on("dblclick", this.onDblClick, this);
35151         
35152         
35153        // console.log(this);
35154     },
35155     initEvents : function(){
35156         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35157         
35158             
35159         var a = this.ranchor;
35160
35161         var el = Roo.get(a);
35162
35163         if(Roo.isOpera){ // opera render bug ignores the CSS
35164             el.setStyle("text-decoration", "none");
35165         }
35166
35167         el.on("click", this.onClick, this);
35168         el.on("dblclick", this.onDblClick, this);
35169         el.on("contextmenu", this.onContextMenu, this);
35170         
35171     },
35172     
35173     /*onSelectedChange : function(state){
35174         if(state){
35175             this.focus();
35176             this.addClass("x-tree-selected");
35177         }else{
35178             //this.blur();
35179             this.removeClass("x-tree-selected");
35180         }
35181     },*/
35182     addClass : function(cls){
35183         if(this.elRow){
35184             Roo.fly(this.elRow).addClass(cls);
35185         }
35186         
35187     },
35188     
35189     
35190     removeClass : function(cls){
35191         if(this.elRow){
35192             Roo.fly(this.elRow).removeClass(cls);
35193         }
35194     }
35195
35196     
35197     
35198 });//<Script type="text/javascript">
35199
35200 /*
35201  * Based on:
35202  * Ext JS Library 1.1.1
35203  * Copyright(c) 2006-2007, Ext JS, LLC.
35204  *
35205  * Originally Released Under LGPL - original licence link has changed is not relivant.
35206  *
35207  * Fork - LGPL
35208  * <script type="text/javascript">
35209  */
35210  
35211
35212 /**
35213  * @class Roo.tree.ColumnTree
35214  * @extends Roo.data.TreePanel
35215  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35216  * @cfg {int} borderWidth  compined right/left border allowance
35217  * @constructor
35218  * @param {String/HTMLElement/Element} el The container element
35219  * @param {Object} config
35220  */
35221 Roo.tree.ColumnTree =  function(el, config)
35222 {
35223    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35224    this.addEvents({
35225         /**
35226         * @event resize
35227         * Fire this event on a container when it resizes
35228         * @param {int} w Width
35229         * @param {int} h Height
35230         */
35231        "resize" : true
35232     });
35233     this.on('resize', this.onResize, this);
35234 };
35235
35236 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35237     //lines:false,
35238     
35239     
35240     borderWidth: Roo.isBorderBox ? 0 : 2, 
35241     headEls : false,
35242     
35243     render : function(){
35244         // add the header.....
35245        
35246         Roo.tree.ColumnTree.superclass.render.apply(this);
35247         
35248         this.el.addClass('x-column-tree');
35249         
35250         this.headers = this.el.createChild(
35251             {cls:'x-tree-headers'},this.innerCt.dom);
35252    
35253         var cols = this.columns, c;
35254         var totalWidth = 0;
35255         this.headEls = [];
35256         var  len = cols.length;
35257         for(var i = 0; i < len; i++){
35258              c = cols[i];
35259              totalWidth += c.width;
35260             this.headEls.push(this.headers.createChild({
35261                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35262                  cn: {
35263                      cls:'x-tree-hd-text',
35264                      html: c.header
35265                  },
35266                  style:'width:'+(c.width-this.borderWidth)+'px;'
35267              }));
35268         }
35269         this.headers.createChild({cls:'x-clear'});
35270         // prevent floats from wrapping when clipped
35271         this.headers.setWidth(totalWidth);
35272         //this.innerCt.setWidth(totalWidth);
35273         this.innerCt.setStyle({ overflow: 'auto' });
35274         this.onResize(this.width, this.height);
35275              
35276         
35277     },
35278     onResize : function(w,h)
35279     {
35280         this.height = h;
35281         this.width = w;
35282         // resize cols..
35283         this.innerCt.setWidth(this.width);
35284         this.innerCt.setHeight(this.height-20);
35285         
35286         // headers...
35287         var cols = this.columns, c;
35288         var totalWidth = 0;
35289         var expEl = false;
35290         var len = cols.length;
35291         for(var i = 0; i < len; i++){
35292             c = cols[i];
35293             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35294                 // it's the expander..
35295                 expEl  = this.headEls[i];
35296                 continue;
35297             }
35298             totalWidth += c.width;
35299             
35300         }
35301         if (expEl) {
35302             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35303         }
35304         this.headers.setWidth(w-20);
35305
35306         
35307         
35308         
35309     }
35310 });
35311 /*
35312  * Based on:
35313  * Ext JS Library 1.1.1
35314  * Copyright(c) 2006-2007, Ext JS, LLC.
35315  *
35316  * Originally Released Under LGPL - original licence link has changed is not relivant.
35317  *
35318  * Fork - LGPL
35319  * <script type="text/javascript">
35320  */
35321  
35322 /**
35323  * @class Roo.menu.Menu
35324  * @extends Roo.util.Observable
35325  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35326  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35327  * @constructor
35328  * Creates a new Menu
35329  * @param {Object} config Configuration options
35330  */
35331 Roo.menu.Menu = function(config){
35332     Roo.apply(this, config);
35333     this.id = this.id || Roo.id();
35334     this.addEvents({
35335         /**
35336          * @event beforeshow
35337          * Fires before this menu is displayed
35338          * @param {Roo.menu.Menu} this
35339          */
35340         beforeshow : true,
35341         /**
35342          * @event beforehide
35343          * Fires before this menu is hidden
35344          * @param {Roo.menu.Menu} this
35345          */
35346         beforehide : true,
35347         /**
35348          * @event show
35349          * Fires after this menu is displayed
35350          * @param {Roo.menu.Menu} this
35351          */
35352         show : true,
35353         /**
35354          * @event hide
35355          * Fires after this menu is hidden
35356          * @param {Roo.menu.Menu} this
35357          */
35358         hide : true,
35359         /**
35360          * @event click
35361          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35362          * @param {Roo.menu.Menu} this
35363          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35364          * @param {Roo.EventObject} e
35365          */
35366         click : true,
35367         /**
35368          * @event mouseover
35369          * Fires when the mouse is hovering over this menu
35370          * @param {Roo.menu.Menu} this
35371          * @param {Roo.EventObject} e
35372          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35373          */
35374         mouseover : true,
35375         /**
35376          * @event mouseout
35377          * Fires when the mouse exits this menu
35378          * @param {Roo.menu.Menu} this
35379          * @param {Roo.EventObject} e
35380          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35381          */
35382         mouseout : true,
35383         /**
35384          * @event itemclick
35385          * Fires when a menu item contained in this menu is clicked
35386          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35387          * @param {Roo.EventObject} e
35388          */
35389         itemclick: true
35390     });
35391     if (this.registerMenu) {
35392         Roo.menu.MenuMgr.register(this);
35393     }
35394     
35395     var mis = this.items;
35396     this.items = new Roo.util.MixedCollection();
35397     if(mis){
35398         this.add.apply(this, mis);
35399     }
35400 };
35401
35402 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35403     /**
35404      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35405      */
35406     minWidth : 120,
35407     /**
35408      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35409      * for bottom-right shadow (defaults to "sides")
35410      */
35411     shadow : "sides",
35412     /**
35413      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35414      * this menu (defaults to "tl-tr?")
35415      */
35416     subMenuAlign : "tl-tr?",
35417     /**
35418      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35419      * relative to its element of origin (defaults to "tl-bl?")
35420      */
35421     defaultAlign : "tl-bl?",
35422     /**
35423      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35424      */
35425     allowOtherMenus : false,
35426     /**
35427      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35428      */
35429     registerMenu : true,
35430
35431     hidden:true,
35432
35433     // private
35434     render : function(){
35435         if(this.el){
35436             return;
35437         }
35438         var el = this.el = new Roo.Layer({
35439             cls: "x-menu",
35440             shadow:this.shadow,
35441             constrain: false,
35442             parentEl: this.parentEl || document.body,
35443             zindex:15000
35444         });
35445
35446         this.keyNav = new Roo.menu.MenuNav(this);
35447
35448         if(this.plain){
35449             el.addClass("x-menu-plain");
35450         }
35451         if(this.cls){
35452             el.addClass(this.cls);
35453         }
35454         // generic focus element
35455         this.focusEl = el.createChild({
35456             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35457         });
35458         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35459         ul.on("click", this.onClick, this);
35460         ul.on("mouseover", this.onMouseOver, this);
35461         ul.on("mouseout", this.onMouseOut, this);
35462         this.items.each(function(item){
35463             if (item.hidden) {
35464                 return;
35465             }
35466             
35467             var li = document.createElement("li");
35468             li.className = "x-menu-list-item";
35469             ul.dom.appendChild(li);
35470             item.render(li, this);
35471         }, this);
35472         this.ul = ul;
35473         this.autoWidth();
35474     },
35475
35476     // private
35477     autoWidth : function(){
35478         var el = this.el, ul = this.ul;
35479         if(!el){
35480             return;
35481         }
35482         var w = this.width;
35483         if(w){
35484             el.setWidth(w);
35485         }else if(Roo.isIE){
35486             el.setWidth(this.minWidth);
35487             var t = el.dom.offsetWidth; // force recalc
35488             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35489         }
35490     },
35491
35492     // private
35493     delayAutoWidth : function(){
35494         if(this.rendered){
35495             if(!this.awTask){
35496                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35497             }
35498             this.awTask.delay(20);
35499         }
35500     },
35501
35502     // private
35503     findTargetItem : function(e){
35504         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35505         if(t && t.menuItemId){
35506             return this.items.get(t.menuItemId);
35507         }
35508     },
35509
35510     // private
35511     onClick : function(e){
35512         var t;
35513         if(t = this.findTargetItem(e)){
35514             t.onClick(e);
35515             this.fireEvent("click", this, t, e);
35516         }
35517     },
35518
35519     // private
35520     setActiveItem : function(item, autoExpand){
35521         if(item != this.activeItem){
35522             if(this.activeItem){
35523                 this.activeItem.deactivate();
35524             }
35525             this.activeItem = item;
35526             item.activate(autoExpand);
35527         }else if(autoExpand){
35528             item.expandMenu();
35529         }
35530     },
35531
35532     // private
35533     tryActivate : function(start, step){
35534         var items = this.items;
35535         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35536             var item = items.get(i);
35537             if(!item.disabled && item.canActivate){
35538                 this.setActiveItem(item, false);
35539                 return item;
35540             }
35541         }
35542         return false;
35543     },
35544
35545     // private
35546     onMouseOver : function(e){
35547         var t;
35548         if(t = this.findTargetItem(e)){
35549             if(t.canActivate && !t.disabled){
35550                 this.setActiveItem(t, true);
35551             }
35552         }
35553         this.fireEvent("mouseover", this, e, t);
35554     },
35555
35556     // private
35557     onMouseOut : function(e){
35558         var t;
35559         if(t = this.findTargetItem(e)){
35560             if(t == this.activeItem && t.shouldDeactivate(e)){
35561                 this.activeItem.deactivate();
35562                 delete this.activeItem;
35563             }
35564         }
35565         this.fireEvent("mouseout", this, e, t);
35566     },
35567
35568     /**
35569      * Read-only.  Returns true if the menu is currently displayed, else false.
35570      * @type Boolean
35571      */
35572     isVisible : function(){
35573         return this.el && !this.hidden;
35574     },
35575
35576     /**
35577      * Displays this menu relative to another element
35578      * @param {String/HTMLElement/Roo.Element} element The element to align to
35579      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35580      * the element (defaults to this.defaultAlign)
35581      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35582      */
35583     show : function(el, pos, parentMenu){
35584         this.parentMenu = parentMenu;
35585         if(!this.el){
35586             this.render();
35587         }
35588         this.fireEvent("beforeshow", this);
35589         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35590     },
35591
35592     /**
35593      * Displays this menu at a specific xy position
35594      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35595      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35596      */
35597     showAt : function(xy, parentMenu, /* private: */_e){
35598         this.parentMenu = parentMenu;
35599         if(!this.el){
35600             this.render();
35601         }
35602         if(_e !== false){
35603             this.fireEvent("beforeshow", this);
35604             xy = this.el.adjustForConstraints(xy);
35605         }
35606         this.el.setXY(xy);
35607         this.el.show();
35608         this.hidden = false;
35609         this.focus();
35610         this.fireEvent("show", this);
35611     },
35612
35613     focus : function(){
35614         if(!this.hidden){
35615             this.doFocus.defer(50, this);
35616         }
35617     },
35618
35619     doFocus : function(){
35620         if(!this.hidden){
35621             this.focusEl.focus();
35622         }
35623     },
35624
35625     /**
35626      * Hides this menu and optionally all parent menus
35627      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35628      */
35629     hide : function(deep){
35630         if(this.el && this.isVisible()){
35631             this.fireEvent("beforehide", this);
35632             if(this.activeItem){
35633                 this.activeItem.deactivate();
35634                 this.activeItem = null;
35635             }
35636             this.el.hide();
35637             this.hidden = true;
35638             this.fireEvent("hide", this);
35639         }
35640         if(deep === true && this.parentMenu){
35641             this.parentMenu.hide(true);
35642         }
35643     },
35644
35645     /**
35646      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35647      * Any of the following are valid:
35648      * <ul>
35649      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35650      * <li>An HTMLElement object which will be converted to a menu item</li>
35651      * <li>A menu item config object that will be created as a new menu item</li>
35652      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35653      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35654      * </ul>
35655      * Usage:
35656      * <pre><code>
35657 // Create the menu
35658 var menu = new Roo.menu.Menu();
35659
35660 // Create a menu item to add by reference
35661 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35662
35663 // Add a bunch of items at once using different methods.
35664 // Only the last item added will be returned.
35665 var item = menu.add(
35666     menuItem,                // add existing item by ref
35667     'Dynamic Item',          // new TextItem
35668     '-',                     // new separator
35669     { text: 'Config Item' }  // new item by config
35670 );
35671 </code></pre>
35672      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35673      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35674      */
35675     add : function(){
35676         var a = arguments, l = a.length, item;
35677         for(var i = 0; i < l; i++){
35678             var el = a[i];
35679             if ((typeof(el) == "object") && el.xtype && el.xns) {
35680                 el = Roo.factory(el, Roo.menu);
35681             }
35682             
35683             if(el.render){ // some kind of Item
35684                 item = this.addItem(el);
35685             }else if(typeof el == "string"){ // string
35686                 if(el == "separator" || el == "-"){
35687                     item = this.addSeparator();
35688                 }else{
35689                     item = this.addText(el);
35690                 }
35691             }else if(el.tagName || el.el){ // element
35692                 item = this.addElement(el);
35693             }else if(typeof el == "object"){ // must be menu item config?
35694                 item = this.addMenuItem(el);
35695             }
35696         }
35697         return item;
35698     },
35699
35700     /**
35701      * Returns this menu's underlying {@link Roo.Element} object
35702      * @return {Roo.Element} The element
35703      */
35704     getEl : function(){
35705         if(!this.el){
35706             this.render();
35707         }
35708         return this.el;
35709     },
35710
35711     /**
35712      * Adds a separator bar to the menu
35713      * @return {Roo.menu.Item} The menu item that was added
35714      */
35715     addSeparator : function(){
35716         return this.addItem(new Roo.menu.Separator());
35717     },
35718
35719     /**
35720      * Adds an {@link Roo.Element} object to the menu
35721      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35722      * @return {Roo.menu.Item} The menu item that was added
35723      */
35724     addElement : function(el){
35725         return this.addItem(new Roo.menu.BaseItem(el));
35726     },
35727
35728     /**
35729      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35730      * @param {Roo.menu.Item} item The menu item to add
35731      * @return {Roo.menu.Item} The menu item that was added
35732      */
35733     addItem : function(item){
35734         this.items.add(item);
35735         if(this.ul){
35736             var li = document.createElement("li");
35737             li.className = "x-menu-list-item";
35738             this.ul.dom.appendChild(li);
35739             item.render(li, this);
35740             this.delayAutoWidth();
35741         }
35742         return item;
35743     },
35744
35745     /**
35746      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35747      * @param {Object} config A MenuItem config object
35748      * @return {Roo.menu.Item} The menu item that was added
35749      */
35750     addMenuItem : function(config){
35751         if(!(config instanceof Roo.menu.Item)){
35752             if(typeof config.checked == "boolean"){ // must be check menu item config?
35753                 config = new Roo.menu.CheckItem(config);
35754             }else{
35755                 config = new Roo.menu.Item(config);
35756             }
35757         }
35758         return this.addItem(config);
35759     },
35760
35761     /**
35762      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35763      * @param {String} text The text to display in the menu item
35764      * @return {Roo.menu.Item} The menu item that was added
35765      */
35766     addText : function(text){
35767         return this.addItem(new Roo.menu.TextItem({ text : text }));
35768     },
35769
35770     /**
35771      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35772      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35773      * @param {Roo.menu.Item} item The menu item to add
35774      * @return {Roo.menu.Item} The menu item that was added
35775      */
35776     insert : function(index, item){
35777         this.items.insert(index, item);
35778         if(this.ul){
35779             var li = document.createElement("li");
35780             li.className = "x-menu-list-item";
35781             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35782             item.render(li, this);
35783             this.delayAutoWidth();
35784         }
35785         return item;
35786     },
35787
35788     /**
35789      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35790      * @param {Roo.menu.Item} item The menu item to remove
35791      */
35792     remove : function(item){
35793         this.items.removeKey(item.id);
35794         item.destroy();
35795     },
35796
35797     /**
35798      * Removes and destroys all items in the menu
35799      */
35800     removeAll : function(){
35801         var f;
35802         while(f = this.items.first()){
35803             this.remove(f);
35804         }
35805     }
35806 });
35807
35808 // MenuNav is a private utility class used internally by the Menu
35809 Roo.menu.MenuNav = function(menu){
35810     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35811     this.scope = this.menu = menu;
35812 };
35813
35814 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35815     doRelay : function(e, h){
35816         var k = e.getKey();
35817         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35818             this.menu.tryActivate(0, 1);
35819             return false;
35820         }
35821         return h.call(this.scope || this, e, this.menu);
35822     },
35823
35824     up : function(e, m){
35825         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35826             m.tryActivate(m.items.length-1, -1);
35827         }
35828     },
35829
35830     down : function(e, m){
35831         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35832             m.tryActivate(0, 1);
35833         }
35834     },
35835
35836     right : function(e, m){
35837         if(m.activeItem){
35838             m.activeItem.expandMenu(true);
35839         }
35840     },
35841
35842     left : function(e, m){
35843         m.hide();
35844         if(m.parentMenu && m.parentMenu.activeItem){
35845             m.parentMenu.activeItem.activate();
35846         }
35847     },
35848
35849     enter : function(e, m){
35850         if(m.activeItem){
35851             e.stopPropagation();
35852             m.activeItem.onClick(e);
35853             m.fireEvent("click", this, m.activeItem);
35854             return true;
35855         }
35856     }
35857 });/*
35858  * Based on:
35859  * Ext JS Library 1.1.1
35860  * Copyright(c) 2006-2007, Ext JS, LLC.
35861  *
35862  * Originally Released Under LGPL - original licence link has changed is not relivant.
35863  *
35864  * Fork - LGPL
35865  * <script type="text/javascript">
35866  */
35867  
35868 /**
35869  * @class Roo.menu.MenuMgr
35870  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35871  * @singleton
35872  */
35873 Roo.menu.MenuMgr = function(){
35874    var menus, active, groups = {}, attached = false, lastShow = new Date();
35875
35876    // private - called when first menu is created
35877    function init(){
35878        menus = {};
35879        active = new Roo.util.MixedCollection();
35880        Roo.get(document).addKeyListener(27, function(){
35881            if(active.length > 0){
35882                hideAll();
35883            }
35884        });
35885    }
35886
35887    // private
35888    function hideAll(){
35889        if(active && active.length > 0){
35890            var c = active.clone();
35891            c.each(function(m){
35892                m.hide();
35893            });
35894        }
35895    }
35896
35897    // private
35898    function onHide(m){
35899        active.remove(m);
35900        if(active.length < 1){
35901            Roo.get(document).un("mousedown", onMouseDown);
35902            attached = false;
35903        }
35904    }
35905
35906    // private
35907    function onShow(m){
35908        var last = active.last();
35909        lastShow = new Date();
35910        active.add(m);
35911        if(!attached){
35912            Roo.get(document).on("mousedown", onMouseDown);
35913            attached = true;
35914        }
35915        if(m.parentMenu){
35916           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35917           m.parentMenu.activeChild = m;
35918        }else if(last && last.isVisible()){
35919           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35920        }
35921    }
35922
35923    // private
35924    function onBeforeHide(m){
35925        if(m.activeChild){
35926            m.activeChild.hide();
35927        }
35928        if(m.autoHideTimer){
35929            clearTimeout(m.autoHideTimer);
35930            delete m.autoHideTimer;
35931        }
35932    }
35933
35934    // private
35935    function onBeforeShow(m){
35936        var pm = m.parentMenu;
35937        if(!pm && !m.allowOtherMenus){
35938            hideAll();
35939        }else if(pm && pm.activeChild && active != m){
35940            pm.activeChild.hide();
35941        }
35942    }
35943
35944    // private
35945    function onMouseDown(e){
35946        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35947            hideAll();
35948        }
35949    }
35950
35951    // private
35952    function onBeforeCheck(mi, state){
35953        if(state){
35954            var g = groups[mi.group];
35955            for(var i = 0, l = g.length; i < l; i++){
35956                if(g[i] != mi){
35957                    g[i].setChecked(false);
35958                }
35959            }
35960        }
35961    }
35962
35963    return {
35964
35965        /**
35966         * Hides all menus that are currently visible
35967         */
35968        hideAll : function(){
35969             hideAll();  
35970        },
35971
35972        // private
35973        register : function(menu){
35974            if(!menus){
35975                init();
35976            }
35977            menus[menu.id] = menu;
35978            menu.on("beforehide", onBeforeHide);
35979            menu.on("hide", onHide);
35980            menu.on("beforeshow", onBeforeShow);
35981            menu.on("show", onShow);
35982            var g = menu.group;
35983            if(g && menu.events["checkchange"]){
35984                if(!groups[g]){
35985                    groups[g] = [];
35986                }
35987                groups[g].push(menu);
35988                menu.on("checkchange", onCheck);
35989            }
35990        },
35991
35992         /**
35993          * Returns a {@link Roo.menu.Menu} object
35994          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35995          * be used to generate and return a new Menu instance.
35996          */
35997        get : function(menu){
35998            if(typeof menu == "string"){ // menu id
35999                return menus[menu];
36000            }else if(menu.events){  // menu instance
36001                return menu;
36002            }else if(typeof menu.length == 'number'){ // array of menu items?
36003                return new Roo.menu.Menu({items:menu});
36004            }else{ // otherwise, must be a config
36005                return new Roo.menu.Menu(menu);
36006            }
36007        },
36008
36009        // private
36010        unregister : function(menu){
36011            delete menus[menu.id];
36012            menu.un("beforehide", onBeforeHide);
36013            menu.un("hide", onHide);
36014            menu.un("beforeshow", onBeforeShow);
36015            menu.un("show", onShow);
36016            var g = menu.group;
36017            if(g && menu.events["checkchange"]){
36018                groups[g].remove(menu);
36019                menu.un("checkchange", onCheck);
36020            }
36021        },
36022
36023        // private
36024        registerCheckable : function(menuItem){
36025            var g = menuItem.group;
36026            if(g){
36027                if(!groups[g]){
36028                    groups[g] = [];
36029                }
36030                groups[g].push(menuItem);
36031                menuItem.on("beforecheckchange", onBeforeCheck);
36032            }
36033        },
36034
36035        // private
36036        unregisterCheckable : function(menuItem){
36037            var g = menuItem.group;
36038            if(g){
36039                groups[g].remove(menuItem);
36040                menuItem.un("beforecheckchange", onBeforeCheck);
36041            }
36042        }
36043    };
36044 }();/*
36045  * Based on:
36046  * Ext JS Library 1.1.1
36047  * Copyright(c) 2006-2007, Ext JS, LLC.
36048  *
36049  * Originally Released Under LGPL - original licence link has changed is not relivant.
36050  *
36051  * Fork - LGPL
36052  * <script type="text/javascript">
36053  */
36054  
36055
36056 /**
36057  * @class Roo.menu.BaseItem
36058  * @extends Roo.Component
36059  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36060  * management and base configuration options shared by all menu components.
36061  * @constructor
36062  * Creates a new BaseItem
36063  * @param {Object} config Configuration options
36064  */
36065 Roo.menu.BaseItem = function(config){
36066     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36067
36068     this.addEvents({
36069         /**
36070          * @event click
36071          * Fires when this item is clicked
36072          * @param {Roo.menu.BaseItem} this
36073          * @param {Roo.EventObject} e
36074          */
36075         click: true,
36076         /**
36077          * @event activate
36078          * Fires when this item is activated
36079          * @param {Roo.menu.BaseItem} this
36080          */
36081         activate : true,
36082         /**
36083          * @event deactivate
36084          * Fires when this item is deactivated
36085          * @param {Roo.menu.BaseItem} this
36086          */
36087         deactivate : true
36088     });
36089
36090     if(this.handler){
36091         this.on("click", this.handler, this.scope, true);
36092     }
36093 };
36094
36095 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36096     /**
36097      * @cfg {Function} handler
36098      * A function that will handle the click event of this menu item (defaults to undefined)
36099      */
36100     /**
36101      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36102      */
36103     canActivate : false,
36104     
36105      /**
36106      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36107      */
36108     hidden: false,
36109     
36110     /**
36111      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36112      */
36113     activeClass : "x-menu-item-active",
36114     /**
36115      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36116      */
36117     hideOnClick : true,
36118     /**
36119      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36120      */
36121     hideDelay : 100,
36122
36123     // private
36124     ctype: "Roo.menu.BaseItem",
36125
36126     // private
36127     actionMode : "container",
36128
36129     // private
36130     render : function(container, parentMenu){
36131         this.parentMenu = parentMenu;
36132         Roo.menu.BaseItem.superclass.render.call(this, container);
36133         this.container.menuItemId = this.id;
36134     },
36135
36136     // private
36137     onRender : function(container, position){
36138         this.el = Roo.get(this.el);
36139         container.dom.appendChild(this.el.dom);
36140     },
36141
36142     // private
36143     onClick : function(e){
36144         if(!this.disabled && this.fireEvent("click", this, e) !== false
36145                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36146             this.handleClick(e);
36147         }else{
36148             e.stopEvent();
36149         }
36150     },
36151
36152     // private
36153     activate : function(){
36154         if(this.disabled){
36155             return false;
36156         }
36157         var li = this.container;
36158         li.addClass(this.activeClass);
36159         this.region = li.getRegion().adjust(2, 2, -2, -2);
36160         this.fireEvent("activate", this);
36161         return true;
36162     },
36163
36164     // private
36165     deactivate : function(){
36166         this.container.removeClass(this.activeClass);
36167         this.fireEvent("deactivate", this);
36168     },
36169
36170     // private
36171     shouldDeactivate : function(e){
36172         return !this.region || !this.region.contains(e.getPoint());
36173     },
36174
36175     // private
36176     handleClick : function(e){
36177         if(this.hideOnClick){
36178             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36179         }
36180     },
36181
36182     // private
36183     expandMenu : function(autoActivate){
36184         // do nothing
36185     },
36186
36187     // private
36188     hideMenu : function(){
36189         // do nothing
36190     }
36191 });/*
36192  * Based on:
36193  * Ext JS Library 1.1.1
36194  * Copyright(c) 2006-2007, Ext JS, LLC.
36195  *
36196  * Originally Released Under LGPL - original licence link has changed is not relivant.
36197  *
36198  * Fork - LGPL
36199  * <script type="text/javascript">
36200  */
36201  
36202 /**
36203  * @class Roo.menu.Adapter
36204  * @extends Roo.menu.BaseItem
36205  * 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.
36206  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36207  * @constructor
36208  * Creates a new Adapter
36209  * @param {Object} config Configuration options
36210  */
36211 Roo.menu.Adapter = function(component, config){
36212     Roo.menu.Adapter.superclass.constructor.call(this, config);
36213     this.component = component;
36214 };
36215 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36216     // private
36217     canActivate : true,
36218
36219     // private
36220     onRender : function(container, position){
36221         this.component.render(container);
36222         this.el = this.component.getEl();
36223     },
36224
36225     // private
36226     activate : function(){
36227         if(this.disabled){
36228             return false;
36229         }
36230         this.component.focus();
36231         this.fireEvent("activate", this);
36232         return true;
36233     },
36234
36235     // private
36236     deactivate : function(){
36237         this.fireEvent("deactivate", this);
36238     },
36239
36240     // private
36241     disable : function(){
36242         this.component.disable();
36243         Roo.menu.Adapter.superclass.disable.call(this);
36244     },
36245
36246     // private
36247     enable : function(){
36248         this.component.enable();
36249         Roo.menu.Adapter.superclass.enable.call(this);
36250     }
36251 });/*
36252  * Based on:
36253  * Ext JS Library 1.1.1
36254  * Copyright(c) 2006-2007, Ext JS, LLC.
36255  *
36256  * Originally Released Under LGPL - original licence link has changed is not relivant.
36257  *
36258  * Fork - LGPL
36259  * <script type="text/javascript">
36260  */
36261
36262 /**
36263  * @class Roo.menu.TextItem
36264  * @extends Roo.menu.BaseItem
36265  * Adds a static text string to a menu, usually used as either a heading or group separator.
36266  * Note: old style constructor with text is still supported.
36267  * 
36268  * @constructor
36269  * Creates a new TextItem
36270  * @param {Object} cfg Configuration
36271  */
36272 Roo.menu.TextItem = function(cfg){
36273     if (typeof(cfg) == 'string') {
36274         this.text = cfg;
36275     } else {
36276         Roo.apply(this,cfg);
36277     }
36278     
36279     Roo.menu.TextItem.superclass.constructor.call(this);
36280 };
36281
36282 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36283     /**
36284      * @cfg {Boolean} text Text to show on item.
36285      */
36286     text : '',
36287     
36288     /**
36289      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36290      */
36291     hideOnClick : false,
36292     /**
36293      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36294      */
36295     itemCls : "x-menu-text",
36296
36297     // private
36298     onRender : function(){
36299         var s = document.createElement("span");
36300         s.className = this.itemCls;
36301         s.innerHTML = this.text;
36302         this.el = s;
36303         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36304     }
36305 });/*
36306  * Based on:
36307  * Ext JS Library 1.1.1
36308  * Copyright(c) 2006-2007, Ext JS, LLC.
36309  *
36310  * Originally Released Under LGPL - original licence link has changed is not relivant.
36311  *
36312  * Fork - LGPL
36313  * <script type="text/javascript">
36314  */
36315
36316 /**
36317  * @class Roo.menu.Separator
36318  * @extends Roo.menu.BaseItem
36319  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36320  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36321  * @constructor
36322  * @param {Object} config Configuration options
36323  */
36324 Roo.menu.Separator = function(config){
36325     Roo.menu.Separator.superclass.constructor.call(this, config);
36326 };
36327
36328 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36329     /**
36330      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36331      */
36332     itemCls : "x-menu-sep",
36333     /**
36334      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36335      */
36336     hideOnClick : false,
36337
36338     // private
36339     onRender : function(li){
36340         var s = document.createElement("span");
36341         s.className = this.itemCls;
36342         s.innerHTML = "&#160;";
36343         this.el = s;
36344         li.addClass("x-menu-sep-li");
36345         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36346     }
36347 });/*
36348  * Based on:
36349  * Ext JS Library 1.1.1
36350  * Copyright(c) 2006-2007, Ext JS, LLC.
36351  *
36352  * Originally Released Under LGPL - original licence link has changed is not relivant.
36353  *
36354  * Fork - LGPL
36355  * <script type="text/javascript">
36356  */
36357 /**
36358  * @class Roo.menu.Item
36359  * @extends Roo.menu.BaseItem
36360  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36361  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36362  * activation and click handling.
36363  * @constructor
36364  * Creates a new Item
36365  * @param {Object} config Configuration options
36366  */
36367 Roo.menu.Item = function(config){
36368     Roo.menu.Item.superclass.constructor.call(this, config);
36369     if(this.menu){
36370         this.menu = Roo.menu.MenuMgr.get(this.menu);
36371     }
36372 };
36373 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36374     
36375     /**
36376      * @cfg {String} text
36377      * The text to show on the menu item.
36378      */
36379     text: '',
36380      /**
36381      * @cfg {String} HTML to render in menu
36382      * The text to show on the menu item (HTML version).
36383      */
36384     html: '',
36385     /**
36386      * @cfg {String} icon
36387      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36388      */
36389     icon: undefined,
36390     /**
36391      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36392      */
36393     itemCls : "x-menu-item",
36394     /**
36395      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36396      */
36397     canActivate : true,
36398     /**
36399      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36400      */
36401     showDelay: 200,
36402     // doc'd in BaseItem
36403     hideDelay: 200,
36404
36405     // private
36406     ctype: "Roo.menu.Item",
36407     
36408     // private
36409     onRender : function(container, position){
36410         var el = document.createElement("a");
36411         el.hideFocus = true;
36412         el.unselectable = "on";
36413         el.href = this.href || "#";
36414         if(this.hrefTarget){
36415             el.target = this.hrefTarget;
36416         }
36417         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36418         
36419         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36420         
36421         el.innerHTML = String.format(
36422                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36423                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36424         this.el = el;
36425         Roo.menu.Item.superclass.onRender.call(this, container, position);
36426     },
36427
36428     /**
36429      * Sets the text to display in this menu item
36430      * @param {String} text The text to display
36431      * @param {Boolean} isHTML true to indicate text is pure html.
36432      */
36433     setText : function(text, isHTML){
36434         if (isHTML) {
36435             this.html = text;
36436         } else {
36437             this.text = text;
36438             this.html = '';
36439         }
36440         if(this.rendered){
36441             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36442      
36443             this.el.update(String.format(
36444                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36445                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36446             this.parentMenu.autoWidth();
36447         }
36448     },
36449
36450     // private
36451     handleClick : function(e){
36452         if(!this.href){ // if no link defined, stop the event automatically
36453             e.stopEvent();
36454         }
36455         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36456     },
36457
36458     // private
36459     activate : function(autoExpand){
36460         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36461             this.focus();
36462             if(autoExpand){
36463                 this.expandMenu();
36464             }
36465         }
36466         return true;
36467     },
36468
36469     // private
36470     shouldDeactivate : function(e){
36471         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36472             if(this.menu && this.menu.isVisible()){
36473                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36474             }
36475             return true;
36476         }
36477         return false;
36478     },
36479
36480     // private
36481     deactivate : function(){
36482         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36483         this.hideMenu();
36484     },
36485
36486     // private
36487     expandMenu : function(autoActivate){
36488         if(!this.disabled && this.menu){
36489             clearTimeout(this.hideTimer);
36490             delete this.hideTimer;
36491             if(!this.menu.isVisible() && !this.showTimer){
36492                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36493             }else if (this.menu.isVisible() && autoActivate){
36494                 this.menu.tryActivate(0, 1);
36495             }
36496         }
36497     },
36498
36499     // private
36500     deferExpand : function(autoActivate){
36501         delete this.showTimer;
36502         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36503         if(autoActivate){
36504             this.menu.tryActivate(0, 1);
36505         }
36506     },
36507
36508     // private
36509     hideMenu : function(){
36510         clearTimeout(this.showTimer);
36511         delete this.showTimer;
36512         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36513             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36514         }
36515     },
36516
36517     // private
36518     deferHide : function(){
36519         delete this.hideTimer;
36520         this.menu.hide();
36521     }
36522 });/*
36523  * Based on:
36524  * Ext JS Library 1.1.1
36525  * Copyright(c) 2006-2007, Ext JS, LLC.
36526  *
36527  * Originally Released Under LGPL - original licence link has changed is not relivant.
36528  *
36529  * Fork - LGPL
36530  * <script type="text/javascript">
36531  */
36532  
36533 /**
36534  * @class Roo.menu.CheckItem
36535  * @extends Roo.menu.Item
36536  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36537  * @constructor
36538  * Creates a new CheckItem
36539  * @param {Object} config Configuration options
36540  */
36541 Roo.menu.CheckItem = function(config){
36542     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36543     this.addEvents({
36544         /**
36545          * @event beforecheckchange
36546          * Fires before the checked value is set, providing an opportunity to cancel if needed
36547          * @param {Roo.menu.CheckItem} this
36548          * @param {Boolean} checked The new checked value that will be set
36549          */
36550         "beforecheckchange" : true,
36551         /**
36552          * @event checkchange
36553          * Fires after the checked value has been set
36554          * @param {Roo.menu.CheckItem} this
36555          * @param {Boolean} checked The checked value that was set
36556          */
36557         "checkchange" : true
36558     });
36559     if(this.checkHandler){
36560         this.on('checkchange', this.checkHandler, this.scope);
36561     }
36562 };
36563 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36564     /**
36565      * @cfg {String} group
36566      * All check items with the same group name will automatically be grouped into a single-select
36567      * radio button group (defaults to '')
36568      */
36569     /**
36570      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36571      */
36572     itemCls : "x-menu-item x-menu-check-item",
36573     /**
36574      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36575      */
36576     groupClass : "x-menu-group-item",
36577
36578     /**
36579      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36580      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36581      * initialized with checked = true will be rendered as checked.
36582      */
36583     checked: false,
36584
36585     // private
36586     ctype: "Roo.menu.CheckItem",
36587
36588     // private
36589     onRender : function(c){
36590         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36591         if(this.group){
36592             this.el.addClass(this.groupClass);
36593         }
36594         Roo.menu.MenuMgr.registerCheckable(this);
36595         if(this.checked){
36596             this.checked = false;
36597             this.setChecked(true, true);
36598         }
36599     },
36600
36601     // private
36602     destroy : function(){
36603         if(this.rendered){
36604             Roo.menu.MenuMgr.unregisterCheckable(this);
36605         }
36606         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36607     },
36608
36609     /**
36610      * Set the checked state of this item
36611      * @param {Boolean} checked The new checked value
36612      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36613      */
36614     setChecked : function(state, suppressEvent){
36615         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36616             if(this.container){
36617                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36618             }
36619             this.checked = state;
36620             if(suppressEvent !== true){
36621                 this.fireEvent("checkchange", this, state);
36622             }
36623         }
36624     },
36625
36626     // private
36627     handleClick : function(e){
36628        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36629            this.setChecked(!this.checked);
36630        }
36631        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36632     }
36633 });/*
36634  * Based on:
36635  * Ext JS Library 1.1.1
36636  * Copyright(c) 2006-2007, Ext JS, LLC.
36637  *
36638  * Originally Released Under LGPL - original licence link has changed is not relivant.
36639  *
36640  * Fork - LGPL
36641  * <script type="text/javascript">
36642  */
36643  
36644 /**
36645  * @class Roo.menu.DateItem
36646  * @extends Roo.menu.Adapter
36647  * A menu item that wraps the {@link Roo.DatPicker} component.
36648  * @constructor
36649  * Creates a new DateItem
36650  * @param {Object} config Configuration options
36651  */
36652 Roo.menu.DateItem = function(config){
36653     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36654     /** The Roo.DatePicker object @type Roo.DatePicker */
36655     this.picker = this.component;
36656     this.addEvents({select: true});
36657     
36658     this.picker.on("render", function(picker){
36659         picker.getEl().swallowEvent("click");
36660         picker.container.addClass("x-menu-date-item");
36661     });
36662
36663     this.picker.on("select", this.onSelect, this);
36664 };
36665
36666 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36667     // private
36668     onSelect : function(picker, date){
36669         this.fireEvent("select", this, date, picker);
36670         Roo.menu.DateItem.superclass.handleClick.call(this);
36671     }
36672 });/*
36673  * Based on:
36674  * Ext JS Library 1.1.1
36675  * Copyright(c) 2006-2007, Ext JS, LLC.
36676  *
36677  * Originally Released Under LGPL - original licence link has changed is not relivant.
36678  *
36679  * Fork - LGPL
36680  * <script type="text/javascript">
36681  */
36682  
36683 /**
36684  * @class Roo.menu.ColorItem
36685  * @extends Roo.menu.Adapter
36686  * A menu item that wraps the {@link Roo.ColorPalette} component.
36687  * @constructor
36688  * Creates a new ColorItem
36689  * @param {Object} config Configuration options
36690  */
36691 Roo.menu.ColorItem = function(config){
36692     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36693     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36694     this.palette = this.component;
36695     this.relayEvents(this.palette, ["select"]);
36696     if(this.selectHandler){
36697         this.on('select', this.selectHandler, this.scope);
36698     }
36699 };
36700 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36701  * Based on:
36702  * Ext JS Library 1.1.1
36703  * Copyright(c) 2006-2007, Ext JS, LLC.
36704  *
36705  * Originally Released Under LGPL - original licence link has changed is not relivant.
36706  *
36707  * Fork - LGPL
36708  * <script type="text/javascript">
36709  */
36710  
36711
36712 /**
36713  * @class Roo.menu.DateMenu
36714  * @extends Roo.menu.Menu
36715  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36716  * @constructor
36717  * Creates a new DateMenu
36718  * @param {Object} config Configuration options
36719  */
36720 Roo.menu.DateMenu = function(config){
36721     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36722     this.plain = true;
36723     var di = new Roo.menu.DateItem(config);
36724     this.add(di);
36725     /**
36726      * The {@link Roo.DatePicker} instance for this DateMenu
36727      * @type DatePicker
36728      */
36729     this.picker = di.picker;
36730     /**
36731      * @event select
36732      * @param {DatePicker} picker
36733      * @param {Date} date
36734      */
36735     this.relayEvents(di, ["select"]);
36736     this.on('beforeshow', function(){
36737         if(this.picker){
36738             this.picker.hideMonthPicker(false);
36739         }
36740     }, this);
36741 };
36742 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36743     cls:'x-date-menu'
36744 });/*
36745  * Based on:
36746  * Ext JS Library 1.1.1
36747  * Copyright(c) 2006-2007, Ext JS, LLC.
36748  *
36749  * Originally Released Under LGPL - original licence link has changed is not relivant.
36750  *
36751  * Fork - LGPL
36752  * <script type="text/javascript">
36753  */
36754  
36755
36756 /**
36757  * @class Roo.menu.ColorMenu
36758  * @extends Roo.menu.Menu
36759  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36760  * @constructor
36761  * Creates a new ColorMenu
36762  * @param {Object} config Configuration options
36763  */
36764 Roo.menu.ColorMenu = function(config){
36765     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36766     this.plain = true;
36767     var ci = new Roo.menu.ColorItem(config);
36768     this.add(ci);
36769     /**
36770      * The {@link Roo.ColorPalette} instance for this ColorMenu
36771      * @type ColorPalette
36772      */
36773     this.palette = ci.palette;
36774     /**
36775      * @event select
36776      * @param {ColorPalette} palette
36777      * @param {String} color
36778      */
36779     this.relayEvents(ci, ["select"]);
36780 };
36781 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36782  * Based on:
36783  * Ext JS Library 1.1.1
36784  * Copyright(c) 2006-2007, Ext JS, LLC.
36785  *
36786  * Originally Released Under LGPL - original licence link has changed is not relivant.
36787  *
36788  * Fork - LGPL
36789  * <script type="text/javascript">
36790  */
36791  
36792 /**
36793  * @class Roo.form.Field
36794  * @extends Roo.BoxComponent
36795  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36796  * @constructor
36797  * Creates a new Field
36798  * @param {Object} config Configuration options
36799  */
36800 Roo.form.Field = function(config){
36801     Roo.form.Field.superclass.constructor.call(this, config);
36802 };
36803
36804 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36805     /**
36806      * @cfg {String} fieldLabel Label to use when rendering a form.
36807      */
36808        /**
36809      * @cfg {String} qtip Mouse over tip
36810      */
36811      
36812     /**
36813      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36814      */
36815     invalidClass : "x-form-invalid",
36816     /**
36817      * @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")
36818      */
36819     invalidText : "The value in this field is invalid",
36820     /**
36821      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36822      */
36823     focusClass : "x-form-focus",
36824     /**
36825      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36826       automatic validation (defaults to "keyup").
36827      */
36828     validationEvent : "keyup",
36829     /**
36830      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36831      */
36832     validateOnBlur : true,
36833     /**
36834      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36835      */
36836     validationDelay : 250,
36837     /**
36838      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36839      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36840      */
36841     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36842     /**
36843      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36844      */
36845     fieldClass : "x-form-field",
36846     /**
36847      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36848      *<pre>
36849 Value         Description
36850 -----------   ----------------------------------------------------------------------
36851 qtip          Display a quick tip when the user hovers over the field
36852 title         Display a default browser title attribute popup
36853 under         Add a block div beneath the field containing the error text
36854 side          Add an error icon to the right of the field with a popup on hover
36855 [element id]  Add the error text directly to the innerHTML of the specified element
36856 </pre>
36857      */
36858     msgTarget : 'qtip',
36859     /**
36860      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36861      */
36862     msgFx : 'normal',
36863
36864     /**
36865      * @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.
36866      */
36867     readOnly : false,
36868
36869     /**
36870      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36871      */
36872     disabled : false,
36873
36874     /**
36875      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36876      */
36877     inputType : undefined,
36878     
36879     /**
36880      * @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).
36881          */
36882         tabIndex : undefined,
36883         
36884     // private
36885     isFormField : true,
36886
36887     // private
36888     hasFocus : false,
36889     /**
36890      * @property {Roo.Element} fieldEl
36891      * Element Containing the rendered Field (with label etc.)
36892      */
36893     /**
36894      * @cfg {Mixed} value A value to initialize this field with.
36895      */
36896     value : undefined,
36897
36898     /**
36899      * @cfg {String} name The field's HTML name attribute.
36900      */
36901     /**
36902      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36903      */
36904
36905         // private ??
36906         initComponent : function(){
36907         Roo.form.Field.superclass.initComponent.call(this);
36908         this.addEvents({
36909             /**
36910              * @event focus
36911              * Fires when this field receives input focus.
36912              * @param {Roo.form.Field} this
36913              */
36914             focus : true,
36915             /**
36916              * @event blur
36917              * Fires when this field loses input focus.
36918              * @param {Roo.form.Field} this
36919              */
36920             blur : true,
36921             /**
36922              * @event specialkey
36923              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36924              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36925              * @param {Roo.form.Field} this
36926              * @param {Roo.EventObject} e The event object
36927              */
36928             specialkey : true,
36929             /**
36930              * @event change
36931              * Fires just before the field blurs if the field value has changed.
36932              * @param {Roo.form.Field} this
36933              * @param {Mixed} newValue The new value
36934              * @param {Mixed} oldValue The original value
36935              */
36936             change : true,
36937             /**
36938              * @event invalid
36939              * Fires after the field has been marked as invalid.
36940              * @param {Roo.form.Field} this
36941              * @param {String} msg The validation message
36942              */
36943             invalid : true,
36944             /**
36945              * @event valid
36946              * Fires after the field has been validated with no errors.
36947              * @param {Roo.form.Field} this
36948              */
36949             valid : true,
36950              /**
36951              * @event keyup
36952              * Fires after the key up
36953              * @param {Roo.form.Field} this
36954              * @param {Roo.EventObject}  e The event Object
36955              */
36956             keyup : true
36957         });
36958     },
36959
36960     /**
36961      * Returns the name attribute of the field if available
36962      * @return {String} name The field name
36963      */
36964     getName: function(){
36965          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36966     },
36967
36968     // private
36969     onRender : function(ct, position){
36970         Roo.form.Field.superclass.onRender.call(this, ct, position);
36971         if(!this.el){
36972             var cfg = this.getAutoCreate();
36973             if(!cfg.name){
36974                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36975             }
36976             if (!cfg.name.length) {
36977                 delete cfg.name;
36978             }
36979             if(this.inputType){
36980                 cfg.type = this.inputType;
36981             }
36982             this.el = ct.createChild(cfg, position);
36983         }
36984         var type = this.el.dom.type;
36985         if(type){
36986             if(type == 'password'){
36987                 type = 'text';
36988             }
36989             this.el.addClass('x-form-'+type);
36990         }
36991         if(this.readOnly){
36992             this.el.dom.readOnly = true;
36993         }
36994         if(this.tabIndex !== undefined){
36995             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36996         }
36997
36998         this.el.addClass([this.fieldClass, this.cls]);
36999         this.initValue();
37000     },
37001
37002     /**
37003      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37004      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37005      * @return {Roo.form.Field} this
37006      */
37007     applyTo : function(target){
37008         this.allowDomMove = false;
37009         this.el = Roo.get(target);
37010         this.render(this.el.dom.parentNode);
37011         return this;
37012     },
37013
37014     // private
37015     initValue : function(){
37016         if(this.value !== undefined){
37017             this.setValue(this.value);
37018         }else if(this.el.dom.value.length > 0){
37019             this.setValue(this.el.dom.value);
37020         }
37021     },
37022
37023     /**
37024      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37025      */
37026     isDirty : function() {
37027         if(this.disabled) {
37028             return false;
37029         }
37030         return String(this.getValue()) !== String(this.originalValue);
37031     },
37032
37033     // private
37034     afterRender : function(){
37035         Roo.form.Field.superclass.afterRender.call(this);
37036         this.initEvents();
37037     },
37038
37039     // private
37040     fireKey : function(e){
37041         //Roo.log('field ' + e.getKey());
37042         if(e.isNavKeyPress()){
37043             this.fireEvent("specialkey", this, e);
37044         }
37045     },
37046
37047     /**
37048      * Resets the current field value to the originally loaded value and clears any validation messages
37049      */
37050     reset : function(){
37051         this.setValue(this.resetValue);
37052         this.clearInvalid();
37053     },
37054
37055     // private
37056     initEvents : function(){
37057         // safari killled keypress - so keydown is now used..
37058         this.el.on("keydown" , this.fireKey,  this);
37059         this.el.on("focus", this.onFocus,  this);
37060         this.el.on("blur", this.onBlur,  this);
37061         this.el.relayEvent('keyup', this);
37062
37063         // reference to original value for reset
37064         this.originalValue = this.getValue();
37065         this.resetValue =  this.getValue();
37066     },
37067
37068     // private
37069     onFocus : function(){
37070         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37071             this.el.addClass(this.focusClass);
37072         }
37073         if(!this.hasFocus){
37074             this.hasFocus = true;
37075             this.startValue = this.getValue();
37076             this.fireEvent("focus", this);
37077         }
37078     },
37079
37080     beforeBlur : Roo.emptyFn,
37081
37082     // private
37083     onBlur : function(){
37084         this.beforeBlur();
37085         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37086             this.el.removeClass(this.focusClass);
37087         }
37088         this.hasFocus = false;
37089         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37090             this.validate();
37091         }
37092         var v = this.getValue();
37093         if(String(v) !== String(this.startValue)){
37094             this.fireEvent('change', this, v, this.startValue);
37095         }
37096         this.fireEvent("blur", this);
37097     },
37098
37099     /**
37100      * Returns whether or not the field value is currently valid
37101      * @param {Boolean} preventMark True to disable marking the field invalid
37102      * @return {Boolean} True if the value is valid, else false
37103      */
37104     isValid : function(preventMark){
37105         if(this.disabled){
37106             return true;
37107         }
37108         var restore = this.preventMark;
37109         this.preventMark = preventMark === true;
37110         var v = this.validateValue(this.processValue(this.getRawValue()));
37111         this.preventMark = restore;
37112         return v;
37113     },
37114
37115     /**
37116      * Validates the field value
37117      * @return {Boolean} True if the value is valid, else false
37118      */
37119     validate : function(){
37120         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37121             this.clearInvalid();
37122             return true;
37123         }
37124         return false;
37125     },
37126
37127     processValue : function(value){
37128         return value;
37129     },
37130
37131     // private
37132     // Subclasses should provide the validation implementation by overriding this
37133     validateValue : function(value){
37134         return true;
37135     },
37136
37137     /**
37138      * Mark this field as invalid
37139      * @param {String} msg The validation message
37140      */
37141     markInvalid : function(msg){
37142         if(!this.rendered || this.preventMark){ // not rendered
37143             return;
37144         }
37145         
37146         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37147         
37148         obj.el.addClass(this.invalidClass);
37149         msg = msg || this.invalidText;
37150         switch(this.msgTarget){
37151             case 'qtip':
37152                 obj.el.dom.qtip = msg;
37153                 obj.el.dom.qclass = 'x-form-invalid-tip';
37154                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37155                     Roo.QuickTips.enable();
37156                 }
37157                 break;
37158             case 'title':
37159                 this.el.dom.title = msg;
37160                 break;
37161             case 'under':
37162                 if(!this.errorEl){
37163                     var elp = this.el.findParent('.x-form-element', 5, true);
37164                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37165                     this.errorEl.setWidth(elp.getWidth(true)-20);
37166                 }
37167                 this.errorEl.update(msg);
37168                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37169                 break;
37170             case 'side':
37171                 if(!this.errorIcon){
37172                     var elp = this.el.findParent('.x-form-element', 5, true);
37173                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37174                 }
37175                 this.alignErrorIcon();
37176                 this.errorIcon.dom.qtip = msg;
37177                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37178                 this.errorIcon.show();
37179                 this.on('resize', this.alignErrorIcon, this);
37180                 break;
37181             default:
37182                 var t = Roo.getDom(this.msgTarget);
37183                 t.innerHTML = msg;
37184                 t.style.display = this.msgDisplay;
37185                 break;
37186         }
37187         this.fireEvent('invalid', this, msg);
37188     },
37189
37190     // private
37191     alignErrorIcon : function(){
37192         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37193     },
37194
37195     /**
37196      * Clear any invalid styles/messages for this field
37197      */
37198     clearInvalid : function(){
37199         if(!this.rendered || this.preventMark){ // not rendered
37200             return;
37201         }
37202         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37203         
37204         obj.el.removeClass(this.invalidClass);
37205         switch(this.msgTarget){
37206             case 'qtip':
37207                 obj.el.dom.qtip = '';
37208                 break;
37209             case 'title':
37210                 this.el.dom.title = '';
37211                 break;
37212             case 'under':
37213                 if(this.errorEl){
37214                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37215                 }
37216                 break;
37217             case 'side':
37218                 if(this.errorIcon){
37219                     this.errorIcon.dom.qtip = '';
37220                     this.errorIcon.hide();
37221                     this.un('resize', this.alignErrorIcon, this);
37222                 }
37223                 break;
37224             default:
37225                 var t = Roo.getDom(this.msgTarget);
37226                 t.innerHTML = '';
37227                 t.style.display = 'none';
37228                 break;
37229         }
37230         this.fireEvent('valid', this);
37231     },
37232
37233     /**
37234      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37235      * @return {Mixed} value The field value
37236      */
37237     getRawValue : function(){
37238         var v = this.el.getValue();
37239         
37240         return v;
37241     },
37242
37243     /**
37244      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37245      * @return {Mixed} value The field value
37246      */
37247     getValue : function(){
37248         var v = this.el.getValue();
37249          
37250         return v;
37251     },
37252
37253     /**
37254      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37255      * @param {Mixed} value The value to set
37256      */
37257     setRawValue : function(v){
37258         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37259     },
37260
37261     /**
37262      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37263      * @param {Mixed} value The value to set
37264      */
37265     setValue : function(v){
37266         this.value = v;
37267         if(this.rendered){
37268             this.el.dom.value = (v === null || v === undefined ? '' : v);
37269              this.validate();
37270         }
37271     },
37272
37273     adjustSize : function(w, h){
37274         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37275         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37276         return s;
37277     },
37278
37279     adjustWidth : function(tag, w){
37280         tag = tag.toLowerCase();
37281         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37282             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37283                 if(tag == 'input'){
37284                     return w + 2;
37285                 }
37286                 if(tag == 'textarea'){
37287                     return w-2;
37288                 }
37289             }else if(Roo.isOpera){
37290                 if(tag == 'input'){
37291                     return w + 2;
37292                 }
37293                 if(tag == 'textarea'){
37294                     return w-2;
37295                 }
37296             }
37297         }
37298         return w;
37299     }
37300 });
37301
37302
37303 // anything other than normal should be considered experimental
37304 Roo.form.Field.msgFx = {
37305     normal : {
37306         show: function(msgEl, f){
37307             msgEl.setDisplayed('block');
37308         },
37309
37310         hide : function(msgEl, f){
37311             msgEl.setDisplayed(false).update('');
37312         }
37313     },
37314
37315     slide : {
37316         show: function(msgEl, f){
37317             msgEl.slideIn('t', {stopFx:true});
37318         },
37319
37320         hide : function(msgEl, f){
37321             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37322         }
37323     },
37324
37325     slideRight : {
37326         show: function(msgEl, f){
37327             msgEl.fixDisplay();
37328             msgEl.alignTo(f.el, 'tl-tr');
37329             msgEl.slideIn('l', {stopFx:true});
37330         },
37331
37332         hide : function(msgEl, f){
37333             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37334         }
37335     }
37336 };/*
37337  * Based on:
37338  * Ext JS Library 1.1.1
37339  * Copyright(c) 2006-2007, Ext JS, LLC.
37340  *
37341  * Originally Released Under LGPL - original licence link has changed is not relivant.
37342  *
37343  * Fork - LGPL
37344  * <script type="text/javascript">
37345  */
37346  
37347
37348 /**
37349  * @class Roo.form.TextField
37350  * @extends Roo.form.Field
37351  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37352  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37353  * @constructor
37354  * Creates a new TextField
37355  * @param {Object} config Configuration options
37356  */
37357 Roo.form.TextField = function(config){
37358     Roo.form.TextField.superclass.constructor.call(this, config);
37359     this.addEvents({
37360         /**
37361          * @event autosize
37362          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37363          * according to the default logic, but this event provides a hook for the developer to apply additional
37364          * logic at runtime to resize the field if needed.
37365              * @param {Roo.form.Field} this This text field
37366              * @param {Number} width The new field width
37367              */
37368         autosize : true
37369     });
37370 };
37371
37372 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37373     /**
37374      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37375      */
37376     grow : false,
37377     /**
37378      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37379      */
37380     growMin : 30,
37381     /**
37382      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37383      */
37384     growMax : 800,
37385     /**
37386      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37387      */
37388     vtype : null,
37389     /**
37390      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37391      */
37392     maskRe : null,
37393     /**
37394      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37395      */
37396     disableKeyFilter : false,
37397     /**
37398      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37399      */
37400     allowBlank : true,
37401     /**
37402      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37403      */
37404     minLength : 0,
37405     /**
37406      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37407      */
37408     maxLength : Number.MAX_VALUE,
37409     /**
37410      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37411      */
37412     minLengthText : "The minimum length for this field is {0}",
37413     /**
37414      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37415      */
37416     maxLengthText : "The maximum length for this field is {0}",
37417     /**
37418      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37419      */
37420     selectOnFocus : false,
37421     /**
37422      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37423      */
37424     blankText : "This field is required",
37425     /**
37426      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37427      * If available, this function will be called only after the basic validators all return true, and will be passed the
37428      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37429      */
37430     validator : null,
37431     /**
37432      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37433      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37434      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37435      */
37436     regex : null,
37437     /**
37438      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37439      */
37440     regexText : "",
37441     /**
37442      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37443      */
37444     emptyText : null,
37445    
37446
37447     // private
37448     initEvents : function()
37449     {
37450         if (this.emptyText) {
37451             this.el.attr('placeholder', this.emptyText);
37452         }
37453         
37454         Roo.form.TextField.superclass.initEvents.call(this);
37455         if(this.validationEvent == 'keyup'){
37456             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37457             this.el.on('keyup', this.filterValidation, this);
37458         }
37459         else if(this.validationEvent !== false){
37460             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37461         }
37462         
37463         if(this.selectOnFocus){
37464             this.on("focus", this.preFocus, this);
37465             
37466         }
37467         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37468             this.el.on("keypress", this.filterKeys, this);
37469         }
37470         if(this.grow){
37471             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37472             this.el.on("click", this.autoSize,  this);
37473         }
37474         if(this.el.is('input[type=password]') && Roo.isSafari){
37475             this.el.on('keydown', this.SafariOnKeyDown, this);
37476         }
37477     },
37478
37479     processValue : function(value){
37480         if(this.stripCharsRe){
37481             var newValue = value.replace(this.stripCharsRe, '');
37482             if(newValue !== value){
37483                 this.setRawValue(newValue);
37484                 return newValue;
37485             }
37486         }
37487         return value;
37488     },
37489
37490     filterValidation : function(e){
37491         if(!e.isNavKeyPress()){
37492             this.validationTask.delay(this.validationDelay);
37493         }
37494     },
37495
37496     // private
37497     onKeyUp : function(e){
37498         if(!e.isNavKeyPress()){
37499             this.autoSize();
37500         }
37501     },
37502
37503     /**
37504      * Resets the current field value to the originally-loaded value and clears any validation messages.
37505      *  
37506      */
37507     reset : function(){
37508         Roo.form.TextField.superclass.reset.call(this);
37509        
37510     },
37511
37512     
37513     // private
37514     preFocus : function(){
37515         
37516         if(this.selectOnFocus){
37517             this.el.dom.select();
37518         }
37519     },
37520
37521     
37522     // private
37523     filterKeys : function(e){
37524         var k = e.getKey();
37525         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37526             return;
37527         }
37528         var c = e.getCharCode(), cc = String.fromCharCode(c);
37529         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37530             return;
37531         }
37532         if(!this.maskRe.test(cc)){
37533             e.stopEvent();
37534         }
37535     },
37536
37537     setValue : function(v){
37538         
37539         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37540         
37541         this.autoSize();
37542     },
37543
37544     /**
37545      * Validates a value according to the field's validation rules and marks the field as invalid
37546      * if the validation fails
37547      * @param {Mixed} value The value to validate
37548      * @return {Boolean} True if the value is valid, else false
37549      */
37550     validateValue : function(value){
37551         if(value.length < 1)  { // if it's blank
37552              if(this.allowBlank){
37553                 this.clearInvalid();
37554                 return true;
37555              }else{
37556                 this.markInvalid(this.blankText);
37557                 return false;
37558              }
37559         }
37560         if(value.length < this.minLength){
37561             this.markInvalid(String.format(this.minLengthText, this.minLength));
37562             return false;
37563         }
37564         if(value.length > this.maxLength){
37565             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37566             return false;
37567         }
37568         if(this.vtype){
37569             var vt = Roo.form.VTypes;
37570             if(!vt[this.vtype](value, this)){
37571                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37572                 return false;
37573             }
37574         }
37575         if(typeof this.validator == "function"){
37576             var msg = this.validator(value);
37577             if(msg !== true){
37578                 this.markInvalid(msg);
37579                 return false;
37580             }
37581         }
37582         if(this.regex && !this.regex.test(value)){
37583             this.markInvalid(this.regexText);
37584             return false;
37585         }
37586         return true;
37587     },
37588
37589     /**
37590      * Selects text in this field
37591      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37592      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37593      */
37594     selectText : function(start, end){
37595         var v = this.getRawValue();
37596         if(v.length > 0){
37597             start = start === undefined ? 0 : start;
37598             end = end === undefined ? v.length : end;
37599             var d = this.el.dom;
37600             if(d.setSelectionRange){
37601                 d.setSelectionRange(start, end);
37602             }else if(d.createTextRange){
37603                 var range = d.createTextRange();
37604                 range.moveStart("character", start);
37605                 range.moveEnd("character", v.length-end);
37606                 range.select();
37607             }
37608         }
37609     },
37610
37611     /**
37612      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37613      * This only takes effect if grow = true, and fires the autosize event.
37614      */
37615     autoSize : function(){
37616         if(!this.grow || !this.rendered){
37617             return;
37618         }
37619         if(!this.metrics){
37620             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37621         }
37622         var el = this.el;
37623         var v = el.dom.value;
37624         var d = document.createElement('div');
37625         d.appendChild(document.createTextNode(v));
37626         v = d.innerHTML;
37627         d = null;
37628         v += "&#160;";
37629         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37630         this.el.setWidth(w);
37631         this.fireEvent("autosize", this, w);
37632     },
37633     
37634     // private
37635     SafariOnKeyDown : function(event)
37636     {
37637         // this is a workaround for a password hang bug on chrome/ webkit.
37638         
37639         var isSelectAll = false;
37640         
37641         if(this.el.dom.selectionEnd > 0){
37642             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37643         }
37644         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37645             event.preventDefault();
37646             this.setValue('');
37647             return;
37648         }
37649         
37650         if(isSelectAll){ // backspace and delete key
37651             
37652             event.preventDefault();
37653             // this is very hacky as keydown always get's upper case.
37654             //
37655             var cc = String.fromCharCode(event.getCharCode());
37656             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37657             
37658         }
37659         
37660         
37661     }
37662 });/*
37663  * Based on:
37664  * Ext JS Library 1.1.1
37665  * Copyright(c) 2006-2007, Ext JS, LLC.
37666  *
37667  * Originally Released Under LGPL - original licence link has changed is not relivant.
37668  *
37669  * Fork - LGPL
37670  * <script type="text/javascript">
37671  */
37672  
37673 /**
37674  * @class Roo.form.Hidden
37675  * @extends Roo.form.TextField
37676  * Simple Hidden element used on forms 
37677  * 
37678  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37679  * 
37680  * @constructor
37681  * Creates a new Hidden form element.
37682  * @param {Object} config Configuration options
37683  */
37684
37685
37686
37687 // easy hidden field...
37688 Roo.form.Hidden = function(config){
37689     Roo.form.Hidden.superclass.constructor.call(this, config);
37690 };
37691   
37692 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37693     fieldLabel:      '',
37694     inputType:      'hidden',
37695     width:          50,
37696     allowBlank:     true,
37697     labelSeparator: '',
37698     hidden:         true,
37699     itemCls :       'x-form-item-display-none'
37700
37701
37702 });
37703
37704
37705 /*
37706  * Based on:
37707  * Ext JS Library 1.1.1
37708  * Copyright(c) 2006-2007, Ext JS, LLC.
37709  *
37710  * Originally Released Under LGPL - original licence link has changed is not relivant.
37711  *
37712  * Fork - LGPL
37713  * <script type="text/javascript">
37714  */
37715  
37716 /**
37717  * @class Roo.form.TriggerField
37718  * @extends Roo.form.TextField
37719  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37720  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37721  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37722  * for which you can provide a custom implementation.  For example:
37723  * <pre><code>
37724 var trigger = new Roo.form.TriggerField();
37725 trigger.onTriggerClick = myTriggerFn;
37726 trigger.applyTo('my-field');
37727 </code></pre>
37728  *
37729  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37730  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37731  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37732  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37733  * @constructor
37734  * Create a new TriggerField.
37735  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37736  * to the base TextField)
37737  */
37738 Roo.form.TriggerField = function(config){
37739     this.mimicing = false;
37740     Roo.form.TriggerField.superclass.constructor.call(this, config);
37741 };
37742
37743 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37744     /**
37745      * @cfg {String} triggerClass A CSS class to apply to the trigger
37746      */
37747     /**
37748      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37749      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37750      */
37751     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37752     /**
37753      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37754      */
37755     hideTrigger:false,
37756
37757     /** @cfg {Boolean} grow @hide */
37758     /** @cfg {Number} growMin @hide */
37759     /** @cfg {Number} growMax @hide */
37760
37761     /**
37762      * @hide 
37763      * @method
37764      */
37765     autoSize: Roo.emptyFn,
37766     // private
37767     monitorTab : true,
37768     // private
37769     deferHeight : true,
37770
37771     
37772     actionMode : 'wrap',
37773     // private
37774     onResize : function(w, h){
37775         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37776         if(typeof w == 'number'){
37777             var x = w - this.trigger.getWidth();
37778             this.el.setWidth(this.adjustWidth('input', x));
37779             this.trigger.setStyle('left', x+'px');
37780         }
37781     },
37782
37783     // private
37784     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37785
37786     // private
37787     getResizeEl : function(){
37788         return this.wrap;
37789     },
37790
37791     // private
37792     getPositionEl : function(){
37793         return this.wrap;
37794     },
37795
37796     // private
37797     alignErrorIcon : function(){
37798         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37799     },
37800
37801     // private
37802     onRender : function(ct, position){
37803         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37804         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37805         this.trigger = this.wrap.createChild(this.triggerConfig ||
37806                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37807         if(this.hideTrigger){
37808             this.trigger.setDisplayed(false);
37809         }
37810         this.initTrigger();
37811         if(!this.width){
37812             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37813         }
37814     },
37815
37816     // private
37817     initTrigger : function(){
37818         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37819         this.trigger.addClassOnOver('x-form-trigger-over');
37820         this.trigger.addClassOnClick('x-form-trigger-click');
37821     },
37822
37823     // private
37824     onDestroy : function(){
37825         if(this.trigger){
37826             this.trigger.removeAllListeners();
37827             this.trigger.remove();
37828         }
37829         if(this.wrap){
37830             this.wrap.remove();
37831         }
37832         Roo.form.TriggerField.superclass.onDestroy.call(this);
37833     },
37834
37835     // private
37836     onFocus : function(){
37837         Roo.form.TriggerField.superclass.onFocus.call(this);
37838         if(!this.mimicing){
37839             this.wrap.addClass('x-trigger-wrap-focus');
37840             this.mimicing = true;
37841             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37842             if(this.monitorTab){
37843                 this.el.on("keydown", this.checkTab, this);
37844             }
37845         }
37846     },
37847
37848     // private
37849     checkTab : function(e){
37850         if(e.getKey() == e.TAB){
37851             this.triggerBlur();
37852         }
37853     },
37854
37855     // private
37856     onBlur : function(){
37857         // do nothing
37858     },
37859
37860     // private
37861     mimicBlur : function(e, t){
37862         if(!this.wrap.contains(t) && this.validateBlur()){
37863             this.triggerBlur();
37864         }
37865     },
37866
37867     // private
37868     triggerBlur : function(){
37869         this.mimicing = false;
37870         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37871         if(this.monitorTab){
37872             this.el.un("keydown", this.checkTab, this);
37873         }
37874         this.wrap.removeClass('x-trigger-wrap-focus');
37875         Roo.form.TriggerField.superclass.onBlur.call(this);
37876     },
37877
37878     // private
37879     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37880     validateBlur : function(e, t){
37881         return true;
37882     },
37883
37884     // private
37885     onDisable : function(){
37886         Roo.form.TriggerField.superclass.onDisable.call(this);
37887         if(this.wrap){
37888             this.wrap.addClass('x-item-disabled');
37889         }
37890     },
37891
37892     // private
37893     onEnable : function(){
37894         Roo.form.TriggerField.superclass.onEnable.call(this);
37895         if(this.wrap){
37896             this.wrap.removeClass('x-item-disabled');
37897         }
37898     },
37899
37900     // private
37901     onShow : function(){
37902         var ae = this.getActionEl();
37903         
37904         if(ae){
37905             ae.dom.style.display = '';
37906             ae.dom.style.visibility = 'visible';
37907         }
37908     },
37909
37910     // private
37911     
37912     onHide : function(){
37913         var ae = this.getActionEl();
37914         ae.dom.style.display = 'none';
37915     },
37916
37917     /**
37918      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37919      * by an implementing function.
37920      * @method
37921      * @param {EventObject} e
37922      */
37923     onTriggerClick : Roo.emptyFn
37924 });
37925
37926 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37927 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37928 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37929 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37930     initComponent : function(){
37931         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37932
37933         this.triggerConfig = {
37934             tag:'span', cls:'x-form-twin-triggers', cn:[
37935             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37936             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37937         ]};
37938     },
37939
37940     getTrigger : function(index){
37941         return this.triggers[index];
37942     },
37943
37944     initTrigger : function(){
37945         var ts = this.trigger.select('.x-form-trigger', true);
37946         this.wrap.setStyle('overflow', 'hidden');
37947         var triggerField = this;
37948         ts.each(function(t, all, index){
37949             t.hide = function(){
37950                 var w = triggerField.wrap.getWidth();
37951                 this.dom.style.display = 'none';
37952                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37953             };
37954             t.show = function(){
37955                 var w = triggerField.wrap.getWidth();
37956                 this.dom.style.display = '';
37957                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37958             };
37959             var triggerIndex = 'Trigger'+(index+1);
37960
37961             if(this['hide'+triggerIndex]){
37962                 t.dom.style.display = 'none';
37963             }
37964             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37965             t.addClassOnOver('x-form-trigger-over');
37966             t.addClassOnClick('x-form-trigger-click');
37967         }, this);
37968         this.triggers = ts.elements;
37969     },
37970
37971     onTrigger1Click : Roo.emptyFn,
37972     onTrigger2Click : Roo.emptyFn
37973 });/*
37974  * Based on:
37975  * Ext JS Library 1.1.1
37976  * Copyright(c) 2006-2007, Ext JS, LLC.
37977  *
37978  * Originally Released Under LGPL - original licence link has changed is not relivant.
37979  *
37980  * Fork - LGPL
37981  * <script type="text/javascript">
37982  */
37983  
37984 /**
37985  * @class Roo.form.TextArea
37986  * @extends Roo.form.TextField
37987  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37988  * support for auto-sizing.
37989  * @constructor
37990  * Creates a new TextArea
37991  * @param {Object} config Configuration options
37992  */
37993 Roo.form.TextArea = function(config){
37994     Roo.form.TextArea.superclass.constructor.call(this, config);
37995     // these are provided exchanges for backwards compat
37996     // minHeight/maxHeight were replaced by growMin/growMax to be
37997     // compatible with TextField growing config values
37998     if(this.minHeight !== undefined){
37999         this.growMin = this.minHeight;
38000     }
38001     if(this.maxHeight !== undefined){
38002         this.growMax = this.maxHeight;
38003     }
38004 };
38005
38006 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38007     /**
38008      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38009      */
38010     growMin : 60,
38011     /**
38012      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38013      */
38014     growMax: 1000,
38015     /**
38016      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38017      * in the field (equivalent to setting overflow: hidden, defaults to false)
38018      */
38019     preventScrollbars: false,
38020     /**
38021      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38022      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38023      */
38024
38025     // private
38026     onRender : function(ct, position){
38027         if(!this.el){
38028             this.defaultAutoCreate = {
38029                 tag: "textarea",
38030                 style:"width:300px;height:60px;",
38031                 autocomplete: "off"
38032             };
38033         }
38034         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38035         if(this.grow){
38036             this.textSizeEl = Roo.DomHelper.append(document.body, {
38037                 tag: "pre", cls: "x-form-grow-sizer"
38038             });
38039             if(this.preventScrollbars){
38040                 this.el.setStyle("overflow", "hidden");
38041             }
38042             this.el.setHeight(this.growMin);
38043         }
38044     },
38045
38046     onDestroy : function(){
38047         if(this.textSizeEl){
38048             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38049         }
38050         Roo.form.TextArea.superclass.onDestroy.call(this);
38051     },
38052
38053     // private
38054     onKeyUp : function(e){
38055         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38056             this.autoSize();
38057         }
38058     },
38059
38060     /**
38061      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38062      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38063      */
38064     autoSize : function(){
38065         if(!this.grow || !this.textSizeEl){
38066             return;
38067         }
38068         var el = this.el;
38069         var v = el.dom.value;
38070         var ts = this.textSizeEl;
38071
38072         ts.innerHTML = '';
38073         ts.appendChild(document.createTextNode(v));
38074         v = ts.innerHTML;
38075
38076         Roo.fly(ts).setWidth(this.el.getWidth());
38077         if(v.length < 1){
38078             v = "&#160;&#160;";
38079         }else{
38080             if(Roo.isIE){
38081                 v = v.replace(/\n/g, '<p>&#160;</p>');
38082             }
38083             v += "&#160;\n&#160;";
38084         }
38085         ts.innerHTML = v;
38086         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38087         if(h != this.lastHeight){
38088             this.lastHeight = h;
38089             this.el.setHeight(h);
38090             this.fireEvent("autosize", this, h);
38091         }
38092     }
38093 });/*
38094  * Based on:
38095  * Ext JS Library 1.1.1
38096  * Copyright(c) 2006-2007, Ext JS, LLC.
38097  *
38098  * Originally Released Under LGPL - original licence link has changed is not relivant.
38099  *
38100  * Fork - LGPL
38101  * <script type="text/javascript">
38102  */
38103  
38104
38105 /**
38106  * @class Roo.form.NumberField
38107  * @extends Roo.form.TextField
38108  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38109  * @constructor
38110  * Creates a new NumberField
38111  * @param {Object} config Configuration options
38112  */
38113 Roo.form.NumberField = function(config){
38114     Roo.form.NumberField.superclass.constructor.call(this, config);
38115 };
38116
38117 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38118     /**
38119      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38120      */
38121     fieldClass: "x-form-field x-form-num-field",
38122     /**
38123      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38124      */
38125     allowDecimals : true,
38126     /**
38127      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38128      */
38129     decimalSeparator : ".",
38130     /**
38131      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38132      */
38133     decimalPrecision : 2,
38134     /**
38135      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38136      */
38137     allowNegative : true,
38138     /**
38139      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38140      */
38141     minValue : Number.NEGATIVE_INFINITY,
38142     /**
38143      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38144      */
38145     maxValue : Number.MAX_VALUE,
38146     /**
38147      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38148      */
38149     minText : "The minimum value for this field is {0}",
38150     /**
38151      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38152      */
38153     maxText : "The maximum value for this field is {0}",
38154     /**
38155      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38156      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38157      */
38158     nanText : "{0} is not a valid number",
38159
38160     // private
38161     initEvents : function(){
38162         Roo.form.NumberField.superclass.initEvents.call(this);
38163         var allowed = "0123456789";
38164         if(this.allowDecimals){
38165             allowed += this.decimalSeparator;
38166         }
38167         if(this.allowNegative){
38168             allowed += "-";
38169         }
38170         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38171         var keyPress = function(e){
38172             var k = e.getKey();
38173             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38174                 return;
38175             }
38176             var c = e.getCharCode();
38177             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38178                 e.stopEvent();
38179             }
38180         };
38181         this.el.on("keypress", keyPress, this);
38182     },
38183
38184     // private
38185     validateValue : function(value){
38186         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38187             return false;
38188         }
38189         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38190              return true;
38191         }
38192         var num = this.parseValue(value);
38193         if(isNaN(num)){
38194             this.markInvalid(String.format(this.nanText, value));
38195             return false;
38196         }
38197         if(num < this.minValue){
38198             this.markInvalid(String.format(this.minText, this.minValue));
38199             return false;
38200         }
38201         if(num > this.maxValue){
38202             this.markInvalid(String.format(this.maxText, this.maxValue));
38203             return false;
38204         }
38205         return true;
38206     },
38207
38208     getValue : function(){
38209         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38210     },
38211
38212     // private
38213     parseValue : function(value){
38214         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38215         return isNaN(value) ? '' : value;
38216     },
38217
38218     // private
38219     fixPrecision : function(value){
38220         var nan = isNaN(value);
38221         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38222             return nan ? '' : value;
38223         }
38224         return parseFloat(value).toFixed(this.decimalPrecision);
38225     },
38226
38227     setValue : function(v){
38228         v = this.fixPrecision(v);
38229         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38230     },
38231
38232     // private
38233     decimalPrecisionFcn : function(v){
38234         return Math.floor(v);
38235     },
38236
38237     beforeBlur : function(){
38238         var v = this.parseValue(this.getRawValue());
38239         if(v){
38240             this.setValue(v);
38241         }
38242     }
38243 });/*
38244  * Based on:
38245  * Ext JS Library 1.1.1
38246  * Copyright(c) 2006-2007, Ext JS, LLC.
38247  *
38248  * Originally Released Under LGPL - original licence link has changed is not relivant.
38249  *
38250  * Fork - LGPL
38251  * <script type="text/javascript">
38252  */
38253  
38254 /**
38255  * @class Roo.form.DateField
38256  * @extends Roo.form.TriggerField
38257  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38258 * @constructor
38259 * Create a new DateField
38260 * @param {Object} config
38261  */
38262 Roo.form.DateField = function(config){
38263     Roo.form.DateField.superclass.constructor.call(this, config);
38264     
38265       this.addEvents({
38266          
38267         /**
38268          * @event select
38269          * Fires when a date is selected
38270              * @param {Roo.form.DateField} combo This combo box
38271              * @param {Date} date The date selected
38272              */
38273         'select' : true
38274          
38275     });
38276     
38277     
38278     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38279     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38280     this.ddMatch = null;
38281     if(this.disabledDates){
38282         var dd = this.disabledDates;
38283         var re = "(?:";
38284         for(var i = 0; i < dd.length; i++){
38285             re += dd[i];
38286             if(i != dd.length-1) re += "|";
38287         }
38288         this.ddMatch = new RegExp(re + ")");
38289     }
38290 };
38291
38292 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38293     /**
38294      * @cfg {String} format
38295      * The default date format string which can be overriden for localization support.  The format must be
38296      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38297      */
38298     format : "m/d/y",
38299     /**
38300      * @cfg {String} altFormats
38301      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38302      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38303      */
38304     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38305     /**
38306      * @cfg {Array} disabledDays
38307      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38308      */
38309     disabledDays : null,
38310     /**
38311      * @cfg {String} disabledDaysText
38312      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38313      */
38314     disabledDaysText : "Disabled",
38315     /**
38316      * @cfg {Array} disabledDates
38317      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38318      * expression so they are very powerful. Some examples:
38319      * <ul>
38320      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38321      * <li>["03/08", "09/16"] would disable those days for every year</li>
38322      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38323      * <li>["03/../2006"] would disable every day in March 2006</li>
38324      * <li>["^03"] would disable every day in every March</li>
38325      * </ul>
38326      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38327      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38328      */
38329     disabledDates : null,
38330     /**
38331      * @cfg {String} disabledDatesText
38332      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38333      */
38334     disabledDatesText : "Disabled",
38335     /**
38336      * @cfg {Date/String} minValue
38337      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38338      * valid format (defaults to null).
38339      */
38340     minValue : null,
38341     /**
38342      * @cfg {Date/String} maxValue
38343      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38344      * valid format (defaults to null).
38345      */
38346     maxValue : null,
38347     /**
38348      * @cfg {String} minText
38349      * The error text to display when the date in the cell is before minValue (defaults to
38350      * 'The date in this field must be after {minValue}').
38351      */
38352     minText : "The date in this field must be equal to or after {0}",
38353     /**
38354      * @cfg {String} maxText
38355      * The error text to display when the date in the cell is after maxValue (defaults to
38356      * 'The date in this field must be before {maxValue}').
38357      */
38358     maxText : "The date in this field must be equal to or before {0}",
38359     /**
38360      * @cfg {String} invalidText
38361      * The error text to display when the date in the field is invalid (defaults to
38362      * '{value} is not a valid date - it must be in the format {format}').
38363      */
38364     invalidText : "{0} is not a valid date - it must be in the format {1}",
38365     /**
38366      * @cfg {String} triggerClass
38367      * An additional CSS class used to style the trigger button.  The trigger will always get the
38368      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38369      * which displays a calendar icon).
38370      */
38371     triggerClass : 'x-form-date-trigger',
38372     
38373
38374     /**
38375      * @cfg {Boolean} useIso
38376      * if enabled, then the date field will use a hidden field to store the 
38377      * real value as iso formated date. default (false)
38378      */ 
38379     useIso : false,
38380     /**
38381      * @cfg {String/Object} autoCreate
38382      * A DomHelper element spec, or true for a default element spec (defaults to
38383      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38384      */ 
38385     // private
38386     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38387     
38388     // private
38389     hiddenField: false,
38390     
38391     onRender : function(ct, position)
38392     {
38393         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38394         if (this.useIso) {
38395             //this.el.dom.removeAttribute('name'); 
38396             Roo.log("Changing name?");
38397             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38398             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38399                     'before', true);
38400             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38401             // prevent input submission
38402             this.hiddenName = this.name;
38403         }
38404             
38405             
38406     },
38407     
38408     // private
38409     validateValue : function(value)
38410     {
38411         value = this.formatDate(value);
38412         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38413             Roo.log('super failed');
38414             return false;
38415         }
38416         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38417              return true;
38418         }
38419         var svalue = value;
38420         value = this.parseDate(value);
38421         if(!value){
38422             Roo.log('parse date failed' + svalue);
38423             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38424             return false;
38425         }
38426         var time = value.getTime();
38427         if(this.minValue && time < this.minValue.getTime()){
38428             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38429             return false;
38430         }
38431         if(this.maxValue && time > this.maxValue.getTime()){
38432             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38433             return false;
38434         }
38435         if(this.disabledDays){
38436             var day = value.getDay();
38437             for(var i = 0; i < this.disabledDays.length; i++) {
38438                 if(day === this.disabledDays[i]){
38439                     this.markInvalid(this.disabledDaysText);
38440                     return false;
38441                 }
38442             }
38443         }
38444         var fvalue = this.formatDate(value);
38445         if(this.ddMatch && this.ddMatch.test(fvalue)){
38446             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38447             return false;
38448         }
38449         return true;
38450     },
38451
38452     // private
38453     // Provides logic to override the default TriggerField.validateBlur which just returns true
38454     validateBlur : function(){
38455         return !this.menu || !this.menu.isVisible();
38456     },
38457     
38458     getName: function()
38459     {
38460         // returns hidden if it's set..
38461         if (!this.rendered) {return ''};
38462         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38463         
38464     },
38465
38466     /**
38467      * Returns the current date value of the date field.
38468      * @return {Date} The date value
38469      */
38470     getValue : function(){
38471         
38472         return  this.hiddenField ?
38473                 this.hiddenField.value :
38474                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38475     },
38476
38477     /**
38478      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38479      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38480      * (the default format used is "m/d/y").
38481      * <br />Usage:
38482      * <pre><code>
38483 //All of these calls set the same date value (May 4, 2006)
38484
38485 //Pass a date object:
38486 var dt = new Date('5/4/06');
38487 dateField.setValue(dt);
38488
38489 //Pass a date string (default format):
38490 dateField.setValue('5/4/06');
38491
38492 //Pass a date string (custom format):
38493 dateField.format = 'Y-m-d';
38494 dateField.setValue('2006-5-4');
38495 </code></pre>
38496      * @param {String/Date} date The date or valid date string
38497      */
38498     setValue : function(date){
38499         if (this.hiddenField) {
38500             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38501         }
38502         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38503         // make sure the value field is always stored as a date..
38504         this.value = this.parseDate(date);
38505         
38506         
38507     },
38508
38509     // private
38510     parseDate : function(value){
38511         if(!value || value instanceof Date){
38512             return value;
38513         }
38514         var v = Date.parseDate(value, this.format);
38515          if (!v && this.useIso) {
38516             v = Date.parseDate(value, 'Y-m-d');
38517         }
38518         if(!v && this.altFormats){
38519             if(!this.altFormatsArray){
38520                 this.altFormatsArray = this.altFormats.split("|");
38521             }
38522             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38523                 v = Date.parseDate(value, this.altFormatsArray[i]);
38524             }
38525         }
38526         return v;
38527     },
38528
38529     // private
38530     formatDate : function(date, fmt){
38531         return (!date || !(date instanceof Date)) ?
38532                date : date.dateFormat(fmt || this.format);
38533     },
38534
38535     // private
38536     menuListeners : {
38537         select: function(m, d){
38538             
38539             this.setValue(d);
38540             this.fireEvent('select', this, d);
38541         },
38542         show : function(){ // retain focus styling
38543             this.onFocus();
38544         },
38545         hide : function(){
38546             this.focus.defer(10, this);
38547             var ml = this.menuListeners;
38548             this.menu.un("select", ml.select,  this);
38549             this.menu.un("show", ml.show,  this);
38550             this.menu.un("hide", ml.hide,  this);
38551         }
38552     },
38553
38554     // private
38555     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38556     onTriggerClick : function(){
38557         if(this.disabled){
38558             return;
38559         }
38560         if(this.menu == null){
38561             this.menu = new Roo.menu.DateMenu();
38562         }
38563         Roo.apply(this.menu.picker,  {
38564             showClear: this.allowBlank,
38565             minDate : this.minValue,
38566             maxDate : this.maxValue,
38567             disabledDatesRE : this.ddMatch,
38568             disabledDatesText : this.disabledDatesText,
38569             disabledDays : this.disabledDays,
38570             disabledDaysText : this.disabledDaysText,
38571             format : this.useIso ? 'Y-m-d' : this.format,
38572             minText : String.format(this.minText, this.formatDate(this.minValue)),
38573             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38574         });
38575         this.menu.on(Roo.apply({}, this.menuListeners, {
38576             scope:this
38577         }));
38578         this.menu.picker.setValue(this.getValue() || new Date());
38579         this.menu.show(this.el, "tl-bl?");
38580     },
38581
38582     beforeBlur : function(){
38583         var v = this.parseDate(this.getRawValue());
38584         if(v){
38585             this.setValue(v);
38586         }
38587     },
38588
38589     /*@
38590      * overide
38591      * 
38592      */
38593     isDirty : function() {
38594         if(this.disabled) {
38595             return false;
38596         }
38597         
38598         if(typeof(this.startValue) === 'undefined'){
38599             return false;
38600         }
38601         
38602         return String(this.getValue()) !== String(this.startValue);
38603         
38604     }
38605 });/*
38606  * Based on:
38607  * Ext JS Library 1.1.1
38608  * Copyright(c) 2006-2007, Ext JS, LLC.
38609  *
38610  * Originally Released Under LGPL - original licence link has changed is not relivant.
38611  *
38612  * Fork - LGPL
38613  * <script type="text/javascript">
38614  */
38615  
38616 /**
38617  * @class Roo.form.MonthField
38618  * @extends Roo.form.TriggerField
38619  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38620 * @constructor
38621 * Create a new MonthField
38622 * @param {Object} config
38623  */
38624 Roo.form.MonthField = function(config){
38625     
38626     Roo.form.MonthField.superclass.constructor.call(this, config);
38627     
38628       this.addEvents({
38629          
38630         /**
38631          * @event select
38632          * Fires when a date is selected
38633              * @param {Roo.form.MonthFieeld} combo This combo box
38634              * @param {Date} date The date selected
38635              */
38636         'select' : true
38637          
38638     });
38639     
38640     
38641     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38642     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38643     this.ddMatch = null;
38644     if(this.disabledDates){
38645         var dd = this.disabledDates;
38646         var re = "(?:";
38647         for(var i = 0; i < dd.length; i++){
38648             re += dd[i];
38649             if(i != dd.length-1) re += "|";
38650         }
38651         this.ddMatch = new RegExp(re + ")");
38652     }
38653 };
38654
38655 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38656     /**
38657      * @cfg {String} format
38658      * The default date format string which can be overriden for localization support.  The format must be
38659      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38660      */
38661     format : "M Y",
38662     /**
38663      * @cfg {String} altFormats
38664      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38665      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38666      */
38667     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38668     /**
38669      * @cfg {Array} disabledDays
38670      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38671      */
38672     disabledDays : [0,1,2,3,4,5,6],
38673     /**
38674      * @cfg {String} disabledDaysText
38675      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38676      */
38677     disabledDaysText : "Disabled",
38678     /**
38679      * @cfg {Array} disabledDates
38680      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38681      * expression so they are very powerful. Some examples:
38682      * <ul>
38683      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38684      * <li>["03/08", "09/16"] would disable those days for every year</li>
38685      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38686      * <li>["03/../2006"] would disable every day in March 2006</li>
38687      * <li>["^03"] would disable every day in every March</li>
38688      * </ul>
38689      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38690      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38691      */
38692     disabledDates : null,
38693     /**
38694      * @cfg {String} disabledDatesText
38695      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38696      */
38697     disabledDatesText : "Disabled",
38698     /**
38699      * @cfg {Date/String} minValue
38700      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38701      * valid format (defaults to null).
38702      */
38703     minValue : null,
38704     /**
38705      * @cfg {Date/String} maxValue
38706      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38707      * valid format (defaults to null).
38708      */
38709     maxValue : null,
38710     /**
38711      * @cfg {String} minText
38712      * The error text to display when the date in the cell is before minValue (defaults to
38713      * 'The date in this field must be after {minValue}').
38714      */
38715     minText : "The date in this field must be equal to or after {0}",
38716     /**
38717      * @cfg {String} maxTextf
38718      * The error text to display when the date in the cell is after maxValue (defaults to
38719      * 'The date in this field must be before {maxValue}').
38720      */
38721     maxText : "The date in this field must be equal to or before {0}",
38722     /**
38723      * @cfg {String} invalidText
38724      * The error text to display when the date in the field is invalid (defaults to
38725      * '{value} is not a valid date - it must be in the format {format}').
38726      */
38727     invalidText : "{0} is not a valid date - it must be in the format {1}",
38728     /**
38729      * @cfg {String} triggerClass
38730      * An additional CSS class used to style the trigger button.  The trigger will always get the
38731      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38732      * which displays a calendar icon).
38733      */
38734     triggerClass : 'x-form-date-trigger',
38735     
38736
38737     /**
38738      * @cfg {Boolean} useIso
38739      * if enabled, then the date field will use a hidden field to store the 
38740      * real value as iso formated date. default (true)
38741      */ 
38742     useIso : true,
38743     /**
38744      * @cfg {String/Object} autoCreate
38745      * A DomHelper element spec, or true for a default element spec (defaults to
38746      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38747      */ 
38748     // private
38749     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38750     
38751     // private
38752     hiddenField: false,
38753     
38754     hideMonthPicker : false,
38755     
38756     onRender : function(ct, position)
38757     {
38758         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38759         if (this.useIso) {
38760             this.el.dom.removeAttribute('name'); 
38761             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38762                     'before', true);
38763             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38764             // prevent input submission
38765             this.hiddenName = this.name;
38766         }
38767             
38768             
38769     },
38770     
38771     // private
38772     validateValue : function(value)
38773     {
38774         value = this.formatDate(value);
38775         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38776             return false;
38777         }
38778         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38779              return true;
38780         }
38781         var svalue = value;
38782         value = this.parseDate(value);
38783         if(!value){
38784             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38785             return false;
38786         }
38787         var time = value.getTime();
38788         if(this.minValue && time < this.minValue.getTime()){
38789             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38790             return false;
38791         }
38792         if(this.maxValue && time > this.maxValue.getTime()){
38793             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38794             return false;
38795         }
38796         /*if(this.disabledDays){
38797             var day = value.getDay();
38798             for(var i = 0; i < this.disabledDays.length; i++) {
38799                 if(day === this.disabledDays[i]){
38800                     this.markInvalid(this.disabledDaysText);
38801                     return false;
38802                 }
38803             }
38804         }
38805         */
38806         var fvalue = this.formatDate(value);
38807         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38808             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38809             return false;
38810         }
38811         */
38812         return true;
38813     },
38814
38815     // private
38816     // Provides logic to override the default TriggerField.validateBlur which just returns true
38817     validateBlur : function(){
38818         return !this.menu || !this.menu.isVisible();
38819     },
38820
38821     /**
38822      * Returns the current date value of the date field.
38823      * @return {Date} The date value
38824      */
38825     getValue : function(){
38826         
38827         
38828         
38829         return  this.hiddenField ?
38830                 this.hiddenField.value :
38831                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38832     },
38833
38834     /**
38835      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38836      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38837      * (the default format used is "m/d/y").
38838      * <br />Usage:
38839      * <pre><code>
38840 //All of these calls set the same date value (May 4, 2006)
38841
38842 //Pass a date object:
38843 var dt = new Date('5/4/06');
38844 monthField.setValue(dt);
38845
38846 //Pass a date string (default format):
38847 monthField.setValue('5/4/06');
38848
38849 //Pass a date string (custom format):
38850 monthField.format = 'Y-m-d';
38851 monthField.setValue('2006-5-4');
38852 </code></pre>
38853      * @param {String/Date} date The date or valid date string
38854      */
38855     setValue : function(date){
38856         Roo.log('month setValue' + date);
38857         // can only be first of month..
38858         
38859         var val = this.parseDate(date);
38860         
38861         if (this.hiddenField) {
38862             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38863         }
38864         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38865         this.value = this.parseDate(date);
38866     },
38867
38868     // private
38869     parseDate : function(value){
38870         if(!value || value instanceof Date){
38871             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38872             return value;
38873         }
38874         var v = Date.parseDate(value, this.format);
38875         if (!v && this.useIso) {
38876             v = Date.parseDate(value, 'Y-m-d');
38877         }
38878         if (v) {
38879             // 
38880             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38881         }
38882         
38883         
38884         if(!v && this.altFormats){
38885             if(!this.altFormatsArray){
38886                 this.altFormatsArray = this.altFormats.split("|");
38887             }
38888             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38889                 v = Date.parseDate(value, this.altFormatsArray[i]);
38890             }
38891         }
38892         return v;
38893     },
38894
38895     // private
38896     formatDate : function(date, fmt){
38897         return (!date || !(date instanceof Date)) ?
38898                date : date.dateFormat(fmt || this.format);
38899     },
38900
38901     // private
38902     menuListeners : {
38903         select: function(m, d){
38904             this.setValue(d);
38905             this.fireEvent('select', this, d);
38906         },
38907         show : function(){ // retain focus styling
38908             this.onFocus();
38909         },
38910         hide : function(){
38911             this.focus.defer(10, this);
38912             var ml = this.menuListeners;
38913             this.menu.un("select", ml.select,  this);
38914             this.menu.un("show", ml.show,  this);
38915             this.menu.un("hide", ml.hide,  this);
38916         }
38917     },
38918     // private
38919     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38920     onTriggerClick : function(){
38921         if(this.disabled){
38922             return;
38923         }
38924         if(this.menu == null){
38925             this.menu = new Roo.menu.DateMenu();
38926            
38927         }
38928         
38929         Roo.apply(this.menu.picker,  {
38930             
38931             showClear: this.allowBlank,
38932             minDate : this.minValue,
38933             maxDate : this.maxValue,
38934             disabledDatesRE : this.ddMatch,
38935             disabledDatesText : this.disabledDatesText,
38936             
38937             format : this.useIso ? 'Y-m-d' : this.format,
38938             minText : String.format(this.minText, this.formatDate(this.minValue)),
38939             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38940             
38941         });
38942          this.menu.on(Roo.apply({}, this.menuListeners, {
38943             scope:this
38944         }));
38945        
38946         
38947         var m = this.menu;
38948         var p = m.picker;
38949         
38950         // hide month picker get's called when we called by 'before hide';
38951         
38952         var ignorehide = true;
38953         p.hideMonthPicker  = function(disableAnim){
38954             if (ignorehide) {
38955                 return;
38956             }
38957              if(this.monthPicker){
38958                 Roo.log("hideMonthPicker called");
38959                 if(disableAnim === true){
38960                     this.monthPicker.hide();
38961                 }else{
38962                     this.monthPicker.slideOut('t', {duration:.2});
38963                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38964                     p.fireEvent("select", this, this.value);
38965                     m.hide();
38966                 }
38967             }
38968         }
38969         
38970         Roo.log('picker set value');
38971         Roo.log(this.getValue());
38972         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38973         m.show(this.el, 'tl-bl?');
38974         ignorehide  = false;
38975         // this will trigger hideMonthPicker..
38976         
38977         
38978         // hidden the day picker
38979         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38980         
38981         
38982         
38983       
38984         
38985         p.showMonthPicker.defer(100, p);
38986     
38987         
38988        
38989     },
38990
38991     beforeBlur : function(){
38992         var v = this.parseDate(this.getRawValue());
38993         if(v){
38994             this.setValue(v);
38995         }
38996     }
38997
38998     /** @cfg {Boolean} grow @hide */
38999     /** @cfg {Number} growMin @hide */
39000     /** @cfg {Number} growMax @hide */
39001     /**
39002      * @hide
39003      * @method autoSize
39004      */
39005 });/*
39006  * Based on:
39007  * Ext JS Library 1.1.1
39008  * Copyright(c) 2006-2007, Ext JS, LLC.
39009  *
39010  * Originally Released Under LGPL - original licence link has changed is not relivant.
39011  *
39012  * Fork - LGPL
39013  * <script type="text/javascript">
39014  */
39015  
39016
39017 /**
39018  * @class Roo.form.ComboBox
39019  * @extends Roo.form.TriggerField
39020  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39021  * @constructor
39022  * Create a new ComboBox.
39023  * @param {Object} config Configuration options
39024  */
39025 Roo.form.ComboBox = function(config){
39026     Roo.form.ComboBox.superclass.constructor.call(this, config);
39027     this.addEvents({
39028         /**
39029          * @event expand
39030          * Fires when the dropdown list is expanded
39031              * @param {Roo.form.ComboBox} combo This combo box
39032              */
39033         'expand' : true,
39034         /**
39035          * @event collapse
39036          * Fires when the dropdown list is collapsed
39037              * @param {Roo.form.ComboBox} combo This combo box
39038              */
39039         'collapse' : true,
39040         /**
39041          * @event beforeselect
39042          * Fires before a list item is selected. Return false to cancel the selection.
39043              * @param {Roo.form.ComboBox} combo This combo box
39044              * @param {Roo.data.Record} record The data record returned from the underlying store
39045              * @param {Number} index The index of the selected item in the dropdown list
39046              */
39047         'beforeselect' : true,
39048         /**
39049          * @event select
39050          * Fires when a list item is selected
39051              * @param {Roo.form.ComboBox} combo This combo box
39052              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39053              * @param {Number} index The index of the selected item in the dropdown list
39054              */
39055         'select' : true,
39056         /**
39057          * @event beforequery
39058          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39059          * The event object passed has these properties:
39060              * @param {Roo.form.ComboBox} combo This combo box
39061              * @param {String} query The query
39062              * @param {Boolean} forceAll true to force "all" query
39063              * @param {Boolean} cancel true to cancel the query
39064              * @param {Object} e The query event object
39065              */
39066         'beforequery': true,
39067          /**
39068          * @event add
39069          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39070              * @param {Roo.form.ComboBox} combo This combo box
39071              */
39072         'add' : true,
39073         /**
39074          * @event edit
39075          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39076              * @param {Roo.form.ComboBox} combo This combo box
39077              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39078              */
39079         'edit' : true
39080         
39081         
39082     });
39083     if(this.transform){
39084         this.allowDomMove = false;
39085         var s = Roo.getDom(this.transform);
39086         if(!this.hiddenName){
39087             this.hiddenName = s.name;
39088         }
39089         if(!this.store){
39090             this.mode = 'local';
39091             var d = [], opts = s.options;
39092             for(var i = 0, len = opts.length;i < len; i++){
39093                 var o = opts[i];
39094                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39095                 if(o.selected) {
39096                     this.value = value;
39097                 }
39098                 d.push([value, o.text]);
39099             }
39100             this.store = new Roo.data.SimpleStore({
39101                 'id': 0,
39102                 fields: ['value', 'text'],
39103                 data : d
39104             });
39105             this.valueField = 'value';
39106             this.displayField = 'text';
39107         }
39108         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39109         if(!this.lazyRender){
39110             this.target = true;
39111             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39112             s.parentNode.removeChild(s); // remove it
39113             this.render(this.el.parentNode);
39114         }else{
39115             s.parentNode.removeChild(s); // remove it
39116         }
39117
39118     }
39119     if (this.store) {
39120         this.store = Roo.factory(this.store, Roo.data);
39121     }
39122     
39123     this.selectedIndex = -1;
39124     if(this.mode == 'local'){
39125         if(config.queryDelay === undefined){
39126             this.queryDelay = 10;
39127         }
39128         if(config.minChars === undefined){
39129             this.minChars = 0;
39130         }
39131     }
39132 };
39133
39134 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39135     /**
39136      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39137      */
39138     /**
39139      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39140      * rendering into an Roo.Editor, defaults to false)
39141      */
39142     /**
39143      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39144      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39145      */
39146     /**
39147      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39148      */
39149     /**
39150      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39151      * the dropdown list (defaults to undefined, with no header element)
39152      */
39153
39154      /**
39155      * @cfg {String/Roo.Template} tpl The template to use to render the output
39156      */
39157      
39158     // private
39159     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39160     /**
39161      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39162      */
39163     listWidth: undefined,
39164     /**
39165      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39166      * mode = 'remote' or 'text' if mode = 'local')
39167      */
39168     displayField: undefined,
39169     /**
39170      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39171      * mode = 'remote' or 'value' if mode = 'local'). 
39172      * Note: use of a valueField requires the user make a selection
39173      * in order for a value to be mapped.
39174      */
39175     valueField: undefined,
39176     
39177     
39178     /**
39179      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39180      * field's data value (defaults to the underlying DOM element's name)
39181      */
39182     hiddenName: undefined,
39183     /**
39184      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39185      */
39186     listClass: '',
39187     /**
39188      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39189      */
39190     selectedClass: 'x-combo-selected',
39191     /**
39192      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39193      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39194      * which displays a downward arrow icon).
39195      */
39196     triggerClass : 'x-form-arrow-trigger',
39197     /**
39198      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39199      */
39200     shadow:'sides',
39201     /**
39202      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39203      * anchor positions (defaults to 'tl-bl')
39204      */
39205     listAlign: 'tl-bl?',
39206     /**
39207      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39208      */
39209     maxHeight: 300,
39210     /**
39211      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39212      * query specified by the allQuery config option (defaults to 'query')
39213      */
39214     triggerAction: 'query',
39215     /**
39216      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39217      * (defaults to 4, does not apply if editable = false)
39218      */
39219     minChars : 4,
39220     /**
39221      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39222      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39223      */
39224     typeAhead: false,
39225     /**
39226      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39227      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39228      */
39229     queryDelay: 500,
39230     /**
39231      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39232      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39233      */
39234     pageSize: 0,
39235     /**
39236      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39237      * when editable = true (defaults to false)
39238      */
39239     selectOnFocus:false,
39240     /**
39241      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39242      */
39243     queryParam: 'query',
39244     /**
39245      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39246      * when mode = 'remote' (defaults to 'Loading...')
39247      */
39248     loadingText: 'Loading...',
39249     /**
39250      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39251      */
39252     resizable: false,
39253     /**
39254      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39255      */
39256     handleHeight : 8,
39257     /**
39258      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39259      * traditional select (defaults to true)
39260      */
39261     editable: true,
39262     /**
39263      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39264      */
39265     allQuery: '',
39266     /**
39267      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39268      */
39269     mode: 'remote',
39270     /**
39271      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39272      * listWidth has a higher value)
39273      */
39274     minListWidth : 70,
39275     /**
39276      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39277      * allow the user to set arbitrary text into the field (defaults to false)
39278      */
39279     forceSelection:false,
39280     /**
39281      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39282      * if typeAhead = true (defaults to 250)
39283      */
39284     typeAheadDelay : 250,
39285     /**
39286      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39287      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39288      */
39289     valueNotFoundText : undefined,
39290     /**
39291      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39292      */
39293     blockFocus : false,
39294     
39295     /**
39296      * @cfg {Boolean} disableClear Disable showing of clear button.
39297      */
39298     disableClear : false,
39299     /**
39300      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39301      */
39302     alwaysQuery : false,
39303     
39304     //private
39305     addicon : false,
39306     editicon: false,
39307     
39308     // element that contains real text value.. (when hidden is used..)
39309      
39310     // private
39311     onRender : function(ct, position){
39312         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39313         if(this.hiddenName){
39314             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39315                     'before', true);
39316             this.hiddenField.value =
39317                 this.hiddenValue !== undefined ? this.hiddenValue :
39318                 this.value !== undefined ? this.value : '';
39319
39320             // prevent input submission
39321             this.el.dom.removeAttribute('name');
39322              
39323              
39324         }
39325         if(Roo.isGecko){
39326             this.el.dom.setAttribute('autocomplete', 'off');
39327         }
39328
39329         var cls = 'x-combo-list';
39330
39331         this.list = new Roo.Layer({
39332             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39333         });
39334
39335         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39336         this.list.setWidth(lw);
39337         this.list.swallowEvent('mousewheel');
39338         this.assetHeight = 0;
39339
39340         if(this.title){
39341             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39342             this.assetHeight += this.header.getHeight();
39343         }
39344
39345         this.innerList = this.list.createChild({cls:cls+'-inner'});
39346         this.innerList.on('mouseover', this.onViewOver, this);
39347         this.innerList.on('mousemove', this.onViewMove, this);
39348         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39349         
39350         if(this.allowBlank && !this.pageSize && !this.disableClear){
39351             this.footer = this.list.createChild({cls:cls+'-ft'});
39352             this.pageTb = new Roo.Toolbar(this.footer);
39353            
39354         }
39355         if(this.pageSize){
39356             this.footer = this.list.createChild({cls:cls+'-ft'});
39357             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39358                     {pageSize: this.pageSize});
39359             
39360         }
39361         
39362         if (this.pageTb && this.allowBlank && !this.disableClear) {
39363             var _this = this;
39364             this.pageTb.add(new Roo.Toolbar.Fill(), {
39365                 cls: 'x-btn-icon x-btn-clear',
39366                 text: '&#160;',
39367                 handler: function()
39368                 {
39369                     _this.collapse();
39370                     _this.clearValue();
39371                     _this.onSelect(false, -1);
39372                 }
39373             });
39374         }
39375         if (this.footer) {
39376             this.assetHeight += this.footer.getHeight();
39377         }
39378         
39379
39380         if(!this.tpl){
39381             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39382         }
39383
39384         this.view = new Roo.View(this.innerList, this.tpl, {
39385             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39386         });
39387
39388         this.view.on('click', this.onViewClick, this);
39389
39390         this.store.on('beforeload', this.onBeforeLoad, this);
39391         this.store.on('load', this.onLoad, this);
39392         this.store.on('loadexception', this.onLoadException, this);
39393
39394         if(this.resizable){
39395             this.resizer = new Roo.Resizable(this.list,  {
39396                pinned:true, handles:'se'
39397             });
39398             this.resizer.on('resize', function(r, w, h){
39399                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39400                 this.listWidth = w;
39401                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39402                 this.restrictHeight();
39403             }, this);
39404             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39405         }
39406         if(!this.editable){
39407             this.editable = true;
39408             this.setEditable(false);
39409         }  
39410         
39411         
39412         if (typeof(this.events.add.listeners) != 'undefined') {
39413             
39414             this.addicon = this.wrap.createChild(
39415                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39416        
39417             this.addicon.on('click', function(e) {
39418                 this.fireEvent('add', this);
39419             }, this);
39420         }
39421         if (typeof(this.events.edit.listeners) != 'undefined') {
39422             
39423             this.editicon = this.wrap.createChild(
39424                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39425             if (this.addicon) {
39426                 this.editicon.setStyle('margin-left', '40px');
39427             }
39428             this.editicon.on('click', function(e) {
39429                 
39430                 // we fire even  if inothing is selected..
39431                 this.fireEvent('edit', this, this.lastData );
39432                 
39433             }, this);
39434         }
39435         
39436         
39437         
39438     },
39439
39440     // private
39441     initEvents : function(){
39442         Roo.form.ComboBox.superclass.initEvents.call(this);
39443
39444         this.keyNav = new Roo.KeyNav(this.el, {
39445             "up" : function(e){
39446                 this.inKeyMode = true;
39447                 this.selectPrev();
39448             },
39449
39450             "down" : function(e){
39451                 if(!this.isExpanded()){
39452                     this.onTriggerClick();
39453                 }else{
39454                     this.inKeyMode = true;
39455                     this.selectNext();
39456                 }
39457             },
39458
39459             "enter" : function(e){
39460                 this.onViewClick();
39461                 //return true;
39462             },
39463
39464             "esc" : function(e){
39465                 this.collapse();
39466             },
39467
39468             "tab" : function(e){
39469                 this.onViewClick(false);
39470                 this.fireEvent("specialkey", this, e);
39471                 return true;
39472             },
39473
39474             scope : this,
39475
39476             doRelay : function(foo, bar, hname){
39477                 if(hname == 'down' || this.scope.isExpanded()){
39478                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39479                 }
39480                 return true;
39481             },
39482
39483             forceKeyDown: true
39484         });
39485         this.queryDelay = Math.max(this.queryDelay || 10,
39486                 this.mode == 'local' ? 10 : 250);
39487         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39488         if(this.typeAhead){
39489             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39490         }
39491         if(this.editable !== false){
39492             this.el.on("keyup", this.onKeyUp, this);
39493         }
39494         if(this.forceSelection){
39495             this.on('blur', this.doForce, this);
39496         }
39497     },
39498
39499     onDestroy : function(){
39500         if(this.view){
39501             this.view.setStore(null);
39502             this.view.el.removeAllListeners();
39503             this.view.el.remove();
39504             this.view.purgeListeners();
39505         }
39506         if(this.list){
39507             this.list.destroy();
39508         }
39509         if(this.store){
39510             this.store.un('beforeload', this.onBeforeLoad, this);
39511             this.store.un('load', this.onLoad, this);
39512             this.store.un('loadexception', this.onLoadException, this);
39513         }
39514         Roo.form.ComboBox.superclass.onDestroy.call(this);
39515     },
39516
39517     // private
39518     fireKey : function(e){
39519         if(e.isNavKeyPress() && !this.list.isVisible()){
39520             this.fireEvent("specialkey", this, e);
39521         }
39522     },
39523
39524     // private
39525     onResize: function(w, h){
39526         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39527         
39528         if(typeof w != 'number'){
39529             // we do not handle it!?!?
39530             return;
39531         }
39532         var tw = this.trigger.getWidth();
39533         tw += this.addicon ? this.addicon.getWidth() : 0;
39534         tw += this.editicon ? this.editicon.getWidth() : 0;
39535         var x = w - tw;
39536         this.el.setWidth( this.adjustWidth('input', x));
39537             
39538         this.trigger.setStyle('left', x+'px');
39539         
39540         if(this.list && this.listWidth === undefined){
39541             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39542             this.list.setWidth(lw);
39543             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39544         }
39545         
39546     
39547         
39548     },
39549
39550     /**
39551      * Allow or prevent the user from directly editing the field text.  If false is passed,
39552      * the user will only be able to select from the items defined in the dropdown list.  This method
39553      * is the runtime equivalent of setting the 'editable' config option at config time.
39554      * @param {Boolean} value True to allow the user to directly edit the field text
39555      */
39556     setEditable : function(value){
39557         if(value == this.editable){
39558             return;
39559         }
39560         this.editable = value;
39561         if(!value){
39562             this.el.dom.setAttribute('readOnly', true);
39563             this.el.on('mousedown', this.onTriggerClick,  this);
39564             this.el.addClass('x-combo-noedit');
39565         }else{
39566             this.el.dom.setAttribute('readOnly', false);
39567             this.el.un('mousedown', this.onTriggerClick,  this);
39568             this.el.removeClass('x-combo-noedit');
39569         }
39570     },
39571
39572     // private
39573     onBeforeLoad : function(){
39574         if(!this.hasFocus){
39575             return;
39576         }
39577         this.innerList.update(this.loadingText ?
39578                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39579         this.restrictHeight();
39580         this.selectedIndex = -1;
39581     },
39582
39583     // private
39584     onLoad : function(){
39585         if(!this.hasFocus){
39586             return;
39587         }
39588         if(this.store.getCount() > 0){
39589             this.expand();
39590             this.restrictHeight();
39591             if(this.lastQuery == this.allQuery){
39592                 if(this.editable){
39593                     this.el.dom.select();
39594                 }
39595                 if(!this.selectByValue(this.value, true)){
39596                     this.select(0, true);
39597                 }
39598             }else{
39599                 this.selectNext();
39600                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39601                     this.taTask.delay(this.typeAheadDelay);
39602                 }
39603             }
39604         }else{
39605             this.onEmptyResults();
39606         }
39607         //this.el.focus();
39608     },
39609     // private
39610     onLoadException : function()
39611     {
39612         this.collapse();
39613         Roo.log(this.store.reader.jsonData);
39614         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39615             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39616         }
39617         
39618         
39619     },
39620     // private
39621     onTypeAhead : function(){
39622         if(this.store.getCount() > 0){
39623             var r = this.store.getAt(0);
39624             var newValue = r.data[this.displayField];
39625             var len = newValue.length;
39626             var selStart = this.getRawValue().length;
39627             if(selStart != len){
39628                 this.setRawValue(newValue);
39629                 this.selectText(selStart, newValue.length);
39630             }
39631         }
39632     },
39633
39634     // private
39635     onSelect : function(record, index){
39636         if(this.fireEvent('beforeselect', this, record, index) !== false){
39637             this.setFromData(index > -1 ? record.data : false);
39638             this.collapse();
39639             this.fireEvent('select', this, record, index);
39640         }
39641     },
39642
39643     /**
39644      * Returns the currently selected field value or empty string if no value is set.
39645      * @return {String} value The selected value
39646      */
39647     getValue : function(){
39648         if(this.valueField){
39649             return typeof this.value != 'undefined' ? this.value : '';
39650         }else{
39651             return Roo.form.ComboBox.superclass.getValue.call(this);
39652         }
39653     },
39654
39655     /**
39656      * Clears any text/value currently set in the field
39657      */
39658     clearValue : function(){
39659         if(this.hiddenField){
39660             this.hiddenField.value = '';
39661         }
39662         this.value = '';
39663         this.setRawValue('');
39664         this.lastSelectionText = '';
39665         
39666     },
39667
39668     /**
39669      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39670      * will be displayed in the field.  If the value does not match the data value of an existing item,
39671      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39672      * Otherwise the field will be blank (although the value will still be set).
39673      * @param {String} value The value to match
39674      */
39675     setValue : function(v){
39676         var text = v;
39677         if(this.valueField){
39678             var r = this.findRecord(this.valueField, v);
39679             if(r){
39680                 text = r.data[this.displayField];
39681             }else if(this.valueNotFoundText !== undefined){
39682                 text = this.valueNotFoundText;
39683             }
39684         }
39685         this.lastSelectionText = text;
39686         if(this.hiddenField){
39687             this.hiddenField.value = v;
39688         }
39689         Roo.form.ComboBox.superclass.setValue.call(this, text);
39690         this.value = v;
39691     },
39692     /**
39693      * @property {Object} the last set data for the element
39694      */
39695     
39696     lastData : false,
39697     /**
39698      * Sets the value of the field based on a object which is related to the record format for the store.
39699      * @param {Object} value the value to set as. or false on reset?
39700      */
39701     setFromData : function(o){
39702         var dv = ''; // display value
39703         var vv = ''; // value value..
39704         this.lastData = o;
39705         if (this.displayField) {
39706             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39707         } else {
39708             // this is an error condition!!!
39709             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39710         }
39711         
39712         if(this.valueField){
39713             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39714         }
39715         if(this.hiddenField){
39716             this.hiddenField.value = vv;
39717             
39718             this.lastSelectionText = dv;
39719             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39720             this.value = vv;
39721             return;
39722         }
39723         // no hidden field.. - we store the value in 'value', but still display
39724         // display field!!!!
39725         this.lastSelectionText = dv;
39726         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39727         this.value = vv;
39728         
39729         
39730     },
39731     // private
39732     reset : function(){
39733         // overridden so that last data is reset..
39734         this.setValue(this.resetValue);
39735         this.clearInvalid();
39736         this.lastData = false;
39737         if (this.view) {
39738             this.view.clearSelections();
39739         }
39740     },
39741     // private
39742     findRecord : function(prop, value){
39743         var record;
39744         if(this.store.getCount() > 0){
39745             this.store.each(function(r){
39746                 if(r.data[prop] == value){
39747                     record = r;
39748                     return false;
39749                 }
39750                 return true;
39751             });
39752         }
39753         return record;
39754     },
39755     
39756     getName: function()
39757     {
39758         // returns hidden if it's set..
39759         if (!this.rendered) {return ''};
39760         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39761         
39762     },
39763     // private
39764     onViewMove : function(e, t){
39765         this.inKeyMode = false;
39766     },
39767
39768     // private
39769     onViewOver : function(e, t){
39770         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39771             return;
39772         }
39773         var item = this.view.findItemFromChild(t);
39774         if(item){
39775             var index = this.view.indexOf(item);
39776             this.select(index, false);
39777         }
39778     },
39779
39780     // private
39781     onViewClick : function(doFocus)
39782     {
39783         var index = this.view.getSelectedIndexes()[0];
39784         var r = this.store.getAt(index);
39785         if(r){
39786             this.onSelect(r, index);
39787         }
39788         if(doFocus !== false && !this.blockFocus){
39789             this.el.focus();
39790         }
39791     },
39792
39793     // private
39794     restrictHeight : function(){
39795         this.innerList.dom.style.height = '';
39796         var inner = this.innerList.dom;
39797         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39798         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39799         this.list.beginUpdate();
39800         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39801         this.list.alignTo(this.el, this.listAlign);
39802         this.list.endUpdate();
39803     },
39804
39805     // private
39806     onEmptyResults : function(){
39807         this.collapse();
39808     },
39809
39810     /**
39811      * Returns true if the dropdown list is expanded, else false.
39812      */
39813     isExpanded : function(){
39814         return this.list.isVisible();
39815     },
39816
39817     /**
39818      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39819      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39820      * @param {String} value The data value of the item to select
39821      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39822      * selected item if it is not currently in view (defaults to true)
39823      * @return {Boolean} True if the value matched an item in the list, else false
39824      */
39825     selectByValue : function(v, scrollIntoView){
39826         if(v !== undefined && v !== null){
39827             var r = this.findRecord(this.valueField || this.displayField, v);
39828             if(r){
39829                 this.select(this.store.indexOf(r), scrollIntoView);
39830                 return true;
39831             }
39832         }
39833         return false;
39834     },
39835
39836     /**
39837      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39838      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39839      * @param {Number} index The zero-based index of the list item to select
39840      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39841      * selected item if it is not currently in view (defaults to true)
39842      */
39843     select : function(index, scrollIntoView){
39844         this.selectedIndex = index;
39845         this.view.select(index);
39846         if(scrollIntoView !== false){
39847             var el = this.view.getNode(index);
39848             if(el){
39849                 this.innerList.scrollChildIntoView(el, false);
39850             }
39851         }
39852     },
39853
39854     // private
39855     selectNext : function(){
39856         var ct = this.store.getCount();
39857         if(ct > 0){
39858             if(this.selectedIndex == -1){
39859                 this.select(0);
39860             }else if(this.selectedIndex < ct-1){
39861                 this.select(this.selectedIndex+1);
39862             }
39863         }
39864     },
39865
39866     // private
39867     selectPrev : function(){
39868         var ct = this.store.getCount();
39869         if(ct > 0){
39870             if(this.selectedIndex == -1){
39871                 this.select(0);
39872             }else if(this.selectedIndex != 0){
39873                 this.select(this.selectedIndex-1);
39874             }
39875         }
39876     },
39877
39878     // private
39879     onKeyUp : function(e){
39880         if(this.editable !== false && !e.isSpecialKey()){
39881             this.lastKey = e.getKey();
39882             this.dqTask.delay(this.queryDelay);
39883         }
39884     },
39885
39886     // private
39887     validateBlur : function(){
39888         return !this.list || !this.list.isVisible();   
39889     },
39890
39891     // private
39892     initQuery : function(){
39893         this.doQuery(this.getRawValue());
39894     },
39895
39896     // private
39897     doForce : function(){
39898         if(this.el.dom.value.length > 0){
39899             this.el.dom.value =
39900                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39901              
39902         }
39903     },
39904
39905     /**
39906      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39907      * query allowing the query action to be canceled if needed.
39908      * @param {String} query The SQL query to execute
39909      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39910      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39911      * saved in the current store (defaults to false)
39912      */
39913     doQuery : function(q, forceAll){
39914         if(q === undefined || q === null){
39915             q = '';
39916         }
39917         var qe = {
39918             query: q,
39919             forceAll: forceAll,
39920             combo: this,
39921             cancel:false
39922         };
39923         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39924             return false;
39925         }
39926         q = qe.query;
39927         forceAll = qe.forceAll;
39928         if(forceAll === true || (q.length >= this.minChars)){
39929             if(this.lastQuery != q || this.alwaysQuery){
39930                 this.lastQuery = q;
39931                 if(this.mode == 'local'){
39932                     this.selectedIndex = -1;
39933                     if(forceAll){
39934                         this.store.clearFilter();
39935                     }else{
39936                         this.store.filter(this.displayField, q);
39937                     }
39938                     this.onLoad();
39939                 }else{
39940                     this.store.baseParams[this.queryParam] = q;
39941                     this.store.load({
39942                         params: this.getParams(q)
39943                     });
39944                     this.expand();
39945                 }
39946             }else{
39947                 this.selectedIndex = -1;
39948                 this.onLoad();   
39949             }
39950         }
39951     },
39952
39953     // private
39954     getParams : function(q){
39955         var p = {};
39956         //p[this.queryParam] = q;
39957         if(this.pageSize){
39958             p.start = 0;
39959             p.limit = this.pageSize;
39960         }
39961         return p;
39962     },
39963
39964     /**
39965      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39966      */
39967     collapse : function(){
39968         if(!this.isExpanded()){
39969             return;
39970         }
39971         this.list.hide();
39972         Roo.get(document).un('mousedown', this.collapseIf, this);
39973         Roo.get(document).un('mousewheel', this.collapseIf, this);
39974         if (!this.editable) {
39975             Roo.get(document).un('keydown', this.listKeyPress, this);
39976         }
39977         this.fireEvent('collapse', this);
39978     },
39979
39980     // private
39981     collapseIf : function(e){
39982         if(!e.within(this.wrap) && !e.within(this.list)){
39983             this.collapse();
39984         }
39985     },
39986
39987     /**
39988      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39989      */
39990     expand : function(){
39991         if(this.isExpanded() || !this.hasFocus){
39992             return;
39993         }
39994         this.list.alignTo(this.el, this.listAlign);
39995         this.list.show();
39996         Roo.get(document).on('mousedown', this.collapseIf, this);
39997         Roo.get(document).on('mousewheel', this.collapseIf, this);
39998         if (!this.editable) {
39999             Roo.get(document).on('keydown', this.listKeyPress, this);
40000         }
40001         
40002         this.fireEvent('expand', this);
40003     },
40004
40005     // private
40006     // Implements the default empty TriggerField.onTriggerClick function
40007     onTriggerClick : function(){
40008         if(this.disabled){
40009             return;
40010         }
40011         if(this.isExpanded()){
40012             this.collapse();
40013             if (!this.blockFocus) {
40014                 this.el.focus();
40015             }
40016             
40017         }else {
40018             this.hasFocus = true;
40019             if(this.triggerAction == 'all') {
40020                 this.doQuery(this.allQuery, true);
40021             } else {
40022                 this.doQuery(this.getRawValue());
40023             }
40024             if (!this.blockFocus) {
40025                 this.el.focus();
40026             }
40027         }
40028     },
40029     listKeyPress : function(e)
40030     {
40031         //Roo.log('listkeypress');
40032         // scroll to first matching element based on key pres..
40033         if (e.isSpecialKey()) {
40034             return false;
40035         }
40036         var k = String.fromCharCode(e.getKey()).toUpperCase();
40037         //Roo.log(k);
40038         var match  = false;
40039         var csel = this.view.getSelectedNodes();
40040         var cselitem = false;
40041         if (csel.length) {
40042             var ix = this.view.indexOf(csel[0]);
40043             cselitem  = this.store.getAt(ix);
40044             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40045                 cselitem = false;
40046             }
40047             
40048         }
40049         
40050         this.store.each(function(v) { 
40051             if (cselitem) {
40052                 // start at existing selection.
40053                 if (cselitem.id == v.id) {
40054                     cselitem = false;
40055                 }
40056                 return;
40057             }
40058                 
40059             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40060                 match = this.store.indexOf(v);
40061                 return false;
40062             }
40063         }, this);
40064         
40065         if (match === false) {
40066             return true; // no more action?
40067         }
40068         // scroll to?
40069         this.view.select(match);
40070         var sn = Roo.get(this.view.getSelectedNodes()[0])
40071         sn.scrollIntoView(sn.dom.parentNode, false);
40072     }
40073
40074     /** 
40075     * @cfg {Boolean} grow 
40076     * @hide 
40077     */
40078     /** 
40079     * @cfg {Number} growMin 
40080     * @hide 
40081     */
40082     /** 
40083     * @cfg {Number} growMax 
40084     * @hide 
40085     */
40086     /**
40087      * @hide
40088      * @method autoSize
40089      */
40090 });/*
40091  * Copyright(c) 2010-2012, Roo J Solutions Limited
40092  *
40093  * Licence LGPL
40094  *
40095  */
40096
40097 /**
40098  * @class Roo.form.ComboBoxArray
40099  * @extends Roo.form.TextField
40100  * A facebook style adder... for lists of email / people / countries  etc...
40101  * pick multiple items from a combo box, and shows each one.
40102  *
40103  *  Fred [x]  Brian [x]  [Pick another |v]
40104  *
40105  *
40106  *  For this to work: it needs various extra information
40107  *    - normal combo problay has
40108  *      name, hiddenName
40109  *    + displayField, valueField
40110  *
40111  *    For our purpose...
40112  *
40113  *
40114  *   If we change from 'extends' to wrapping...
40115  *   
40116  *  
40117  *
40118  
40119  
40120  * @constructor
40121  * Create a new ComboBoxArray.
40122  * @param {Object} config Configuration options
40123  */
40124  
40125
40126 Roo.form.ComboBoxArray = function(config)
40127 {
40128     
40129     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40130     
40131     this.items = new Roo.util.MixedCollection(false);
40132     
40133     // construct the child combo...
40134     
40135     
40136     
40137     
40138    
40139     
40140 }
40141
40142  
40143 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40144
40145     /**
40146      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40147      */
40148     
40149     lastData : false,
40150     
40151     // behavies liek a hiddne field
40152     inputType:      'hidden',
40153     /**
40154      * @cfg {Number} width The width of the box that displays the selected element
40155      */ 
40156     width:          300,
40157
40158     
40159     
40160     /**
40161      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40162      */
40163     name : false,
40164     /**
40165      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40166      */
40167     hiddenName : false,
40168     
40169     
40170     // private the array of items that are displayed..
40171     items  : false,
40172     // private - the hidden field el.
40173     hiddenEl : false,
40174     // private - the filed el..
40175     el : false,
40176     
40177     //validateValue : function() { return true; }, // all values are ok!
40178     //onAddClick: function() { },
40179     
40180     onRender : function(ct, position) 
40181     {
40182         
40183         // create the standard hidden element
40184         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40185         
40186         
40187         // give fake names to child combo;
40188         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40189         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40190         
40191         this.combo = Roo.factory(this.combo, Roo.form);
40192         this.combo.onRender(ct, position);
40193         if (typeof(this.combo.width) != 'undefined') {
40194             this.combo.onResize(this.combo.width,0);
40195         }
40196         
40197         this.combo.initEvents();
40198         
40199         // assigned so form know we need to do this..
40200         this.store          = this.combo.store;
40201         this.valueField     = this.combo.valueField;
40202         this.displayField   = this.combo.displayField ;
40203         
40204         
40205         this.combo.wrap.addClass('x-cbarray-grp');
40206         
40207         var cbwrap = this.combo.wrap.createChild(
40208             {tag: 'div', cls: 'x-cbarray-cb'},
40209             this.combo.el.dom
40210         );
40211         
40212              
40213         this.hiddenEl = this.combo.wrap.createChild({
40214             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40215         });
40216         this.el = this.combo.wrap.createChild({
40217             tag: 'input',  type:'hidden' , name: this.name, value : ''
40218         });
40219          //   this.el.dom.removeAttribute("name");
40220         
40221         
40222         this.outerWrap = this.combo.wrap;
40223         this.wrap = cbwrap;
40224         
40225         this.outerWrap.setWidth(this.width);
40226         this.outerWrap.dom.removeChild(this.el.dom);
40227         
40228         this.wrap.dom.appendChild(this.el.dom);
40229         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40230         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40231         
40232         this.combo.trigger.setStyle('position','relative');
40233         this.combo.trigger.setStyle('left', '0px');
40234         this.combo.trigger.setStyle('top', '2px');
40235         
40236         this.combo.el.setStyle('vertical-align', 'text-bottom');
40237         
40238         //this.trigger.setStyle('vertical-align', 'top');
40239         
40240         // this should use the code from combo really... on('add' ....)
40241         if (this.adder) {
40242             
40243         
40244             this.adder = this.outerWrap.createChild(
40245                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40246             var _t = this;
40247             this.adder.on('click', function(e) {
40248                 _t.fireEvent('adderclick', this, e);
40249             }, _t);
40250         }
40251         //var _t = this;
40252         //this.adder.on('click', this.onAddClick, _t);
40253         
40254         
40255         this.combo.on('select', function(cb, rec, ix) {
40256             this.addItem(rec.data);
40257             
40258             cb.setValue('');
40259             cb.el.dom.value = '';
40260             //cb.lastData = rec.data;
40261             // add to list
40262             
40263         }, this);
40264         
40265         
40266     },
40267     
40268     
40269     getName: function()
40270     {
40271         // returns hidden if it's set..
40272         if (!this.rendered) {return ''};
40273         return  this.hiddenName ? this.hiddenName : this.name;
40274         
40275     },
40276     
40277     
40278     onResize: function(w, h){
40279         
40280         return;
40281         // not sure if this is needed..
40282         //this.combo.onResize(w,h);
40283         
40284         if(typeof w != 'number'){
40285             // we do not handle it!?!?
40286             return;
40287         }
40288         var tw = this.combo.trigger.getWidth();
40289         tw += this.addicon ? this.addicon.getWidth() : 0;
40290         tw += this.editicon ? this.editicon.getWidth() : 0;
40291         var x = w - tw;
40292         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40293             
40294         this.combo.trigger.setStyle('left', '0px');
40295         
40296         if(this.list && this.listWidth === undefined){
40297             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40298             this.list.setWidth(lw);
40299             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40300         }
40301         
40302     
40303         
40304     },
40305     
40306     addItem: function(rec)
40307     {
40308         var valueField = this.combo.valueField;
40309         var displayField = this.combo.displayField;
40310         if (this.items.indexOfKey(rec[valueField]) > -1) {
40311             //console.log("GOT " + rec.data.id);
40312             return;
40313         }
40314         
40315         var x = new Roo.form.ComboBoxArray.Item({
40316             //id : rec[this.idField],
40317             data : rec,
40318             displayField : displayField ,
40319             tipField : displayField ,
40320             cb : this
40321         });
40322         // use the 
40323         this.items.add(rec[valueField],x);
40324         // add it before the element..
40325         this.updateHiddenEl();
40326         x.render(this.outerWrap, this.wrap.dom);
40327         // add the image handler..
40328     },
40329     
40330     updateHiddenEl : function()
40331     {
40332         this.validate();
40333         if (!this.hiddenEl) {
40334             return;
40335         }
40336         var ar = [];
40337         var idField = this.combo.valueField;
40338         
40339         this.items.each(function(f) {
40340             ar.push(f.data[idField]);
40341            
40342         });
40343         this.hiddenEl.dom.value = ar.join(',');
40344         this.validate();
40345     },
40346     
40347     reset : function()
40348     {
40349         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40350         this.items.each(function(f) {
40351            f.remove(); 
40352         });
40353         this.el.dom.value = '';
40354         if (this.hiddenEl) {
40355             this.hiddenEl.dom.value = '';
40356         }
40357         
40358     },
40359     getValue: function()
40360     {
40361         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40362     },
40363     setValue: function(v) // not a valid action - must use addItems..
40364     {
40365          
40366         this.reset();
40367         
40368         
40369         
40370         if (this.store.isLocal && (typeof(v) == 'string')) {
40371             // then we can use the store to find the values..
40372             // comma seperated at present.. this needs to allow JSON based encoding..
40373             this.hiddenEl.value  = v;
40374             var v_ar = [];
40375             Roo.each(v.split(','), function(k) {
40376                 Roo.log("CHECK " + this.valueField + ',' + k);
40377                 var li = this.store.query(this.valueField, k);
40378                 if (!li.length) {
40379                     return;
40380                 }
40381                 var add = {};
40382                 add[this.valueField] = k;
40383                 add[this.displayField] = li.item(0).data[this.displayField];
40384                 
40385                 this.addItem(add);
40386             }, this) 
40387              
40388         }
40389         if (typeof(v) == 'object') {
40390             // then let's assume it's an array of objects..
40391             Roo.each(v, function(l) {
40392                 this.addItem(l);
40393             }, this);
40394              
40395         }
40396         
40397         
40398     },
40399     setFromData: function(v)
40400     {
40401         // this recieves an object, if setValues is called.
40402         this.reset();
40403         this.el.dom.value = v[this.displayField];
40404         this.hiddenEl.dom.value = v[this.valueField];
40405         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40406             return;
40407         }
40408         var kv = v[this.valueField];
40409         var dv = v[this.displayField];
40410         kv = typeof(kv) != 'string' ? '' : kv;
40411         dv = typeof(dv) != 'string' ? '' : dv;
40412         
40413         
40414         var keys = kv.split(',');
40415         var display = dv.split(',');
40416         for (var i = 0 ; i < keys.length; i++) {
40417             
40418             add = {};
40419             add[this.valueField] = keys[i];
40420             add[this.displayField] = display[i];
40421             this.addItem(add);
40422         }
40423       
40424         
40425     },
40426     
40427     /**
40428      * Validates the combox array value
40429      * @return {Boolean} True if the value is valid, else false
40430      */
40431     validate : function(){
40432         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40433             this.clearInvalid();
40434             return true;
40435         }
40436         return false;
40437     },
40438     
40439     validateValue : function(value){
40440         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40441         
40442     },
40443     
40444     /*@
40445      * overide
40446      * 
40447      */
40448     isDirty : function() {
40449         if(this.disabled) {
40450             return false;
40451         }
40452         
40453         try {
40454             var d = Roo.decode(String(this.originalValue));
40455         } catch (e) {
40456             return String(this.getValue()) !== String(this.originalValue);
40457         }
40458         
40459         var originalValue = [];
40460         
40461         for (var i = 0; i < d.length; i++){
40462             originalValue.push(d[i][this.valueField]);
40463         }
40464         
40465         return String(this.getValue()) !== String(originalValue.join(','));
40466         
40467     }
40468     
40469 });
40470
40471
40472
40473 /**
40474  * @class Roo.form.ComboBoxArray.Item
40475  * @extends Roo.BoxComponent
40476  * A selected item in the list
40477  *  Fred [x]  Brian [x]  [Pick another |v]
40478  * 
40479  * @constructor
40480  * Create a new item.
40481  * @param {Object} config Configuration options
40482  */
40483  
40484 Roo.form.ComboBoxArray.Item = function(config) {
40485     config.id = Roo.id();
40486     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40487 }
40488
40489 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40490     data : {},
40491     cb: false,
40492     displayField : false,
40493     tipField : false,
40494     
40495     
40496     defaultAutoCreate : {
40497         tag: 'div',
40498         cls: 'x-cbarray-item',
40499         cn : [ 
40500             { tag: 'div' },
40501             {
40502                 tag: 'img',
40503                 width:16,
40504                 height : 16,
40505                 src : Roo.BLANK_IMAGE_URL ,
40506                 align: 'center'
40507             }
40508         ]
40509         
40510     },
40511     
40512  
40513     onRender : function(ct, position)
40514     {
40515         Roo.form.Field.superclass.onRender.call(this, ct, position);
40516         
40517         if(!this.el){
40518             var cfg = this.getAutoCreate();
40519             this.el = ct.createChild(cfg, position);
40520         }
40521         
40522         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40523         
40524         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40525             this.cb.renderer(this.data) :
40526             String.format('{0}',this.data[this.displayField]);
40527         
40528             
40529         this.el.child('div').dom.setAttribute('qtip',
40530                         String.format('{0}',this.data[this.tipField])
40531         );
40532         
40533         this.el.child('img').on('click', this.remove, this);
40534         
40535     },
40536    
40537     remove : function()
40538     {
40539         
40540         this.cb.items.remove(this);
40541         this.el.child('img').un('click', this.remove, this);
40542         this.el.remove();
40543         this.cb.updateHiddenEl();
40544     }
40545 });/*
40546  * Based on:
40547  * Ext JS Library 1.1.1
40548  * Copyright(c) 2006-2007, Ext JS, LLC.
40549  *
40550  * Originally Released Under LGPL - original licence link has changed is not relivant.
40551  *
40552  * Fork - LGPL
40553  * <script type="text/javascript">
40554  */
40555 /**
40556  * @class Roo.form.Checkbox
40557  * @extends Roo.form.Field
40558  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40559  * @constructor
40560  * Creates a new Checkbox
40561  * @param {Object} config Configuration options
40562  */
40563 Roo.form.Checkbox = function(config){
40564     Roo.form.Checkbox.superclass.constructor.call(this, config);
40565     this.addEvents({
40566         /**
40567          * @event check
40568          * Fires when the checkbox is checked or unchecked.
40569              * @param {Roo.form.Checkbox} this This checkbox
40570              * @param {Boolean} checked The new checked value
40571              */
40572         check : true
40573     });
40574 };
40575
40576 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40577     /**
40578      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40579      */
40580     focusClass : undefined,
40581     /**
40582      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40583      */
40584     fieldClass: "x-form-field",
40585     /**
40586      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40587      */
40588     checked: false,
40589     /**
40590      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40591      * {tag: "input", type: "checkbox", autocomplete: "off"})
40592      */
40593     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40594     /**
40595      * @cfg {String} boxLabel The text that appears beside the checkbox
40596      */
40597     boxLabel : "",
40598     /**
40599      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40600      */  
40601     inputValue : '1',
40602     /**
40603      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40604      */
40605      valueOff: '0', // value when not checked..
40606
40607     actionMode : 'viewEl', 
40608     //
40609     // private
40610     itemCls : 'x-menu-check-item x-form-item',
40611     groupClass : 'x-menu-group-item',
40612     inputType : 'hidden',
40613     
40614     
40615     inSetChecked: false, // check that we are not calling self...
40616     
40617     inputElement: false, // real input element?
40618     basedOn: false, // ????
40619     
40620     isFormField: true, // not sure where this is needed!!!!
40621
40622     onResize : function(){
40623         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40624         if(!this.boxLabel){
40625             this.el.alignTo(this.wrap, 'c-c');
40626         }
40627     },
40628
40629     initEvents : function(){
40630         Roo.form.Checkbox.superclass.initEvents.call(this);
40631         this.el.on("click", this.onClick,  this);
40632         this.el.on("change", this.onClick,  this);
40633     },
40634
40635
40636     getResizeEl : function(){
40637         return this.wrap;
40638     },
40639
40640     getPositionEl : function(){
40641         return this.wrap;
40642     },
40643
40644     // private
40645     onRender : function(ct, position){
40646         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40647         /*
40648         if(this.inputValue !== undefined){
40649             this.el.dom.value = this.inputValue;
40650         }
40651         */
40652         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40653         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40654         var viewEl = this.wrap.createChild({ 
40655             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40656         this.viewEl = viewEl;   
40657         this.wrap.on('click', this.onClick,  this); 
40658         
40659         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40660         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40661         
40662         
40663         
40664         if(this.boxLabel){
40665             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40666         //    viewEl.on('click', this.onClick,  this); 
40667         }
40668         //if(this.checked){
40669             this.setChecked(this.checked);
40670         //}else{
40671             //this.checked = this.el.dom;
40672         //}
40673
40674     },
40675
40676     // private
40677     initValue : Roo.emptyFn,
40678
40679     /**
40680      * Returns the checked state of the checkbox.
40681      * @return {Boolean} True if checked, else false
40682      */
40683     getValue : function(){
40684         if(this.el){
40685             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40686         }
40687         return this.valueOff;
40688         
40689     },
40690
40691         // private
40692     onClick : function(){ 
40693         this.setChecked(!this.checked);
40694
40695         //if(this.el.dom.checked != this.checked){
40696         //    this.setValue(this.el.dom.checked);
40697        // }
40698     },
40699
40700     /**
40701      * Sets the checked state of the checkbox.
40702      * On is always based on a string comparison between inputValue and the param.
40703      * @param {Boolean/String} value - the value to set 
40704      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40705      */
40706     setValue : function(v,suppressEvent){
40707         
40708         
40709         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40710         //if(this.el && this.el.dom){
40711         //    this.el.dom.checked = this.checked;
40712         //    this.el.dom.defaultChecked = this.checked;
40713         //}
40714         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40715         //this.fireEvent("check", this, this.checked);
40716     },
40717     // private..
40718     setChecked : function(state,suppressEvent)
40719     {
40720         if (this.inSetChecked) {
40721             this.checked = state;
40722             return;
40723         }
40724         
40725     
40726         if(this.wrap){
40727             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40728         }
40729         this.checked = state;
40730         if(suppressEvent !== true){
40731             this.fireEvent('check', this, state);
40732         }
40733         this.inSetChecked = true;
40734         this.el.dom.value = state ? this.inputValue : this.valueOff;
40735         this.inSetChecked = false;
40736         
40737     },
40738     // handle setting of hidden value by some other method!!?!?
40739     setFromHidden: function()
40740     {
40741         if(!this.el){
40742             return;
40743         }
40744         //console.log("SET FROM HIDDEN");
40745         //alert('setFrom hidden');
40746         this.setValue(this.el.dom.value);
40747     },
40748     
40749     onDestroy : function()
40750     {
40751         if(this.viewEl){
40752             Roo.get(this.viewEl).remove();
40753         }
40754          
40755         Roo.form.Checkbox.superclass.onDestroy.call(this);
40756     }
40757
40758 });/*
40759  * Based on:
40760  * Ext JS Library 1.1.1
40761  * Copyright(c) 2006-2007, Ext JS, LLC.
40762  *
40763  * Originally Released Under LGPL - original licence link has changed is not relivant.
40764  *
40765  * Fork - LGPL
40766  * <script type="text/javascript">
40767  */
40768  
40769 /**
40770  * @class Roo.form.Radio
40771  * @extends Roo.form.Checkbox
40772  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40773  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40774  * @constructor
40775  * Creates a new Radio
40776  * @param {Object} config Configuration options
40777  */
40778 Roo.form.Radio = function(){
40779     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40780 };
40781 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40782     inputType: 'radio',
40783
40784     /**
40785      * If this radio is part of a group, it will return the selected value
40786      * @return {String}
40787      */
40788     getGroupValue : function(){
40789         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40790     },
40791     
40792     
40793     onRender : function(ct, position){
40794         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40795         
40796         if(this.inputValue !== undefined){
40797             this.el.dom.value = this.inputValue;
40798         }
40799          
40800         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40801         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40802         //var viewEl = this.wrap.createChild({ 
40803         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40804         //this.viewEl = viewEl;   
40805         //this.wrap.on('click', this.onClick,  this); 
40806         
40807         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40808         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40809         
40810         
40811         
40812         if(this.boxLabel){
40813             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40814         //    viewEl.on('click', this.onClick,  this); 
40815         }
40816          if(this.checked){
40817             this.el.dom.checked =   'checked' ;
40818         }
40819          
40820     } 
40821     
40822     
40823 });//<script type="text/javascript">
40824
40825 /*
40826  * Ext JS Library 1.1.1
40827  * Copyright(c) 2006-2007, Ext JS, LLC.
40828  * licensing@extjs.com
40829  * 
40830  * http://www.extjs.com/license
40831  */
40832  
40833  /*
40834   * 
40835   * Known bugs:
40836   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40837   * - IE ? - no idea how much works there.
40838   * 
40839   * 
40840   * 
40841   */
40842  
40843
40844 /**
40845  * @class Ext.form.HtmlEditor
40846  * @extends Ext.form.Field
40847  * Provides a lightweight HTML Editor component.
40848  *
40849  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40850  * 
40851  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40852  * supported by this editor.</b><br/><br/>
40853  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40854  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40855  */
40856 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40857       /**
40858      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40859      */
40860     toolbars : false,
40861     /**
40862      * @cfg {String} createLinkText The default text for the create link prompt
40863      */
40864     createLinkText : 'Please enter the URL for the link:',
40865     /**
40866      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40867      */
40868     defaultLinkValue : 'http:/'+'/',
40869    
40870      /**
40871      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40872      *                        Roo.resizable.
40873      */
40874     resizable : false,
40875      /**
40876      * @cfg {Number} height (in pixels)
40877      */   
40878     height: 300,
40879    /**
40880      * @cfg {Number} width (in pixels)
40881      */   
40882     width: 500,
40883     
40884     /**
40885      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40886      * 
40887      */
40888     stylesheets: false,
40889     
40890     // id of frame..
40891     frameId: false,
40892     
40893     // private properties
40894     validationEvent : false,
40895     deferHeight: true,
40896     initialized : false,
40897     activated : false,
40898     sourceEditMode : false,
40899     onFocus : Roo.emptyFn,
40900     iframePad:3,
40901     hideMode:'offsets',
40902     
40903     defaultAutoCreate : { // modified by initCompnoent..
40904         tag: "textarea",
40905         style:"width:500px;height:300px;",
40906         autocomplete: "off"
40907     },
40908
40909     // private
40910     initComponent : function(){
40911         this.addEvents({
40912             /**
40913              * @event initialize
40914              * Fires when the editor is fully initialized (including the iframe)
40915              * @param {HtmlEditor} this
40916              */
40917             initialize: true,
40918             /**
40919              * @event activate
40920              * Fires when the editor is first receives the focus. Any insertion must wait
40921              * until after this event.
40922              * @param {HtmlEditor} this
40923              */
40924             activate: true,
40925              /**
40926              * @event beforesync
40927              * Fires before the textarea is updated with content from the editor iframe. Return false
40928              * to cancel the sync.
40929              * @param {HtmlEditor} this
40930              * @param {String} html
40931              */
40932             beforesync: true,
40933              /**
40934              * @event beforepush
40935              * Fires before the iframe editor is updated with content from the textarea. Return false
40936              * to cancel the push.
40937              * @param {HtmlEditor} this
40938              * @param {String} html
40939              */
40940             beforepush: true,
40941              /**
40942              * @event sync
40943              * Fires when the textarea is updated with content from the editor iframe.
40944              * @param {HtmlEditor} this
40945              * @param {String} html
40946              */
40947             sync: true,
40948              /**
40949              * @event push
40950              * Fires when the iframe editor is updated with content from the textarea.
40951              * @param {HtmlEditor} this
40952              * @param {String} html
40953              */
40954             push: true,
40955              /**
40956              * @event editmodechange
40957              * Fires when the editor switches edit modes
40958              * @param {HtmlEditor} this
40959              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40960              */
40961             editmodechange: true,
40962             /**
40963              * @event editorevent
40964              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40965              * @param {HtmlEditor} this
40966              */
40967             editorevent: true
40968         });
40969         this.defaultAutoCreate =  {
40970             tag: "textarea",
40971             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40972             autocomplete: "off"
40973         };
40974     },
40975
40976     /**
40977      * Protected method that will not generally be called directly. It
40978      * is called when the editor creates its toolbar. Override this method if you need to
40979      * add custom toolbar buttons.
40980      * @param {HtmlEditor} editor
40981      */
40982     createToolbar : function(editor){
40983         if (!editor.toolbars || !editor.toolbars.length) {
40984             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40985         }
40986         
40987         for (var i =0 ; i < editor.toolbars.length;i++) {
40988             editor.toolbars[i] = Roo.factory(
40989                     typeof(editor.toolbars[i]) == 'string' ?
40990                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40991                 Roo.form.HtmlEditor);
40992             editor.toolbars[i].init(editor);
40993         }
40994          
40995         
40996     },
40997
40998     /**
40999      * Protected method that will not generally be called directly. It
41000      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41001      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41002      */
41003     getDocMarkup : function(){
41004         // body styles..
41005         var st = '';
41006         if (this.stylesheets === false) {
41007             
41008             Roo.get(document.head).select('style').each(function(node) {
41009                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41010             });
41011             
41012             Roo.get(document.head).select('link').each(function(node) { 
41013                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41014             });
41015             
41016         } else if (!this.stylesheets.length) {
41017                 // simple..
41018                 st = '<style type="text/css">' +
41019                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41020                    '</style>';
41021         } else {
41022             Roo.each(this.stylesheets, function(s) {
41023                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41024             });
41025             
41026         }
41027         
41028         st +=  '<style type="text/css">' +
41029             'IMG { cursor: pointer } ' +
41030         '</style>';
41031
41032         
41033         return '<html><head>' + st  +
41034             //<style type="text/css">' +
41035             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41036             //'</style>' +
41037             ' </head><body class="roo-htmleditor-body"></body></html>';
41038     },
41039
41040     // private
41041     onRender : function(ct, position)
41042     {
41043         var _t = this;
41044         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
41045         this.el.dom.style.border = '0 none';
41046         this.el.dom.setAttribute('tabIndex', -1);
41047         this.el.addClass('x-hidden');
41048         if(Roo.isIE){ // fix IE 1px bogus margin
41049             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41050         }
41051         this.wrap = this.el.wrap({
41052             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
41053         });
41054         
41055         if (this.resizable) {
41056             this.resizeEl = new Roo.Resizable(this.wrap, {
41057                 pinned : true,
41058                 wrap: true,
41059                 dynamic : true,
41060                 minHeight : this.height,
41061                 height: this.height,
41062                 handles : this.resizable,
41063                 width: this.width,
41064                 listeners : {
41065                     resize : function(r, w, h) {
41066                         _t.onResize(w,h); // -something
41067                     }
41068                 }
41069             });
41070             
41071         }
41072
41073         this.frameId = Roo.id();
41074         
41075         this.createToolbar(this);
41076         
41077       
41078         
41079         var iframe = this.wrap.createChild({
41080             tag: 'iframe',
41081             id: this.frameId,
41082             name: this.frameId,
41083             frameBorder : 'no',
41084             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41085         }, this.el
41086         );
41087         
41088        // console.log(iframe);
41089         //this.wrap.dom.appendChild(iframe);
41090
41091         this.iframe = iframe.dom;
41092
41093          this.assignDocWin();
41094         
41095         this.doc.designMode = 'on';
41096        
41097         this.doc.open();
41098         this.doc.write(this.getDocMarkup());
41099         this.doc.close();
41100
41101         
41102         var task = { // must defer to wait for browser to be ready
41103             run : function(){
41104                 //console.log("run task?" + this.doc.readyState);
41105                 this.assignDocWin();
41106                 if(this.doc.body || this.doc.readyState == 'complete'){
41107                     try {
41108                         this.doc.designMode="on";
41109                     } catch (e) {
41110                         return;
41111                     }
41112                     Roo.TaskMgr.stop(task);
41113                     this.initEditor.defer(10, this);
41114                 }
41115             },
41116             interval : 10,
41117             duration:10000,
41118             scope: this
41119         };
41120         Roo.TaskMgr.start(task);
41121
41122         if(!this.width){
41123             this.setSize(this.wrap.getSize());
41124         }
41125         if (this.resizeEl) {
41126             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
41127             // should trigger onReize..
41128         }
41129     },
41130
41131     // private
41132     onResize : function(w, h)
41133     {
41134         //Roo.log('resize: ' +w + ',' + h );
41135         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
41136         if(this.el && this.iframe){
41137             if(typeof w == 'number'){
41138                 var aw = w - this.wrap.getFrameWidth('lr');
41139                 this.el.setWidth(this.adjustWidth('textarea', aw));
41140                 this.iframe.style.width = aw + 'px';
41141             }
41142             if(typeof h == 'number'){
41143                 var tbh = 0;
41144                 for (var i =0; i < this.toolbars.length;i++) {
41145                     // fixme - ask toolbars for heights?
41146                     tbh += this.toolbars[i].tb.el.getHeight();
41147                     if (this.toolbars[i].footer) {
41148                         tbh += this.toolbars[i].footer.el.getHeight();
41149                     }
41150                 }
41151                 
41152                 
41153                 
41154                 
41155                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
41156                 ah -= 5; // knock a few pixes off for look..
41157                 this.el.setHeight(this.adjustWidth('textarea', ah));
41158                 this.iframe.style.height = ah + 'px';
41159                 if(this.doc){
41160                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
41161                 }
41162             }
41163         }
41164     },
41165
41166     /**
41167      * Toggles the editor between standard and source edit mode.
41168      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41169      */
41170     toggleSourceEdit : function(sourceEditMode){
41171         
41172         this.sourceEditMode = sourceEditMode === true;
41173         
41174         if(this.sourceEditMode){
41175 //            Roo.log('in');
41176 //            Roo.log(this.syncValue());
41177             this.syncValue();
41178             this.iframe.className = 'x-hidden';
41179             this.el.removeClass('x-hidden');
41180             this.el.dom.removeAttribute('tabIndex');
41181             this.el.focus();
41182         }else{
41183 //            Roo.log('out')
41184 //            Roo.log(this.pushValue()); 
41185             this.pushValue();
41186             this.iframe.className = '';
41187             this.el.addClass('x-hidden');
41188             this.el.dom.setAttribute('tabIndex', -1);
41189             this.deferFocus();
41190         }
41191         this.setSize(this.wrap.getSize());
41192         this.fireEvent('editmodechange', this, this.sourceEditMode);
41193     },
41194
41195     // private used internally
41196     createLink : function(){
41197         var url = prompt(this.createLinkText, this.defaultLinkValue);
41198         if(url && url != 'http:/'+'/'){
41199             this.relayCmd('createlink', url);
41200         }
41201     },
41202
41203     // private (for BoxComponent)
41204     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41205
41206     // private (for BoxComponent)
41207     getResizeEl : function(){
41208         return this.wrap;
41209     },
41210
41211     // private (for BoxComponent)
41212     getPositionEl : function(){
41213         return this.wrap;
41214     },
41215
41216     // private
41217     initEvents : function(){
41218         this.originalValue = this.getValue();
41219     },
41220
41221     /**
41222      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41223      * @method
41224      */
41225     markInvalid : Roo.emptyFn,
41226     /**
41227      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41228      * @method
41229      */
41230     clearInvalid : Roo.emptyFn,
41231
41232     setValue : function(v){
41233         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
41234         this.pushValue();
41235     },
41236
41237     /**
41238      * Protected method that will not generally be called directly. If you need/want
41239      * custom HTML cleanup, this is the method you should override.
41240      * @param {String} html The HTML to be cleaned
41241      * return {String} The cleaned HTML
41242      */
41243     cleanHtml : function(html){
41244         html = String(html);
41245         if(html.length > 5){
41246             if(Roo.isSafari){ // strip safari nonsense
41247                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41248             }
41249         }
41250         if(html == '&nbsp;'){
41251             html = '';
41252         }
41253         return html;
41254     },
41255
41256     /**
41257      * Protected method that will not generally be called directly. Syncs the contents
41258      * of the editor iframe with the textarea.
41259      */
41260     syncValue : function(){
41261         if(this.initialized){
41262             var bd = (this.doc.body || this.doc.documentElement);
41263             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41264             var html = bd.innerHTML;
41265             if(Roo.isSafari){
41266                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41267                 var m = bs.match(/text-align:(.*?);/i);
41268                 if(m && m[1]){
41269                     html = '<div style="'+m[0]+'">' + html + '</div>';
41270                 }
41271             }
41272             html = this.cleanHtml(html);
41273             // fix up the special chars.. normaly like back quotes in word...
41274             // however we do not want to do this with chinese..
41275             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41276                 var cc = b.charCodeAt();
41277                 if (
41278                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41279                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41280                     (cc >= 0xf900 && cc < 0xfb00 )
41281                 ) {
41282                         return b;
41283                 }
41284                 return "&#"+cc+";" 
41285             });
41286             if(this.fireEvent('beforesync', this, html) !== false){
41287                 this.el.dom.value = html;
41288                 this.fireEvent('sync', this, html);
41289             }
41290         }
41291     },
41292
41293     /**
41294      * Protected method that will not generally be called directly. Pushes the value of the textarea
41295      * into the iframe editor.
41296      */
41297     pushValue : function(){
41298         if(this.initialized){
41299             var v = this.el.dom.value;
41300             
41301             if(v.length < 1){
41302                 v = '&#160;';
41303             }
41304             
41305             if(this.fireEvent('beforepush', this, v) !== false){
41306                 var d = (this.doc.body || this.doc.documentElement);
41307                 d.innerHTML = v;
41308                 this.cleanUpPaste();
41309                 this.el.dom.value = d.innerHTML;
41310                 this.fireEvent('push', this, v);
41311             }
41312         }
41313     },
41314
41315     // private
41316     deferFocus : function(){
41317         this.focus.defer(10, this);
41318     },
41319
41320     // doc'ed in Field
41321     focus : function(){
41322         if(this.win && !this.sourceEditMode){
41323             this.win.focus();
41324         }else{
41325             this.el.focus();
41326         }
41327     },
41328     
41329     assignDocWin: function()
41330     {
41331         var iframe = this.iframe;
41332         
41333          if(Roo.isIE){
41334             this.doc = iframe.contentWindow.document;
41335             this.win = iframe.contentWindow;
41336         } else {
41337             if (!Roo.get(this.frameId)) {
41338                 return;
41339             }
41340             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41341             this.win = Roo.get(this.frameId).dom.contentWindow;
41342         }
41343     },
41344     
41345     // private
41346     initEditor : function(){
41347         //console.log("INIT EDITOR");
41348         this.assignDocWin();
41349         
41350         
41351         
41352         this.doc.designMode="on";
41353         this.doc.open();
41354         this.doc.write(this.getDocMarkup());
41355         this.doc.close();
41356         
41357         var dbody = (this.doc.body || this.doc.documentElement);
41358         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41359         // this copies styles from the containing element into thsi one..
41360         // not sure why we need all of this..
41361         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41362         ss['background-attachment'] = 'fixed'; // w3c
41363         dbody.bgProperties = 'fixed'; // ie
41364         Roo.DomHelper.applyStyles(dbody, ss);
41365         Roo.EventManager.on(this.doc, {
41366             //'mousedown': this.onEditorEvent,
41367             'mouseup': this.onEditorEvent,
41368             'dblclick': this.onEditorEvent,
41369             'click': this.onEditorEvent,
41370             'keyup': this.onEditorEvent,
41371             buffer:100,
41372             scope: this
41373         });
41374         if(Roo.isGecko){
41375             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41376         }
41377         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41378             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41379         }
41380         this.initialized = true;
41381
41382         this.fireEvent('initialize', this);
41383         this.pushValue();
41384     },
41385
41386     // private
41387     onDestroy : function(){
41388         
41389         
41390         
41391         if(this.rendered){
41392             
41393             for (var i =0; i < this.toolbars.length;i++) {
41394                 // fixme - ask toolbars for heights?
41395                 this.toolbars[i].onDestroy();
41396             }
41397             
41398             this.wrap.dom.innerHTML = '';
41399             this.wrap.remove();
41400         }
41401     },
41402
41403     // private
41404     onFirstFocus : function(){
41405         
41406         this.assignDocWin();
41407         
41408         
41409         this.activated = true;
41410         for (var i =0; i < this.toolbars.length;i++) {
41411             this.toolbars[i].onFirstFocus();
41412         }
41413        
41414         if(Roo.isGecko){ // prevent silly gecko errors
41415             this.win.focus();
41416             var s = this.win.getSelection();
41417             if(!s.focusNode || s.focusNode.nodeType != 3){
41418                 var r = s.getRangeAt(0);
41419                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41420                 r.collapse(true);
41421                 this.deferFocus();
41422             }
41423             try{
41424                 this.execCmd('useCSS', true);
41425                 this.execCmd('styleWithCSS', false);
41426             }catch(e){}
41427         }
41428         this.fireEvent('activate', this);
41429     },
41430
41431     // private
41432     adjustFont: function(btn){
41433         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41434         //if(Roo.isSafari){ // safari
41435         //    adjust *= 2;
41436        // }
41437         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41438         if(Roo.isSafari){ // safari
41439             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41440             v =  (v < 10) ? 10 : v;
41441             v =  (v > 48) ? 48 : v;
41442             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41443             
41444         }
41445         
41446         
41447         v = Math.max(1, v+adjust);
41448         
41449         this.execCmd('FontSize', v  );
41450     },
41451
41452     onEditorEvent : function(e){
41453         this.fireEvent('editorevent', this, e);
41454       //  this.updateToolbar();
41455         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41456     },
41457
41458     insertTag : function(tg)
41459     {
41460         // could be a bit smarter... -> wrap the current selected tRoo..
41461         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41462             
41463             range = this.createRange(this.getSelection());
41464             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41465             wrappingNode.appendChild(range.extractContents());
41466             range.insertNode(wrappingNode);
41467
41468             return;
41469             
41470             
41471             
41472         }
41473         this.execCmd("formatblock",   tg);
41474         
41475     },
41476     
41477     insertText : function(txt)
41478     {
41479         
41480         
41481         var range = this.createRange();
41482         range.deleteContents();
41483                //alert(Sender.getAttribute('label'));
41484                
41485         range.insertNode(this.doc.createTextNode(txt));
41486     } ,
41487     
41488     // private
41489     relayBtnCmd : function(btn){
41490         this.relayCmd(btn.cmd);
41491     },
41492
41493     /**
41494      * Executes a Midas editor command on the editor document and performs necessary focus and
41495      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41496      * @param {String} cmd The Midas command
41497      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41498      */
41499     relayCmd : function(cmd, value){
41500         this.win.focus();
41501         this.execCmd(cmd, value);
41502         this.fireEvent('editorevent', this);
41503         //this.updateToolbar();
41504         this.deferFocus();
41505     },
41506
41507     /**
41508      * Executes a Midas editor command directly on the editor document.
41509      * For visual commands, you should use {@link #relayCmd} instead.
41510      * <b>This should only be called after the editor is initialized.</b>
41511      * @param {String} cmd The Midas command
41512      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41513      */
41514     execCmd : function(cmd, value){
41515         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41516         this.syncValue();
41517     },
41518  
41519  
41520    
41521     /**
41522      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41523      * to insert tRoo.
41524      * @param {String} text | dom node.. 
41525      */
41526     insertAtCursor : function(text)
41527     {
41528         
41529         
41530         
41531         if(!this.activated){
41532             return;
41533         }
41534         /*
41535         if(Roo.isIE){
41536             this.win.focus();
41537             var r = this.doc.selection.createRange();
41538             if(r){
41539                 r.collapse(true);
41540                 r.pasteHTML(text);
41541                 this.syncValue();
41542                 this.deferFocus();
41543             
41544             }
41545             return;
41546         }
41547         */
41548         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41549             this.win.focus();
41550             
41551             
41552             // from jquery ui (MIT licenced)
41553             var range, node;
41554             var win = this.win;
41555             
41556             if (win.getSelection && win.getSelection().getRangeAt) {
41557                 range = win.getSelection().getRangeAt(0);
41558                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41559                 range.insertNode(node);
41560             } else if (win.document.selection && win.document.selection.createRange) {
41561                 // no firefox support
41562                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41563                 win.document.selection.createRange().pasteHTML(txt);
41564             } else {
41565                 // no firefox support
41566                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41567                 this.execCmd('InsertHTML', txt);
41568             } 
41569             
41570             this.syncValue();
41571             
41572             this.deferFocus();
41573         }
41574     },
41575  // private
41576     mozKeyPress : function(e){
41577         if(e.ctrlKey){
41578             var c = e.getCharCode(), cmd;
41579           
41580             if(c > 0){
41581                 c = String.fromCharCode(c).toLowerCase();
41582                 switch(c){
41583                     case 'b':
41584                         cmd = 'bold';
41585                         break;
41586                     case 'i':
41587                         cmd = 'italic';
41588                         break;
41589                     
41590                     case 'u':
41591                         cmd = 'underline';
41592                         break;
41593                     
41594                     case 'v':
41595                         this.cleanUpPaste.defer(100, this);
41596                         return;
41597                         
41598                 }
41599                 if(cmd){
41600                     this.win.focus();
41601                     this.execCmd(cmd);
41602                     this.deferFocus();
41603                     e.preventDefault();
41604                 }
41605                 
41606             }
41607         }
41608     },
41609
41610     // private
41611     fixKeys : function(){ // load time branching for fastest keydown performance
41612         if(Roo.isIE){
41613             return function(e){
41614                 var k = e.getKey(), r;
41615                 if(k == e.TAB){
41616                     e.stopEvent();
41617                     r = this.doc.selection.createRange();
41618                     if(r){
41619                         r.collapse(true);
41620                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41621                         this.deferFocus();
41622                     }
41623                     return;
41624                 }
41625                 
41626                 if(k == e.ENTER){
41627                     r = this.doc.selection.createRange();
41628                     if(r){
41629                         var target = r.parentElement();
41630                         if(!target || target.tagName.toLowerCase() != 'li'){
41631                             e.stopEvent();
41632                             r.pasteHTML('<br />');
41633                             r.collapse(false);
41634                             r.select();
41635                         }
41636                     }
41637                 }
41638                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41639                     this.cleanUpPaste.defer(100, this);
41640                     return;
41641                 }
41642                 
41643                 
41644             };
41645         }else if(Roo.isOpera){
41646             return function(e){
41647                 var k = e.getKey();
41648                 if(k == e.TAB){
41649                     e.stopEvent();
41650                     this.win.focus();
41651                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41652                     this.deferFocus();
41653                 }
41654                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41655                     this.cleanUpPaste.defer(100, this);
41656                     return;
41657                 }
41658                 
41659             };
41660         }else if(Roo.isSafari){
41661             return function(e){
41662                 var k = e.getKey();
41663                 
41664                 if(k == e.TAB){
41665                     e.stopEvent();
41666                     this.execCmd('InsertText','\t');
41667                     this.deferFocus();
41668                     return;
41669                 }
41670                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41671                     this.cleanUpPaste.defer(100, this);
41672                     return;
41673                 }
41674                 
41675              };
41676         }
41677     }(),
41678     
41679     getAllAncestors: function()
41680     {
41681         var p = this.getSelectedNode();
41682         var a = [];
41683         if (!p) {
41684             a.push(p); // push blank onto stack..
41685             p = this.getParentElement();
41686         }
41687         
41688         
41689         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41690             a.push(p);
41691             p = p.parentNode;
41692         }
41693         a.push(this.doc.body);
41694         return a;
41695     },
41696     lastSel : false,
41697     lastSelNode : false,
41698     
41699     
41700     getSelection : function() 
41701     {
41702         this.assignDocWin();
41703         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41704     },
41705     
41706     getSelectedNode: function() 
41707     {
41708         // this may only work on Gecko!!!
41709         
41710         // should we cache this!!!!
41711         
41712         
41713         
41714          
41715         var range = this.createRange(this.getSelection()).cloneRange();
41716         
41717         if (Roo.isIE) {
41718             var parent = range.parentElement();
41719             while (true) {
41720                 var testRange = range.duplicate();
41721                 testRange.moveToElementText(parent);
41722                 if (testRange.inRange(range)) {
41723                     break;
41724                 }
41725                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41726                     break;
41727                 }
41728                 parent = parent.parentElement;
41729             }
41730             return parent;
41731         }
41732         
41733         // is ancestor a text element.
41734         var ac =  range.commonAncestorContainer;
41735         if (ac.nodeType == 3) {
41736             ac = ac.parentNode;
41737         }
41738         
41739         var ar = ac.childNodes;
41740          
41741         var nodes = [];
41742         var other_nodes = [];
41743         var has_other_nodes = false;
41744         for (var i=0;i<ar.length;i++) {
41745             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41746                 continue;
41747             }
41748             // fullly contained node.
41749             
41750             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41751                 nodes.push(ar[i]);
41752                 continue;
41753             }
41754             
41755             // probably selected..
41756             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41757                 other_nodes.push(ar[i]);
41758                 continue;
41759             }
41760             // outer..
41761             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41762                 continue;
41763             }
41764             
41765             
41766             has_other_nodes = true;
41767         }
41768         if (!nodes.length && other_nodes.length) {
41769             nodes= other_nodes;
41770         }
41771         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41772             return false;
41773         }
41774         
41775         return nodes[0];
41776     },
41777     createRange: function(sel)
41778     {
41779         // this has strange effects when using with 
41780         // top toolbar - not sure if it's a great idea.
41781         //this.editor.contentWindow.focus();
41782         if (typeof sel != "undefined") {
41783             try {
41784                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41785             } catch(e) {
41786                 return this.doc.createRange();
41787             }
41788         } else {
41789             return this.doc.createRange();
41790         }
41791     },
41792     getParentElement: function()
41793     {
41794         
41795         this.assignDocWin();
41796         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41797         
41798         var range = this.createRange(sel);
41799          
41800         try {
41801             var p = range.commonAncestorContainer;
41802             while (p.nodeType == 3) { // text node
41803                 p = p.parentNode;
41804             }
41805             return p;
41806         } catch (e) {
41807             return null;
41808         }
41809     
41810     },
41811     /***
41812      *
41813      * Range intersection.. the hard stuff...
41814      *  '-1' = before
41815      *  '0' = hits..
41816      *  '1' = after.
41817      *         [ -- selected range --- ]
41818      *   [fail]                        [fail]
41819      *
41820      *    basically..
41821      *      if end is before start or  hits it. fail.
41822      *      if start is after end or hits it fail.
41823      *
41824      *   if either hits (but other is outside. - then it's not 
41825      *   
41826      *    
41827      **/
41828     
41829     
41830     // @see http://www.thismuchiknow.co.uk/?p=64.
41831     rangeIntersectsNode : function(range, node)
41832     {
41833         var nodeRange = node.ownerDocument.createRange();
41834         try {
41835             nodeRange.selectNode(node);
41836         } catch (e) {
41837             nodeRange.selectNodeContents(node);
41838         }
41839     
41840         var rangeStartRange = range.cloneRange();
41841         rangeStartRange.collapse(true);
41842     
41843         var rangeEndRange = range.cloneRange();
41844         rangeEndRange.collapse(false);
41845     
41846         var nodeStartRange = nodeRange.cloneRange();
41847         nodeStartRange.collapse(true);
41848     
41849         var nodeEndRange = nodeRange.cloneRange();
41850         nodeEndRange.collapse(false);
41851     
41852         return rangeStartRange.compareBoundaryPoints(
41853                  Range.START_TO_START, nodeEndRange) == -1 &&
41854                rangeEndRange.compareBoundaryPoints(
41855                  Range.START_TO_START, nodeStartRange) == 1;
41856         
41857          
41858     },
41859     rangeCompareNode : function(range, node)
41860     {
41861         var nodeRange = node.ownerDocument.createRange();
41862         try {
41863             nodeRange.selectNode(node);
41864         } catch (e) {
41865             nodeRange.selectNodeContents(node);
41866         }
41867         
41868         
41869         range.collapse(true);
41870     
41871         nodeRange.collapse(true);
41872      
41873         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41874         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41875          
41876         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41877         
41878         var nodeIsBefore   =  ss == 1;
41879         var nodeIsAfter    = ee == -1;
41880         
41881         if (nodeIsBefore && nodeIsAfter)
41882             return 0; // outer
41883         if (!nodeIsBefore && nodeIsAfter)
41884             return 1; //right trailed.
41885         
41886         if (nodeIsBefore && !nodeIsAfter)
41887             return 2;  // left trailed.
41888         // fully contined.
41889         return 3;
41890     },
41891
41892     // private? - in a new class?
41893     cleanUpPaste :  function()
41894     {
41895         // cleans up the whole document..
41896          Roo.log('cleanuppaste');
41897         this.cleanUpChildren(this.doc.body);
41898         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41899         if (clean != this.doc.body.innerHTML) {
41900             this.doc.body.innerHTML = clean;
41901         }
41902         
41903     },
41904     
41905     cleanWordChars : function(input) {// change the chars to hex code
41906         var he = Roo.form.HtmlEditor;
41907         
41908         var output = input;
41909         Roo.each(he.swapCodes, function(sw) { 
41910             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41911             
41912             output = output.replace(swapper, sw[1]);
41913         });
41914         
41915         return output;
41916     },
41917     
41918     
41919     cleanUpChildren : function (n)
41920     {
41921         if (!n.childNodes.length) {
41922             return;
41923         }
41924         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41925            this.cleanUpChild(n.childNodes[i]);
41926         }
41927     },
41928     
41929     
41930         
41931     
41932     cleanUpChild : function (node)
41933     {
41934         var ed = this;
41935         //console.log(node);
41936         if (node.nodeName == "#text") {
41937             // clean up silly Windows -- stuff?
41938             return; 
41939         }
41940         if (node.nodeName == "#comment") {
41941             node.parentNode.removeChild(node);
41942             // clean up silly Windows -- stuff?
41943             return; 
41944         }
41945         
41946         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41947             // remove node.
41948             node.parentNode.removeChild(node);
41949             return;
41950             
41951         }
41952         
41953         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41954         
41955         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41956         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41957         
41958         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41959         //    remove_keep_children = true;
41960         //}
41961         
41962         if (remove_keep_children) {
41963             this.cleanUpChildren(node);
41964             // inserts everything just before this node...
41965             while (node.childNodes.length) {
41966                 var cn = node.childNodes[0];
41967                 node.removeChild(cn);
41968                 node.parentNode.insertBefore(cn, node);
41969             }
41970             node.parentNode.removeChild(node);
41971             return;
41972         }
41973         
41974         if (!node.attributes || !node.attributes.length) {
41975             this.cleanUpChildren(node);
41976             return;
41977         }
41978         
41979         function cleanAttr(n,v)
41980         {
41981             
41982             if (v.match(/^\./) || v.match(/^\//)) {
41983                 return;
41984             }
41985             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41986                 return;
41987             }
41988             if (v.match(/^#/)) {
41989                 return;
41990             }
41991 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41992             node.removeAttribute(n);
41993             
41994         }
41995         
41996         function cleanStyle(n,v)
41997         {
41998             if (v.match(/expression/)) { //XSS?? should we even bother..
41999                 node.removeAttribute(n);
42000                 return;
42001             }
42002             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
42003             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
42004             
42005             
42006             var parts = v.split(/;/);
42007             var clean = [];
42008             
42009             Roo.each(parts, function(p) {
42010                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42011                 if (!p.length) {
42012                     return true;
42013                 }
42014                 var l = p.split(':').shift().replace(/\s+/g,'');
42015                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42016                 
42017                 
42018                 if ( cblack.indexOf(l) > -1) {
42019 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42020                     //node.removeAttribute(n);
42021                     return true;
42022                 }
42023                 //Roo.log()
42024                 // only allow 'c whitelisted system attributes'
42025                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42026 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42027                     //node.removeAttribute(n);
42028                     return true;
42029                 }
42030                 
42031                 
42032                  
42033                 
42034                 clean.push(p);
42035                 return true;
42036             });
42037             if (clean.length) { 
42038                 node.setAttribute(n, clean.join(';'));
42039             } else {
42040                 node.removeAttribute(n);
42041             }
42042             
42043         }
42044         
42045         
42046         for (var i = node.attributes.length-1; i > -1 ; i--) {
42047             var a = node.attributes[i];
42048             //console.log(a);
42049             
42050             if (a.name.toLowerCase().substr(0,2)=='on')  {
42051                 node.removeAttribute(a.name);
42052                 continue;
42053             }
42054             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
42055                 node.removeAttribute(a.name);
42056                 continue;
42057             }
42058             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
42059                 cleanAttr(a.name,a.value); // fixme..
42060                 continue;
42061             }
42062             if (a.name == 'style') {
42063                 cleanStyle(a.name,a.value);
42064                 continue;
42065             }
42066             /// clean up MS crap..
42067             // tecnically this should be a list of valid class'es..
42068             
42069             
42070             if (a.name == 'class') {
42071                 if (a.value.match(/^Mso/)) {
42072                     node.className = '';
42073                 }
42074                 
42075                 if (a.value.match(/body/)) {
42076                     node.className = '';
42077                 }
42078                 continue;
42079             }
42080             
42081             // style cleanup!?
42082             // class cleanup?
42083             
42084         }
42085         
42086         
42087         this.cleanUpChildren(node);
42088         
42089         
42090     }
42091     
42092     
42093     // hide stuff that is not compatible
42094     /**
42095      * @event blur
42096      * @hide
42097      */
42098     /**
42099      * @event change
42100      * @hide
42101      */
42102     /**
42103      * @event focus
42104      * @hide
42105      */
42106     /**
42107      * @event specialkey
42108      * @hide
42109      */
42110     /**
42111      * @cfg {String} fieldClass @hide
42112      */
42113     /**
42114      * @cfg {String} focusClass @hide
42115      */
42116     /**
42117      * @cfg {String} autoCreate @hide
42118      */
42119     /**
42120      * @cfg {String} inputType @hide
42121      */
42122     /**
42123      * @cfg {String} invalidClass @hide
42124      */
42125     /**
42126      * @cfg {String} invalidText @hide
42127      */
42128     /**
42129      * @cfg {String} msgFx @hide
42130      */
42131     /**
42132      * @cfg {String} validateOnBlur @hide
42133      */
42134 });
42135
42136 Roo.form.HtmlEditor.white = [
42137         'area', 'br', 'img', 'input', 'hr', 'wbr',
42138         
42139        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42140        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42141        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42142        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42143        'table',   'ul',         'xmp', 
42144        
42145        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42146       'thead',   'tr', 
42147      
42148       'dir', 'menu', 'ol', 'ul', 'dl',
42149        
42150       'embed',  'object'
42151 ];
42152
42153
42154 Roo.form.HtmlEditor.black = [
42155     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42156         'applet', // 
42157         'base',   'basefont', 'bgsound', 'blink',  'body', 
42158         'frame',  'frameset', 'head',    'html',   'ilayer', 
42159         'iframe', 'layer',  'link',     'meta',    'object',   
42160         'script', 'style' ,'title',  'xml' // clean later..
42161 ];
42162 Roo.form.HtmlEditor.clean = [
42163     'script', 'style', 'title', 'xml'
42164 ];
42165 Roo.form.HtmlEditor.remove = [
42166     'font'
42167 ];
42168 // attributes..
42169
42170 Roo.form.HtmlEditor.ablack = [
42171     'on'
42172 ];
42173     
42174 Roo.form.HtmlEditor.aclean = [ 
42175     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42176 ];
42177
42178 // protocols..
42179 Roo.form.HtmlEditor.pwhite= [
42180         'http',  'https',  'mailto'
42181 ];
42182
42183 // white listed style attributes.
42184 Roo.form.HtmlEditor.cwhite= [
42185       //  'text-align', /// default is to allow most things..
42186       
42187          
42188 //        'font-size'//??
42189 ];
42190
42191 // black listed style attributes.
42192 Roo.form.HtmlEditor.cblack= [
42193       //  'font-size' -- this can be set by the project 
42194 ];
42195
42196
42197 Roo.form.HtmlEditor.swapCodes   =[ 
42198     [    8211, "--" ], 
42199     [    8212, "--" ], 
42200     [    8216,  "'" ],  
42201     [    8217, "'" ],  
42202     [    8220, '"' ],  
42203     [    8221, '"' ],  
42204     [    8226, "*" ],  
42205     [    8230, "..." ]
42206 ]; 
42207
42208     // <script type="text/javascript">
42209 /*
42210  * Based on
42211  * Ext JS Library 1.1.1
42212  * Copyright(c) 2006-2007, Ext JS, LLC.
42213  *  
42214  
42215  */
42216
42217 /**
42218  * @class Roo.form.HtmlEditorToolbar1
42219  * Basic Toolbar
42220  * 
42221  * Usage:
42222  *
42223  new Roo.form.HtmlEditor({
42224     ....
42225     toolbars : [
42226         new Roo.form.HtmlEditorToolbar1({
42227             disable : { fonts: 1 , format: 1, ..., ... , ...],
42228             btns : [ .... ]
42229         })
42230     }
42231      
42232  * 
42233  * @cfg {Object} disable List of elements to disable..
42234  * @cfg {Array} btns List of additional buttons.
42235  * 
42236  * 
42237  * NEEDS Extra CSS? 
42238  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42239  */
42240  
42241 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42242 {
42243     
42244     Roo.apply(this, config);
42245     
42246     // default disabled, based on 'good practice'..
42247     this.disable = this.disable || {};
42248     Roo.applyIf(this.disable, {
42249         fontSize : true,
42250         colors : true,
42251         specialElements : true
42252     });
42253     
42254     
42255     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42256     // dont call parent... till later.
42257 }
42258
42259 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42260     
42261     tb: false,
42262     
42263     rendered: false,
42264     
42265     editor : false,
42266     /**
42267      * @cfg {Object} disable  List of toolbar elements to disable
42268          
42269      */
42270     disable : false,
42271       /**
42272      * @cfg {Array} fontFamilies An array of available font families
42273      */
42274     fontFamilies : [
42275         'Arial',
42276         'Courier New',
42277         'Tahoma',
42278         'Times New Roman',
42279         'Verdana'
42280     ],
42281     
42282     specialChars : [
42283            "&#169;",
42284           "&#174;",     
42285           "&#8482;",    
42286           "&#163;" ,    
42287          // "&#8212;",    
42288           "&#8230;",    
42289           "&#247;" ,    
42290         //  "&#225;" ,     ?? a acute?
42291            "&#8364;"    , //Euro
42292        //   "&#8220;"    ,
42293         //  "&#8221;"    ,
42294         //  "&#8226;"    ,
42295           "&#176;"  //   , // degrees
42296
42297          // "&#233;"     , // e ecute
42298          // "&#250;"     , // u ecute?
42299     ],
42300     
42301     specialElements : [
42302         {
42303             text: "Insert Table",
42304             xtype: 'MenuItem',
42305             xns : Roo.Menu,
42306             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42307                 
42308         },
42309         {    
42310             text: "Insert Image",
42311             xtype: 'MenuItem',
42312             xns : Roo.Menu,
42313             ihtml : '<img src="about:blank"/>'
42314             
42315         }
42316         
42317          
42318     ],
42319     
42320     
42321     inputElements : [ 
42322             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42323             "input:submit", "input:button", "select", "textarea", "label" ],
42324     formats : [
42325         ["p"] ,  
42326         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42327         ["pre"],[ "code"], 
42328         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42329         ['div'],['span']
42330     ],
42331     
42332     cleanStyles : [
42333         "font-size"
42334     ],
42335      /**
42336      * @cfg {String} defaultFont default font to use.
42337      */
42338     defaultFont: 'tahoma',
42339    
42340     fontSelect : false,
42341     
42342     
42343     formatCombo : false,
42344     
42345     init : function(editor)
42346     {
42347         this.editor = editor;
42348         
42349         
42350         var fid = editor.frameId;
42351         var etb = this;
42352         function btn(id, toggle, handler){
42353             var xid = fid + '-'+ id ;
42354             return {
42355                 id : xid,
42356                 cmd : id,
42357                 cls : 'x-btn-icon x-edit-'+id,
42358                 enableToggle:toggle !== false,
42359                 scope: editor, // was editor...
42360                 handler:handler||editor.relayBtnCmd,
42361                 clickEvent:'mousedown',
42362                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42363                 tabIndex:-1
42364             };
42365         }
42366         
42367         
42368         
42369         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42370         this.tb = tb;
42371          // stop form submits
42372         tb.el.on('click', function(e){
42373             e.preventDefault(); // what does this do?
42374         });
42375
42376         if(!this.disable.font) { // && !Roo.isSafari){
42377             /* why no safari for fonts 
42378             editor.fontSelect = tb.el.createChild({
42379                 tag:'select',
42380                 tabIndex: -1,
42381                 cls:'x-font-select',
42382                 html: this.createFontOptions()
42383             });
42384             
42385             editor.fontSelect.on('change', function(){
42386                 var font = editor.fontSelect.dom.value;
42387                 editor.relayCmd('fontname', font);
42388                 editor.deferFocus();
42389             }, editor);
42390             
42391             tb.add(
42392                 editor.fontSelect.dom,
42393                 '-'
42394             );
42395             */
42396             
42397         };
42398         if(!this.disable.formats){
42399             this.formatCombo = new Roo.form.ComboBox({
42400                 store: new Roo.data.SimpleStore({
42401                     id : 'tag',
42402                     fields: ['tag'],
42403                     data : this.formats // from states.js
42404                 }),
42405                 blockFocus : true,
42406                 name : '',
42407                 //autoCreate : {tag: "div",  size: "20"},
42408                 displayField:'tag',
42409                 typeAhead: false,
42410                 mode: 'local',
42411                 editable : false,
42412                 triggerAction: 'all',
42413                 emptyText:'Add tag',
42414                 selectOnFocus:true,
42415                 width:135,
42416                 listeners : {
42417                     'select': function(c, r, i) {
42418                         editor.insertTag(r.get('tag'));
42419                         editor.focus();
42420                     }
42421                 }
42422
42423             });
42424             tb.addField(this.formatCombo);
42425             
42426         }
42427         
42428         if(!this.disable.format){
42429             tb.add(
42430                 btn('bold'),
42431                 btn('italic'),
42432                 btn('underline')
42433             );
42434         };
42435         if(!this.disable.fontSize){
42436             tb.add(
42437                 '-',
42438                 
42439                 
42440                 btn('increasefontsize', false, editor.adjustFont),
42441                 btn('decreasefontsize', false, editor.adjustFont)
42442             );
42443         };
42444         
42445         
42446         if(!this.disable.colors){
42447             tb.add(
42448                 '-', {
42449                     id:editor.frameId +'-forecolor',
42450                     cls:'x-btn-icon x-edit-forecolor',
42451                     clickEvent:'mousedown',
42452                     tooltip: this.buttonTips['forecolor'] || undefined,
42453                     tabIndex:-1,
42454                     menu : new Roo.menu.ColorMenu({
42455                         allowReselect: true,
42456                         focus: Roo.emptyFn,
42457                         value:'000000',
42458                         plain:true,
42459                         selectHandler: function(cp, color){
42460                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42461                             editor.deferFocus();
42462                         },
42463                         scope: editor,
42464                         clickEvent:'mousedown'
42465                     })
42466                 }, {
42467                     id:editor.frameId +'backcolor',
42468                     cls:'x-btn-icon x-edit-backcolor',
42469                     clickEvent:'mousedown',
42470                     tooltip: this.buttonTips['backcolor'] || undefined,
42471                     tabIndex:-1,
42472                     menu : new Roo.menu.ColorMenu({
42473                         focus: Roo.emptyFn,
42474                         value:'FFFFFF',
42475                         plain:true,
42476                         allowReselect: true,
42477                         selectHandler: function(cp, color){
42478                             if(Roo.isGecko){
42479                                 editor.execCmd('useCSS', false);
42480                                 editor.execCmd('hilitecolor', color);
42481                                 editor.execCmd('useCSS', true);
42482                                 editor.deferFocus();
42483                             }else{
42484                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42485                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42486                                 editor.deferFocus();
42487                             }
42488                         },
42489                         scope:editor,
42490                         clickEvent:'mousedown'
42491                     })
42492                 }
42493             );
42494         };
42495         // now add all the items...
42496         
42497
42498         if(!this.disable.alignments){
42499             tb.add(
42500                 '-',
42501                 btn('justifyleft'),
42502                 btn('justifycenter'),
42503                 btn('justifyright')
42504             );
42505         };
42506
42507         //if(!Roo.isSafari){
42508             if(!this.disable.links){
42509                 tb.add(
42510                     '-',
42511                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
42512                 );
42513             };
42514
42515             if(!this.disable.lists){
42516                 tb.add(
42517                     '-',
42518                     btn('insertorderedlist'),
42519                     btn('insertunorderedlist')
42520                 );
42521             }
42522             if(!this.disable.sourceEdit){
42523                 tb.add(
42524                     '-',
42525                     btn('sourceedit', true, function(btn){
42526                         this.toggleSourceEdit(btn.pressed);
42527                     })
42528                 );
42529             }
42530         //}
42531         
42532         var smenu = { };
42533         // special menu.. - needs to be tidied up..
42534         if (!this.disable.special) {
42535             smenu = {
42536                 text: "&#169;",
42537                 cls: 'x-edit-none',
42538                 
42539                 menu : {
42540                     items : []
42541                 }
42542             };
42543             for (var i =0; i < this.specialChars.length; i++) {
42544                 smenu.menu.items.push({
42545                     
42546                     html: this.specialChars[i],
42547                     handler: function(a,b) {
42548                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42549                         //editor.insertAtCursor(a.html);
42550                         
42551                     },
42552                     tabIndex:-1
42553                 });
42554             }
42555             
42556             
42557             tb.add(smenu);
42558             
42559             
42560         }
42561         
42562         var cmenu = { };
42563         if (!this.disable.cleanStyles) {
42564             cmenu = {
42565                 cls: 'x-btn-icon x-btn-clear',
42566                 
42567                 menu : {
42568                     items : []
42569                 }
42570             };
42571             for (var i =0; i < this.cleanStyles.length; i++) {
42572                 cmenu.menu.items.push({
42573                     actiontype : this.cleanStyles[i],
42574                     html: 'Remove ' + this.cleanStyles[i],
42575                     handler: function(a,b) {
42576                         Roo.log(a);
42577                         Roo.log(b);
42578                         var c = Roo.get(editor.doc.body);
42579                         c.select('[style]').each(function(s) {
42580                             s.dom.style.removeProperty(a.actiontype);
42581                         });
42582                         
42583                     },
42584                     tabIndex:-1
42585                 });
42586             }
42587             
42588             tb.add(cmenu);
42589         }
42590          
42591         if (!this.disable.specialElements) {
42592             var semenu = {
42593                 text: "Other;",
42594                 cls: 'x-edit-none',
42595                 menu : {
42596                     items : []
42597                 }
42598             };
42599             for (var i =0; i < this.specialElements.length; i++) {
42600                 semenu.menu.items.push(
42601                     Roo.apply({ 
42602                         handler: function(a,b) {
42603                             editor.insertAtCursor(this.ihtml);
42604                         }
42605                     }, this.specialElements[i])
42606                 );
42607                     
42608             }
42609             
42610             tb.add(semenu);
42611             
42612             
42613         }
42614          
42615         
42616         if (this.btns) {
42617             for(var i =0; i< this.btns.length;i++) {
42618                 var b = Roo.factory(this.btns[i],Roo.form);
42619                 b.cls =  'x-edit-none';
42620                 b.scope = editor;
42621                 tb.add(b);
42622             }
42623         
42624         }
42625         
42626         
42627         
42628         // disable everything...
42629         
42630         this.tb.items.each(function(item){
42631            if(item.id != editor.frameId+ '-sourceedit'){
42632                 item.disable();
42633             }
42634         });
42635         this.rendered = true;
42636         
42637         // the all the btns;
42638         editor.on('editorevent', this.updateToolbar, this);
42639         // other toolbars need to implement this..
42640         //editor.on('editmodechange', this.updateToolbar, this);
42641     },
42642     
42643     
42644     
42645     /**
42646      * Protected method that will not generally be called directly. It triggers
42647      * a toolbar update by reading the markup state of the current selection in the editor.
42648      */
42649     updateToolbar: function(){
42650
42651         if(!this.editor.activated){
42652             this.editor.onFirstFocus();
42653             return;
42654         }
42655
42656         var btns = this.tb.items.map, 
42657             doc = this.editor.doc,
42658             frameId = this.editor.frameId;
42659
42660         if(!this.disable.font && !Roo.isSafari){
42661             /*
42662             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42663             if(name != this.fontSelect.dom.value){
42664                 this.fontSelect.dom.value = name;
42665             }
42666             */
42667         }
42668         if(!this.disable.format){
42669             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42670             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42671             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42672         }
42673         if(!this.disable.alignments){
42674             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42675             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42676             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42677         }
42678         if(!Roo.isSafari && !this.disable.lists){
42679             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42680             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42681         }
42682         
42683         var ans = this.editor.getAllAncestors();
42684         if (this.formatCombo) {
42685             
42686             
42687             var store = this.formatCombo.store;
42688             this.formatCombo.setValue("");
42689             for (var i =0; i < ans.length;i++) {
42690                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42691                     // select it..
42692                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42693                     break;
42694                 }
42695             }
42696         }
42697         
42698         
42699         
42700         // hides menus... - so this cant be on a menu...
42701         Roo.menu.MenuMgr.hideAll();
42702
42703         //this.editorsyncValue();
42704     },
42705    
42706     
42707     createFontOptions : function(){
42708         var buf = [], fs = this.fontFamilies, ff, lc;
42709         
42710         
42711         
42712         for(var i = 0, len = fs.length; i< len; i++){
42713             ff = fs[i];
42714             lc = ff.toLowerCase();
42715             buf.push(
42716                 '<option value="',lc,'" style="font-family:',ff,';"',
42717                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42718                     ff,
42719                 '</option>'
42720             );
42721         }
42722         return buf.join('');
42723     },
42724     
42725     toggleSourceEdit : function(sourceEditMode){
42726         if(sourceEditMode === undefined){
42727             sourceEditMode = !this.sourceEditMode;
42728         }
42729         this.sourceEditMode = sourceEditMode === true;
42730         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42731         // just toggle the button?
42732         if(btn.pressed !== this.editor.sourceEditMode){
42733             btn.toggle(this.editor.sourceEditMode);
42734             return;
42735         }
42736         
42737         if(this.sourceEditMode){
42738             this.tb.items.each(function(item){
42739                 if(item.cmd != 'sourceedit'){
42740                     item.disable();
42741                 }
42742             });
42743           
42744         }else{
42745             if(this.initialized){
42746                 this.tb.items.each(function(item){
42747                     item.enable();
42748                 });
42749             }
42750             
42751         }
42752         // tell the editor that it's been pressed..
42753         this.editor.toggleSourceEdit(sourceEditMode);
42754        
42755     },
42756      /**
42757      * Object collection of toolbar tooltips for the buttons in the editor. The key
42758      * is the command id associated with that button and the value is a valid QuickTips object.
42759      * For example:
42760 <pre><code>
42761 {
42762     bold : {
42763         title: 'Bold (Ctrl+B)',
42764         text: 'Make the selected text bold.',
42765         cls: 'x-html-editor-tip'
42766     },
42767     italic : {
42768         title: 'Italic (Ctrl+I)',
42769         text: 'Make the selected text italic.',
42770         cls: 'x-html-editor-tip'
42771     },
42772     ...
42773 </code></pre>
42774     * @type Object
42775      */
42776     buttonTips : {
42777         bold : {
42778             title: 'Bold (Ctrl+B)',
42779             text: 'Make the selected text bold.',
42780             cls: 'x-html-editor-tip'
42781         },
42782         italic : {
42783             title: 'Italic (Ctrl+I)',
42784             text: 'Make the selected text italic.',
42785             cls: 'x-html-editor-tip'
42786         },
42787         underline : {
42788             title: 'Underline (Ctrl+U)',
42789             text: 'Underline the selected text.',
42790             cls: 'x-html-editor-tip'
42791         },
42792         increasefontsize : {
42793             title: 'Grow Text',
42794             text: 'Increase the font size.',
42795             cls: 'x-html-editor-tip'
42796         },
42797         decreasefontsize : {
42798             title: 'Shrink Text',
42799             text: 'Decrease the font size.',
42800             cls: 'x-html-editor-tip'
42801         },
42802         backcolor : {
42803             title: 'Text Highlight Color',
42804             text: 'Change the background color of the selected text.',
42805             cls: 'x-html-editor-tip'
42806         },
42807         forecolor : {
42808             title: 'Font Color',
42809             text: 'Change the color of the selected text.',
42810             cls: 'x-html-editor-tip'
42811         },
42812         justifyleft : {
42813             title: 'Align Text Left',
42814             text: 'Align text to the left.',
42815             cls: 'x-html-editor-tip'
42816         },
42817         justifycenter : {
42818             title: 'Center Text',
42819             text: 'Center text in the editor.',
42820             cls: 'x-html-editor-tip'
42821         },
42822         justifyright : {
42823             title: 'Align Text Right',
42824             text: 'Align text to the right.',
42825             cls: 'x-html-editor-tip'
42826         },
42827         insertunorderedlist : {
42828             title: 'Bullet List',
42829             text: 'Start a bulleted list.',
42830             cls: 'x-html-editor-tip'
42831         },
42832         insertorderedlist : {
42833             title: 'Numbered List',
42834             text: 'Start a numbered list.',
42835             cls: 'x-html-editor-tip'
42836         },
42837         createlink : {
42838             title: 'Hyperlink',
42839             text: 'Make the selected text a hyperlink.',
42840             cls: 'x-html-editor-tip'
42841         },
42842         sourceedit : {
42843             title: 'Source Edit',
42844             text: 'Switch to source editing mode.',
42845             cls: 'x-html-editor-tip'
42846         }
42847     },
42848     // private
42849     onDestroy : function(){
42850         if(this.rendered){
42851             
42852             this.tb.items.each(function(item){
42853                 if(item.menu){
42854                     item.menu.removeAll();
42855                     if(item.menu.el){
42856                         item.menu.el.destroy();
42857                     }
42858                 }
42859                 item.destroy();
42860             });
42861              
42862         }
42863     },
42864     onFirstFocus: function() {
42865         this.tb.items.each(function(item){
42866            item.enable();
42867         });
42868     }
42869 });
42870
42871
42872
42873
42874 // <script type="text/javascript">
42875 /*
42876  * Based on
42877  * Ext JS Library 1.1.1
42878  * Copyright(c) 2006-2007, Ext JS, LLC.
42879  *  
42880  
42881  */
42882
42883  
42884 /**
42885  * @class Roo.form.HtmlEditor.ToolbarContext
42886  * Context Toolbar
42887  * 
42888  * Usage:
42889  *
42890  new Roo.form.HtmlEditor({
42891     ....
42892     toolbars : [
42893         { xtype: 'ToolbarStandard', styles : {} }
42894         { xtype: 'ToolbarContext', disable : {} }
42895     ]
42896 })
42897
42898      
42899  * 
42900  * @config : {Object} disable List of elements to disable.. (not done yet.)
42901  * @config : {Object} styles  Map of styles available.
42902  * 
42903  */
42904
42905 Roo.form.HtmlEditor.ToolbarContext = function(config)
42906 {
42907     
42908     Roo.apply(this, config);
42909     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42910     // dont call parent... till later.
42911     this.styles = this.styles || {};
42912 }
42913
42914  
42915
42916 Roo.form.HtmlEditor.ToolbarContext.types = {
42917     'IMG' : {
42918         width : {
42919             title: "Width",
42920             width: 40
42921         },
42922         height:  {
42923             title: "Height",
42924             width: 40
42925         },
42926         align: {
42927             title: "Align",
42928             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42929             width : 80
42930             
42931         },
42932         border: {
42933             title: "Border",
42934             width: 40
42935         },
42936         alt: {
42937             title: "Alt",
42938             width: 120
42939         },
42940         src : {
42941             title: "Src",
42942             width: 220
42943         }
42944         
42945     },
42946     'A' : {
42947         name : {
42948             title: "Name",
42949             width: 50
42950         },
42951         target:  {
42952             title: "Target",
42953             width: 120
42954         },
42955         href:  {
42956             title: "Href",
42957             width: 220
42958         } // border?
42959         
42960     },
42961     'TABLE' : {
42962         rows : {
42963             title: "Rows",
42964             width: 20
42965         },
42966         cols : {
42967             title: "Cols",
42968             width: 20
42969         },
42970         width : {
42971             title: "Width",
42972             width: 40
42973         },
42974         height : {
42975             title: "Height",
42976             width: 40
42977         },
42978         border : {
42979             title: "Border",
42980             width: 20
42981         }
42982     },
42983     'TD' : {
42984         width : {
42985             title: "Width",
42986             width: 40
42987         },
42988         height : {
42989             title: "Height",
42990             width: 40
42991         },   
42992         align: {
42993             title: "Align",
42994             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42995             width: 80
42996         },
42997         valign: {
42998             title: "Valign",
42999             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43000             width: 80
43001         },
43002         colspan: {
43003             title: "Colspan",
43004             width: 20
43005             
43006         },
43007          'font-family'  : {
43008             title : "Font",
43009             style : 'fontFamily',
43010             displayField: 'display',
43011             optname : 'font-family',
43012             width: 140
43013         }
43014     },
43015     'INPUT' : {
43016         name : {
43017             title: "name",
43018             width: 120
43019         },
43020         value : {
43021             title: "Value",
43022             width: 120
43023         },
43024         width : {
43025             title: "Width",
43026             width: 40
43027         }
43028     },
43029     'LABEL' : {
43030         'for' : {
43031             title: "For",
43032             width: 120
43033         }
43034     },
43035     'TEXTAREA' : {
43036           name : {
43037             title: "name",
43038             width: 120
43039         },
43040         rows : {
43041             title: "Rows",
43042             width: 20
43043         },
43044         cols : {
43045             title: "Cols",
43046             width: 20
43047         }
43048     },
43049     'SELECT' : {
43050         name : {
43051             title: "name",
43052             width: 120
43053         },
43054         selectoptions : {
43055             title: "Options",
43056             width: 200
43057         }
43058     },
43059     
43060     // should we really allow this??
43061     // should this just be 
43062     'BODY' : {
43063         title : {
43064             title: "Title",
43065             width: 200,
43066             disabled : true
43067         }
43068     },
43069     'SPAN' : {
43070         'font-family'  : {
43071             title : "Font",
43072             style : 'fontFamily',
43073             displayField: 'display',
43074             optname : 'font-family',
43075             width: 140
43076         }
43077     },
43078     'DIV' : {
43079         'font-family'  : {
43080             title : "Font",
43081             style : 'fontFamily',
43082             displayField: 'display',
43083             optname : 'font-family',
43084             width: 140
43085         }
43086     },
43087      'P' : {
43088         'font-family'  : {
43089             title : "Font",
43090             style : 'fontFamily',
43091             displayField: 'display',
43092             optname : 'font-family',
43093             width: 140
43094         }
43095     },
43096     
43097     '*' : {
43098         // empty..
43099     }
43100
43101 };
43102
43103 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43104 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43105
43106 Roo.form.HtmlEditor.ToolbarContext.options = {
43107         'font-family'  : [ 
43108                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43109                 [ 'Courier New', 'Courier New'],
43110                 [ 'Tahoma', 'Tahoma'],
43111                 [ 'Times New Roman,serif', 'Times'],
43112                 [ 'Verdana','Verdana' ]
43113         ]
43114 };
43115
43116 // fixme - these need to be configurable..
43117  
43118
43119 Roo.form.HtmlEditor.ToolbarContext.types
43120
43121
43122 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43123     
43124     tb: false,
43125     
43126     rendered: false,
43127     
43128     editor : false,
43129     /**
43130      * @cfg {Object} disable  List of toolbar elements to disable
43131          
43132      */
43133     disable : false,
43134     /**
43135      * @cfg {Object} styles List of styles 
43136      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43137      *
43138      * These must be defined in the page, so they get rendered correctly..
43139      * .headline { }
43140      * TD.underline { }
43141      * 
43142      */
43143     styles : false,
43144     
43145     options: false,
43146     
43147     toolbars : false,
43148     
43149     init : function(editor)
43150     {
43151         this.editor = editor;
43152         
43153         
43154         var fid = editor.frameId;
43155         var etb = this;
43156         function btn(id, toggle, handler){
43157             var xid = fid + '-'+ id ;
43158             return {
43159                 id : xid,
43160                 cmd : id,
43161                 cls : 'x-btn-icon x-edit-'+id,
43162                 enableToggle:toggle !== false,
43163                 scope: editor, // was editor...
43164                 handler:handler||editor.relayBtnCmd,
43165                 clickEvent:'mousedown',
43166                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43167                 tabIndex:-1
43168             };
43169         }
43170         // create a new element.
43171         var wdiv = editor.wrap.createChild({
43172                 tag: 'div'
43173             }, editor.wrap.dom.firstChild.nextSibling, true);
43174         
43175         // can we do this more than once??
43176         
43177          // stop form submits
43178       
43179  
43180         // disable everything...
43181         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43182         this.toolbars = {};
43183            
43184         for (var i in  ty) {
43185           
43186             this.toolbars[i] = this.buildToolbar(ty[i],i);
43187         }
43188         this.tb = this.toolbars.BODY;
43189         this.tb.el.show();
43190         this.buildFooter();
43191         this.footer.show();
43192         editor.on('hide', function( ) { this.footer.hide() }, this);
43193         editor.on('show', function( ) { this.footer.show() }, this);
43194         
43195          
43196         this.rendered = true;
43197         
43198         // the all the btns;
43199         editor.on('editorevent', this.updateToolbar, this);
43200         // other toolbars need to implement this..
43201         //editor.on('editmodechange', this.updateToolbar, this);
43202     },
43203     
43204     
43205     
43206     /**
43207      * Protected method that will not generally be called directly. It triggers
43208      * a toolbar update by reading the markup state of the current selection in the editor.
43209      */
43210     updateToolbar: function(editor,ev,sel){
43211
43212         //Roo.log(ev);
43213         // capture mouse up - this is handy for selecting images..
43214         // perhaps should go somewhere else...
43215         if(!this.editor.activated){
43216              this.editor.onFirstFocus();
43217             return;
43218         }
43219         
43220         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43221         // selectNode - might want to handle IE?
43222         if (ev &&
43223             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43224             ev.target && ev.target.tagName == 'IMG') {
43225             // they have click on an image...
43226             // let's see if we can change the selection...
43227             sel = ev.target;
43228          
43229               var nodeRange = sel.ownerDocument.createRange();
43230             try {
43231                 nodeRange.selectNode(sel);
43232             } catch (e) {
43233                 nodeRange.selectNodeContents(sel);
43234             }
43235             //nodeRange.collapse(true);
43236             var s = editor.win.getSelection();
43237             s.removeAllRanges();
43238             s.addRange(nodeRange);
43239         }  
43240         
43241       
43242         var updateFooter = sel ? false : true;
43243         
43244         
43245         var ans = this.editor.getAllAncestors();
43246         
43247         // pick
43248         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43249         
43250         if (!sel) { 
43251             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
43252             sel = sel ? sel : this.editor.doc.body;
43253             sel = sel.tagName.length ? sel : this.editor.doc.body;
43254             
43255         }
43256         // pick a menu that exists..
43257         var tn = sel.tagName.toUpperCase();
43258         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43259         
43260         tn = sel.tagName.toUpperCase();
43261         
43262         var lastSel = this.tb.selectedNode
43263         
43264         this.tb.selectedNode = sel;
43265         
43266         // if current menu does not match..
43267         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43268                 
43269             this.tb.el.hide();
43270             ///console.log("show: " + tn);
43271             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43272             this.tb.el.show();
43273             // update name
43274             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43275             
43276             
43277             // update attributes
43278             if (this.tb.fields) {
43279                 this.tb.fields.each(function(e) {
43280                     if (e.stylename) {
43281                         e.setValue(sel.style[e.stylename]);
43282                         return;
43283                     } 
43284                    e.setValue(sel.getAttribute(e.attrname));
43285                 });
43286             }
43287             
43288             var hasStyles = false;
43289             for(var i in this.styles) {
43290                 hasStyles = true;
43291                 break;
43292             }
43293             
43294             // update styles
43295             if (hasStyles) { 
43296                 var st = this.tb.fields.item(0);
43297                 
43298                 st.store.removeAll();
43299                
43300                 
43301                 var cn = sel.className.split(/\s+/);
43302                 
43303                 var avs = [];
43304                 if (this.styles['*']) {
43305                     
43306                     Roo.each(this.styles['*'], function(v) {
43307                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43308                     });
43309                 }
43310                 if (this.styles[tn]) { 
43311                     Roo.each(this.styles[tn], function(v) {
43312                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43313                     });
43314                 }
43315                 
43316                 st.store.loadData(avs);
43317                 st.collapse();
43318                 st.setValue(cn);
43319             }
43320             // flag our selected Node.
43321             this.tb.selectedNode = sel;
43322            
43323            
43324             Roo.menu.MenuMgr.hideAll();
43325
43326         }
43327         
43328         if (!updateFooter) {
43329             //this.footDisp.dom.innerHTML = ''; 
43330             return;
43331         }
43332         // update the footer
43333         //
43334         var html = '';
43335         
43336         this.footerEls = ans.reverse();
43337         Roo.each(this.footerEls, function(a,i) {
43338             if (!a) { return; }
43339             html += html.length ? ' &gt; '  :  '';
43340             
43341             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43342             
43343         });
43344        
43345         // 
43346         var sz = this.footDisp.up('td').getSize();
43347         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43348         this.footDisp.dom.style.marginLeft = '5px';
43349         
43350         this.footDisp.dom.style.overflow = 'hidden';
43351         
43352         this.footDisp.dom.innerHTML = html;
43353             
43354         //this.editorsyncValue();
43355     },
43356      
43357     
43358    
43359        
43360     // private
43361     onDestroy : function(){
43362         if(this.rendered){
43363             
43364             this.tb.items.each(function(item){
43365                 if(item.menu){
43366                     item.menu.removeAll();
43367                     if(item.menu.el){
43368                         item.menu.el.destroy();
43369                     }
43370                 }
43371                 item.destroy();
43372             });
43373              
43374         }
43375     },
43376     onFirstFocus: function() {
43377         // need to do this for all the toolbars..
43378         this.tb.items.each(function(item){
43379            item.enable();
43380         });
43381     },
43382     buildToolbar: function(tlist, nm)
43383     {
43384         var editor = this.editor;
43385          // create a new element.
43386         var wdiv = editor.wrap.createChild({
43387                 tag: 'div'
43388             }, editor.wrap.dom.firstChild.nextSibling, true);
43389         
43390        
43391         var tb = new Roo.Toolbar(wdiv);
43392         // add the name..
43393         
43394         tb.add(nm+ ":&nbsp;");
43395         
43396         var styles = [];
43397         for(var i in this.styles) {
43398             styles.push(i);
43399         }
43400         
43401         // styles...
43402         if (styles && styles.length) {
43403             
43404             // this needs a multi-select checkbox...
43405             tb.addField( new Roo.form.ComboBox({
43406                 store: new Roo.data.SimpleStore({
43407                     id : 'val',
43408                     fields: ['val', 'selected'],
43409                     data : [] 
43410                 }),
43411                 name : '-roo-edit-className',
43412                 attrname : 'className',
43413                 displayField: 'val',
43414                 typeAhead: false,
43415                 mode: 'local',
43416                 editable : false,
43417                 triggerAction: 'all',
43418                 emptyText:'Select Style',
43419                 selectOnFocus:true,
43420                 width: 130,
43421                 listeners : {
43422                     'select': function(c, r, i) {
43423                         // initial support only for on class per el..
43424                         tb.selectedNode.className =  r ? r.get('val') : '';
43425                         editor.syncValue();
43426                     }
43427                 }
43428     
43429             }));
43430         }
43431         
43432         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43433         var tbops = tbc.options;
43434         
43435         for (var i in tlist) {
43436             
43437             var item = tlist[i];
43438             tb.add(item.title + ":&nbsp;");
43439             
43440             
43441             //optname == used so you can configure the options available..
43442             var opts = item.opts ? item.opts : false;
43443             if (item.optname) {
43444                 opts = tbops[item.optname];
43445            
43446             }
43447             
43448             if (opts) {
43449                 // opts == pulldown..
43450                 tb.addField( new Roo.form.ComboBox({
43451                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43452                         id : 'val',
43453                         fields: ['val', 'display'],
43454                         data : opts  
43455                     }),
43456                     name : '-roo-edit-' + i,
43457                     attrname : i,
43458                     stylename : item.style ? item.style : false,
43459                     displayField: item.displayField ? item.displayField : 'val',
43460                     valueField :  'val',
43461                     typeAhead: false,
43462                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43463                     editable : false,
43464                     triggerAction: 'all',
43465                     emptyText:'Select',
43466                     selectOnFocus:true,
43467                     width: item.width ? item.width  : 130,
43468                     listeners : {
43469                         'select': function(c, r, i) {
43470                             if (c.stylename) {
43471                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43472                                 return;
43473                             }
43474                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43475                         }
43476                     }
43477
43478                 }));
43479                 continue;
43480                     
43481                  
43482                 
43483                 tb.addField( new Roo.form.TextField({
43484                     name: i,
43485                     width: 100,
43486                     //allowBlank:false,
43487                     value: ''
43488                 }));
43489                 continue;
43490             }
43491             tb.addField( new Roo.form.TextField({
43492                 name: '-roo-edit-' + i,
43493                 attrname : i,
43494                 
43495                 width: item.width,
43496                 //allowBlank:true,
43497                 value: '',
43498                 listeners: {
43499                     'change' : function(f, nv, ov) {
43500                         tb.selectedNode.setAttribute(f.attrname, nv);
43501                     }
43502                 }
43503             }));
43504              
43505         }
43506         tb.addFill();
43507         var _this = this;
43508         tb.addButton( {
43509             text: 'Remove Tag',
43510     
43511             listeners : {
43512                 click : function ()
43513                 {
43514                     // remove
43515                     // undo does not work.
43516                      
43517                     var sn = tb.selectedNode;
43518                     
43519                     var pn = sn.parentNode;
43520                     
43521                     var stn =  sn.childNodes[0];
43522                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43523                     while (sn.childNodes.length) {
43524                         var node = sn.childNodes[0];
43525                         sn.removeChild(node);
43526                         //Roo.log(node);
43527                         pn.insertBefore(node, sn);
43528                         
43529                     }
43530                     pn.removeChild(sn);
43531                     var range = editor.createRange();
43532         
43533                     range.setStart(stn,0);
43534                     range.setEnd(en,0); //????
43535                     //range.selectNode(sel);
43536                     
43537                     
43538                     var selection = editor.getSelection();
43539                     selection.removeAllRanges();
43540                     selection.addRange(range);
43541                     
43542                     
43543                     
43544                     //_this.updateToolbar(null, null, pn);
43545                     _this.updateToolbar(null, null, null);
43546                     _this.footDisp.dom.innerHTML = ''; 
43547                 }
43548             }
43549             
43550                     
43551                 
43552             
43553         });
43554         
43555         
43556         tb.el.on('click', function(e){
43557             e.preventDefault(); // what does this do?
43558         });
43559         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43560         tb.el.hide();
43561         tb.name = nm;
43562         // dont need to disable them... as they will get hidden
43563         return tb;
43564          
43565         
43566     },
43567     buildFooter : function()
43568     {
43569         
43570         var fel = this.editor.wrap.createChild();
43571         this.footer = new Roo.Toolbar(fel);
43572         // toolbar has scrolly on left / right?
43573         var footDisp= new Roo.Toolbar.Fill();
43574         var _t = this;
43575         this.footer.add(
43576             {
43577                 text : '&lt;',
43578                 xtype: 'Button',
43579                 handler : function() {
43580                     _t.footDisp.scrollTo('left',0,true)
43581                 }
43582             }
43583         );
43584         this.footer.add( footDisp );
43585         this.footer.add( 
43586             {
43587                 text : '&gt;',
43588                 xtype: 'Button',
43589                 handler : function() {
43590                     // no animation..
43591                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43592                 }
43593             }
43594         );
43595         var fel = Roo.get(footDisp.el);
43596         fel.addClass('x-editor-context');
43597         this.footDispWrap = fel; 
43598         this.footDispWrap.overflow  = 'hidden';
43599         
43600         this.footDisp = fel.createChild();
43601         this.footDispWrap.on('click', this.onContextClick, this)
43602         
43603         
43604     },
43605     onContextClick : function (ev,dom)
43606     {
43607         ev.preventDefault();
43608         var  cn = dom.className;
43609         //Roo.log(cn);
43610         if (!cn.match(/x-ed-loc-/)) {
43611             return;
43612         }
43613         var n = cn.split('-').pop();
43614         var ans = this.footerEls;
43615         var sel = ans[n];
43616         
43617          // pick
43618         var range = this.editor.createRange();
43619         
43620         range.selectNodeContents(sel);
43621         //range.selectNode(sel);
43622         
43623         
43624         var selection = this.editor.getSelection();
43625         selection.removeAllRanges();
43626         selection.addRange(range);
43627         
43628         
43629         
43630         this.updateToolbar(null, null, sel);
43631         
43632         
43633     }
43634     
43635     
43636     
43637     
43638     
43639 });
43640
43641
43642
43643
43644
43645 /*
43646  * Based on:
43647  * Ext JS Library 1.1.1
43648  * Copyright(c) 2006-2007, Ext JS, LLC.
43649  *
43650  * Originally Released Under LGPL - original licence link has changed is not relivant.
43651  *
43652  * Fork - LGPL
43653  * <script type="text/javascript">
43654  */
43655  
43656 /**
43657  * @class Roo.form.BasicForm
43658  * @extends Roo.util.Observable
43659  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43660  * @constructor
43661  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43662  * @param {Object} config Configuration options
43663  */
43664 Roo.form.BasicForm = function(el, config){
43665     this.allItems = [];
43666     this.childForms = [];
43667     Roo.apply(this, config);
43668     /*
43669      * The Roo.form.Field items in this form.
43670      * @type MixedCollection
43671      */
43672      
43673      
43674     this.items = new Roo.util.MixedCollection(false, function(o){
43675         return o.id || (o.id = Roo.id());
43676     });
43677     this.addEvents({
43678         /**
43679          * @event beforeaction
43680          * Fires before any action is performed. Return false to cancel the action.
43681          * @param {Form} this
43682          * @param {Action} action The action to be performed
43683          */
43684         beforeaction: true,
43685         /**
43686          * @event actionfailed
43687          * Fires when an action fails.
43688          * @param {Form} this
43689          * @param {Action} action The action that failed
43690          */
43691         actionfailed : true,
43692         /**
43693          * @event actioncomplete
43694          * Fires when an action is completed.
43695          * @param {Form} this
43696          * @param {Action} action The action that completed
43697          */
43698         actioncomplete : true
43699     });
43700     if(el){
43701         this.initEl(el);
43702     }
43703     Roo.form.BasicForm.superclass.constructor.call(this);
43704 };
43705
43706 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43707     /**
43708      * @cfg {String} method
43709      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43710      */
43711     /**
43712      * @cfg {DataReader} reader
43713      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43714      * This is optional as there is built-in support for processing JSON.
43715      */
43716     /**
43717      * @cfg {DataReader} errorReader
43718      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43719      * This is completely optional as there is built-in support for processing JSON.
43720      */
43721     /**
43722      * @cfg {String} url
43723      * The URL to use for form actions if one isn't supplied in the action options.
43724      */
43725     /**
43726      * @cfg {Boolean} fileUpload
43727      * Set to true if this form is a file upload.
43728      */
43729      
43730     /**
43731      * @cfg {Object} baseParams
43732      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43733      */
43734      /**
43735      
43736     /**
43737      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43738      */
43739     timeout: 30,
43740
43741     // private
43742     activeAction : null,
43743
43744     /**
43745      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43746      * or setValues() data instead of when the form was first created.
43747      */
43748     trackResetOnLoad : false,
43749     
43750     
43751     /**
43752      * childForms - used for multi-tab forms
43753      * @type {Array}
43754      */
43755     childForms : false,
43756     
43757     /**
43758      * allItems - full list of fields.
43759      * @type {Array}
43760      */
43761     allItems : false,
43762     
43763     /**
43764      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43765      * element by passing it or its id or mask the form itself by passing in true.
43766      * @type Mixed
43767      */
43768     waitMsgTarget : false,
43769
43770     // private
43771     initEl : function(el){
43772         this.el = Roo.get(el);
43773         this.id = this.el.id || Roo.id();
43774         this.el.on('submit', this.onSubmit, this);
43775         this.el.addClass('x-form');
43776     },
43777
43778     // private
43779     onSubmit : function(e){
43780         e.stopEvent();
43781     },
43782
43783     /**
43784      * Returns true if client-side validation on the form is successful.
43785      * @return Boolean
43786      */
43787     isValid : function(){
43788         var valid = true;
43789         this.items.each(function(f){
43790            if(!f.validate()){
43791                valid = false;
43792            }
43793         });
43794         return valid;
43795     },
43796
43797     /**
43798      * Returns true if any fields in this form have changed since their original load.
43799      * @return Boolean
43800      */
43801     isDirty : function(){
43802         var dirty = false;
43803         this.items.each(function(f){
43804            if(f.isDirty()){
43805                dirty = true;
43806                return false;
43807            }
43808         });
43809         return dirty;
43810     },
43811
43812     /**
43813      * Performs a predefined action (submit or load) or custom actions you define on this form.
43814      * @param {String} actionName The name of the action type
43815      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43816      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43817      * accept other config options):
43818      * <pre>
43819 Property          Type             Description
43820 ----------------  ---------------  ----------------------------------------------------------------------------------
43821 url               String           The url for the action (defaults to the form's url)
43822 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43823 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43824 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43825                                    validate the form on the client (defaults to false)
43826      * </pre>
43827      * @return {BasicForm} this
43828      */
43829     doAction : function(action, options){
43830         if(typeof action == 'string'){
43831             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43832         }
43833         if(this.fireEvent('beforeaction', this, action) !== false){
43834             this.beforeAction(action);
43835             action.run.defer(100, action);
43836         }
43837         return this;
43838     },
43839
43840     /**
43841      * Shortcut to do a submit action.
43842      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43843      * @return {BasicForm} this
43844      */
43845     submit : function(options){
43846         this.doAction('submit', options);
43847         return this;
43848     },
43849
43850     /**
43851      * Shortcut to do a load action.
43852      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43853      * @return {BasicForm} this
43854      */
43855     load : function(options){
43856         this.doAction('load', options);
43857         return this;
43858     },
43859
43860     /**
43861      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43862      * @param {Record} record The record to edit
43863      * @return {BasicForm} this
43864      */
43865     updateRecord : function(record){
43866         record.beginEdit();
43867         var fs = record.fields;
43868         fs.each(function(f){
43869             var field = this.findField(f.name);
43870             if(field){
43871                 record.set(f.name, field.getValue());
43872             }
43873         }, this);
43874         record.endEdit();
43875         return this;
43876     },
43877
43878     /**
43879      * Loads an Roo.data.Record into this form.
43880      * @param {Record} record The record to load
43881      * @return {BasicForm} this
43882      */
43883     loadRecord : function(record){
43884         this.setValues(record.data);
43885         return this;
43886     },
43887
43888     // private
43889     beforeAction : function(action){
43890         var o = action.options;
43891         
43892        
43893         if(this.waitMsgTarget === true){
43894             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43895         }else if(this.waitMsgTarget){
43896             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43897             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43898         }else {
43899             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43900         }
43901          
43902     },
43903
43904     // private
43905     afterAction : function(action, success){
43906         this.activeAction = null;
43907         var o = action.options;
43908         
43909         if(this.waitMsgTarget === true){
43910             this.el.unmask();
43911         }else if(this.waitMsgTarget){
43912             this.waitMsgTarget.unmask();
43913         }else{
43914             Roo.MessageBox.updateProgress(1);
43915             Roo.MessageBox.hide();
43916         }
43917          
43918         if(success){
43919             if(o.reset){
43920                 this.reset();
43921             }
43922             Roo.callback(o.success, o.scope, [this, action]);
43923             this.fireEvent('actioncomplete', this, action);
43924             
43925         }else{
43926             
43927             // failure condition..
43928             // we have a scenario where updates need confirming.
43929             // eg. if a locking scenario exists..
43930             // we look for { errors : { needs_confirm : true }} in the response.
43931             if (
43932                 (typeof(action.result) != 'undefined')  &&
43933                 (typeof(action.result.errors) != 'undefined')  &&
43934                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43935            ){
43936                 var _t = this;
43937                 Roo.MessageBox.confirm(
43938                     "Change requires confirmation",
43939                     action.result.errorMsg,
43940                     function(r) {
43941                         if (r != 'yes') {
43942                             return;
43943                         }
43944                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43945                     }
43946                     
43947                 );
43948                 
43949                 
43950                 
43951                 return;
43952             }
43953             
43954             Roo.callback(o.failure, o.scope, [this, action]);
43955             // show an error message if no failed handler is set..
43956             if (!this.hasListener('actionfailed')) {
43957                 Roo.MessageBox.alert("Error",
43958                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43959                         action.result.errorMsg :
43960                         "Saving Failed, please check your entries or try again"
43961                 );
43962             }
43963             
43964             this.fireEvent('actionfailed', this, action);
43965         }
43966         
43967     },
43968
43969     /**
43970      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43971      * @param {String} id The value to search for
43972      * @return Field
43973      */
43974     findField : function(id){
43975         var field = this.items.get(id);
43976         if(!field){
43977             this.items.each(function(f){
43978                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43979                     field = f;
43980                     return false;
43981                 }
43982             });
43983         }
43984         return field || null;
43985     },
43986
43987     /**
43988      * Add a secondary form to this one, 
43989      * Used to provide tabbed forms. One form is primary, with hidden values 
43990      * which mirror the elements from the other forms.
43991      * 
43992      * @param {Roo.form.Form} form to add.
43993      * 
43994      */
43995     addForm : function(form)
43996     {
43997        
43998         if (this.childForms.indexOf(form) > -1) {
43999             // already added..
44000             return;
44001         }
44002         this.childForms.push(form);
44003         var n = '';
44004         Roo.each(form.allItems, function (fe) {
44005             
44006             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44007             if (this.findField(n)) { // already added..
44008                 return;
44009             }
44010             var add = new Roo.form.Hidden({
44011                 name : n
44012             });
44013             add.render(this.el);
44014             
44015             this.add( add );
44016         }, this);
44017         
44018     },
44019     /**
44020      * Mark fields in this form invalid in bulk.
44021      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44022      * @return {BasicForm} this
44023      */
44024     markInvalid : function(errors){
44025         if(errors instanceof Array){
44026             for(var i = 0, len = errors.length; i < len; i++){
44027                 var fieldError = errors[i];
44028                 var f = this.findField(fieldError.id);
44029                 if(f){
44030                     f.markInvalid(fieldError.msg);
44031                 }
44032             }
44033         }else{
44034             var field, id;
44035             for(id in errors){
44036                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44037                     field.markInvalid(errors[id]);
44038                 }
44039             }
44040         }
44041         Roo.each(this.childForms || [], function (f) {
44042             f.markInvalid(errors);
44043         });
44044         
44045         return this;
44046     },
44047
44048     /**
44049      * Set values for fields in this form in bulk.
44050      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44051      * @return {BasicForm} this
44052      */
44053     setValues : function(values){
44054         if(values instanceof Array){ // array of objects
44055             for(var i = 0, len = values.length; i < len; i++){
44056                 var v = values[i];
44057                 var f = this.findField(v.id);
44058                 if(f){
44059                     f.setValue(v.value);
44060                     if(this.trackResetOnLoad){
44061                         f.originalValue = f.getValue();
44062                     }
44063                 }
44064             }
44065         }else{ // object hash
44066             var field, id;
44067             for(id in values){
44068                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44069                     
44070                     if (field.setFromData && 
44071                         field.valueField && 
44072                         field.displayField &&
44073                         // combos' with local stores can 
44074                         // be queried via setValue()
44075                         // to set their value..
44076                         (field.store && !field.store.isLocal)
44077                         ) {
44078                         // it's a combo
44079                         var sd = { };
44080                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44081                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44082                         field.setFromData(sd);
44083                         
44084                     } else {
44085                         field.setValue(values[id]);
44086                     }
44087                     
44088                     
44089                     if(this.trackResetOnLoad){
44090                         field.originalValue = field.getValue();
44091                     }
44092                 }
44093             }
44094         }
44095          
44096         Roo.each(this.childForms || [], function (f) {
44097             f.setValues(values);
44098         });
44099                 
44100         return this;
44101     },
44102
44103     /**
44104      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44105      * they are returned as an array.
44106      * @param {Boolean} asString
44107      * @return {Object}
44108      */
44109     getValues : function(asString){
44110         if (this.childForms) {
44111             // copy values from the child forms
44112             Roo.each(this.childForms, function (f) {
44113                 this.setValues(f.getValues());
44114             }, this);
44115         }
44116         
44117         
44118         
44119         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44120         if(asString === true){
44121             return fs;
44122         }
44123         return Roo.urlDecode(fs);
44124     },
44125     
44126     /**
44127      * Returns the fields in this form as an object with key/value pairs. 
44128      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44129      * @return {Object}
44130      */
44131     getFieldValues : function(with_hidden)
44132     {
44133         if (this.childForms) {
44134             // copy values from the child forms
44135             // should this call getFieldValues - probably not as we do not currently copy
44136             // hidden fields when we generate..
44137             Roo.each(this.childForms, function (f) {
44138                 this.setValues(f.getValues());
44139             }, this);
44140         }
44141         
44142         var ret = {};
44143         this.items.each(function(f){
44144             if (!f.getName()) {
44145                 return;
44146             }
44147             var v = f.getValue();
44148             if (f.inputType =='radio') {
44149                 if (typeof(ret[f.getName()]) == 'undefined') {
44150                     ret[f.getName()] = ''; // empty..
44151                 }
44152                 
44153                 if (!f.el.dom.checked) {
44154                     return;
44155                     
44156                 }
44157                 v = f.el.dom.value;
44158                 
44159             }
44160             
44161             // not sure if this supported any more..
44162             if ((typeof(v) == 'object') && f.getRawValue) {
44163                 v = f.getRawValue() ; // dates..
44164             }
44165             // combo boxes where name != hiddenName...
44166             if (f.name != f.getName()) {
44167                 ret[f.name] = f.getRawValue();
44168             }
44169             ret[f.getName()] = v;
44170         });
44171         
44172         return ret;
44173     },
44174
44175     /**
44176      * Clears all invalid messages in this form.
44177      * @return {BasicForm} this
44178      */
44179     clearInvalid : function(){
44180         this.items.each(function(f){
44181            f.clearInvalid();
44182         });
44183         
44184         Roo.each(this.childForms || [], function (f) {
44185             f.clearInvalid();
44186         });
44187         
44188         
44189         return this;
44190     },
44191
44192     /**
44193      * Resets this form.
44194      * @return {BasicForm} this
44195      */
44196     reset : function(){
44197         this.items.each(function(f){
44198             f.reset();
44199         });
44200         
44201         Roo.each(this.childForms || [], function (f) {
44202             f.reset();
44203         });
44204        
44205         
44206         return this;
44207     },
44208
44209     /**
44210      * Add Roo.form components to this form.
44211      * @param {Field} field1
44212      * @param {Field} field2 (optional)
44213      * @param {Field} etc (optional)
44214      * @return {BasicForm} this
44215      */
44216     add : function(){
44217         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44218         return this;
44219     },
44220
44221
44222     /**
44223      * Removes a field from the items collection (does NOT remove its markup).
44224      * @param {Field} field
44225      * @return {BasicForm} this
44226      */
44227     remove : function(field){
44228         this.items.remove(field);
44229         return this;
44230     },
44231
44232     /**
44233      * Looks at the fields in this form, checks them for an id attribute,
44234      * and calls applyTo on the existing dom element with that id.
44235      * @return {BasicForm} this
44236      */
44237     render : function(){
44238         this.items.each(function(f){
44239             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44240                 f.applyTo(f.id);
44241             }
44242         });
44243         return this;
44244     },
44245
44246     /**
44247      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44248      * @param {Object} values
44249      * @return {BasicForm} this
44250      */
44251     applyToFields : function(o){
44252         this.items.each(function(f){
44253            Roo.apply(f, o);
44254         });
44255         return this;
44256     },
44257
44258     /**
44259      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44260      * @param {Object} values
44261      * @return {BasicForm} this
44262      */
44263     applyIfToFields : function(o){
44264         this.items.each(function(f){
44265            Roo.applyIf(f, o);
44266         });
44267         return this;
44268     }
44269 });
44270
44271 // back compat
44272 Roo.BasicForm = Roo.form.BasicForm;/*
44273  * Based on:
44274  * Ext JS Library 1.1.1
44275  * Copyright(c) 2006-2007, Ext JS, LLC.
44276  *
44277  * Originally Released Under LGPL - original licence link has changed is not relivant.
44278  *
44279  * Fork - LGPL
44280  * <script type="text/javascript">
44281  */
44282
44283 /**
44284  * @class Roo.form.Form
44285  * @extends Roo.form.BasicForm
44286  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44287  * @constructor
44288  * @param {Object} config Configuration options
44289  */
44290 Roo.form.Form = function(config){
44291     var xitems =  [];
44292     if (config.items) {
44293         xitems = config.items;
44294         delete config.items;
44295     }
44296    
44297     
44298     Roo.form.Form.superclass.constructor.call(this, null, config);
44299     this.url = this.url || this.action;
44300     if(!this.root){
44301         this.root = new Roo.form.Layout(Roo.applyIf({
44302             id: Roo.id()
44303         }, config));
44304     }
44305     this.active = this.root;
44306     /**
44307      * Array of all the buttons that have been added to this form via {@link addButton}
44308      * @type Array
44309      */
44310     this.buttons = [];
44311     this.allItems = [];
44312     this.addEvents({
44313         /**
44314          * @event clientvalidation
44315          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44316          * @param {Form} this
44317          * @param {Boolean} valid true if the form has passed client-side validation
44318          */
44319         clientvalidation: true,
44320         /**
44321          * @event rendered
44322          * Fires when the form is rendered
44323          * @param {Roo.form.Form} form
44324          */
44325         rendered : true
44326     });
44327     
44328     if (this.progressUrl) {
44329             // push a hidden field onto the list of fields..
44330             this.addxtype( {
44331                     xns: Roo.form, 
44332                     xtype : 'Hidden', 
44333                     name : 'UPLOAD_IDENTIFIER' 
44334             });
44335         }
44336         
44337     
44338     Roo.each(xitems, this.addxtype, this);
44339     
44340     
44341     
44342 };
44343
44344 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44345     /**
44346      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44347      */
44348     /**
44349      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44350      */
44351     /**
44352      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44353      */
44354     buttonAlign:'center',
44355
44356     /**
44357      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44358      */
44359     minButtonWidth:75,
44360
44361     /**
44362      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44363      * This property cascades to child containers if not set.
44364      */
44365     labelAlign:'left',
44366
44367     /**
44368      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44369      * fires a looping event with that state. This is required to bind buttons to the valid
44370      * state using the config value formBind:true on the button.
44371      */
44372     monitorValid : false,
44373
44374     /**
44375      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44376      */
44377     monitorPoll : 200,
44378     
44379     /**
44380      * @cfg {String} progressUrl - Url to return progress data 
44381      */
44382     
44383     progressUrl : false,
44384   
44385     /**
44386      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44387      * fields are added and the column is closed. If no fields are passed the column remains open
44388      * until end() is called.
44389      * @param {Object} config The config to pass to the column
44390      * @param {Field} field1 (optional)
44391      * @param {Field} field2 (optional)
44392      * @param {Field} etc (optional)
44393      * @return Column The column container object
44394      */
44395     column : function(c){
44396         var col = new Roo.form.Column(c);
44397         this.start(col);
44398         if(arguments.length > 1){ // duplicate code required because of Opera
44399             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44400             this.end();
44401         }
44402         return col;
44403     },
44404
44405     /**
44406      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44407      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44408      * until end() is called.
44409      * @param {Object} config The config to pass to the fieldset
44410      * @param {Field} field1 (optional)
44411      * @param {Field} field2 (optional)
44412      * @param {Field} etc (optional)
44413      * @return FieldSet The fieldset container object
44414      */
44415     fieldset : function(c){
44416         var fs = new Roo.form.FieldSet(c);
44417         this.start(fs);
44418         if(arguments.length > 1){ // duplicate code required because of Opera
44419             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44420             this.end();
44421         }
44422         return fs;
44423     },
44424
44425     /**
44426      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44427      * fields are added and the container is closed. If no fields are passed the container remains open
44428      * until end() is called.
44429      * @param {Object} config The config to pass to the Layout
44430      * @param {Field} field1 (optional)
44431      * @param {Field} field2 (optional)
44432      * @param {Field} etc (optional)
44433      * @return Layout The container object
44434      */
44435     container : function(c){
44436         var l = new Roo.form.Layout(c);
44437         this.start(l);
44438         if(arguments.length > 1){ // duplicate code required because of Opera
44439             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44440             this.end();
44441         }
44442         return l;
44443     },
44444
44445     /**
44446      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44447      * @param {Object} container A Roo.form.Layout or subclass of Layout
44448      * @return {Form} this
44449      */
44450     start : function(c){
44451         // cascade label info
44452         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44453         this.active.stack.push(c);
44454         c.ownerCt = this.active;
44455         this.active = c;
44456         return this;
44457     },
44458
44459     /**
44460      * Closes the current open container
44461      * @return {Form} this
44462      */
44463     end : function(){
44464         if(this.active == this.root){
44465             return this;
44466         }
44467         this.active = this.active.ownerCt;
44468         return this;
44469     },
44470
44471     /**
44472      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44473      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44474      * as the label of the field.
44475      * @param {Field} field1
44476      * @param {Field} field2 (optional)
44477      * @param {Field} etc. (optional)
44478      * @return {Form} this
44479      */
44480     add : function(){
44481         this.active.stack.push.apply(this.active.stack, arguments);
44482         this.allItems.push.apply(this.allItems,arguments);
44483         var r = [];
44484         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44485             if(a[i].isFormField){
44486                 r.push(a[i]);
44487             }
44488         }
44489         if(r.length > 0){
44490             Roo.form.Form.superclass.add.apply(this, r);
44491         }
44492         return this;
44493     },
44494     
44495
44496     
44497     
44498     
44499      /**
44500      * Find any element that has been added to a form, using it's ID or name
44501      * This can include framesets, columns etc. along with regular fields..
44502      * @param {String} id - id or name to find.
44503      
44504      * @return {Element} e - or false if nothing found.
44505      */
44506     findbyId : function(id)
44507     {
44508         var ret = false;
44509         if (!id) {
44510             return ret;
44511         }
44512         Roo.each(this.allItems, function(f){
44513             if (f.id == id || f.name == id ){
44514                 ret = f;
44515                 return false;
44516             }
44517         });
44518         return ret;
44519     },
44520
44521     
44522     
44523     /**
44524      * Render this form into the passed container. This should only be called once!
44525      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44526      * @return {Form} this
44527      */
44528     render : function(ct)
44529     {
44530         
44531         
44532         
44533         ct = Roo.get(ct);
44534         var o = this.autoCreate || {
44535             tag: 'form',
44536             method : this.method || 'POST',
44537             id : this.id || Roo.id()
44538         };
44539         this.initEl(ct.createChild(o));
44540
44541         this.root.render(this.el);
44542         
44543        
44544              
44545         this.items.each(function(f){
44546             f.render('x-form-el-'+f.id);
44547         });
44548
44549         if(this.buttons.length > 0){
44550             // tables are required to maintain order and for correct IE layout
44551             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44552                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44553                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44554             }}, null, true);
44555             var tr = tb.getElementsByTagName('tr')[0];
44556             for(var i = 0, len = this.buttons.length; i < len; i++) {
44557                 var b = this.buttons[i];
44558                 var td = document.createElement('td');
44559                 td.className = 'x-form-btn-td';
44560                 b.render(tr.appendChild(td));
44561             }
44562         }
44563         if(this.monitorValid){ // initialize after render
44564             this.startMonitoring();
44565         }
44566         this.fireEvent('rendered', this);
44567         return this;
44568     },
44569
44570     /**
44571      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44572      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44573      * object or a valid Roo.DomHelper element config
44574      * @param {Function} handler The function called when the button is clicked
44575      * @param {Object} scope (optional) The scope of the handler function
44576      * @return {Roo.Button}
44577      */
44578     addButton : function(config, handler, scope){
44579         var bc = {
44580             handler: handler,
44581             scope: scope,
44582             minWidth: this.minButtonWidth,
44583             hideParent:true
44584         };
44585         if(typeof config == "string"){
44586             bc.text = config;
44587         }else{
44588             Roo.apply(bc, config);
44589         }
44590         var btn = new Roo.Button(null, bc);
44591         this.buttons.push(btn);
44592         return btn;
44593     },
44594
44595      /**
44596      * Adds a series of form elements (using the xtype property as the factory method.
44597      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44598      * @param {Object} config 
44599      */
44600     
44601     addxtype : function()
44602     {
44603         var ar = Array.prototype.slice.call(arguments, 0);
44604         var ret = false;
44605         for(var i = 0; i < ar.length; i++) {
44606             if (!ar[i]) {
44607                 continue; // skip -- if this happends something invalid got sent, we 
44608                 // should ignore it, as basically that interface element will not show up
44609                 // and that should be pretty obvious!!
44610             }
44611             
44612             if (Roo.form[ar[i].xtype]) {
44613                 ar[i].form = this;
44614                 var fe = Roo.factory(ar[i], Roo.form);
44615                 if (!ret) {
44616                     ret = fe;
44617                 }
44618                 fe.form = this;
44619                 if (fe.store) {
44620                     fe.store.form = this;
44621                 }
44622                 if (fe.isLayout) {  
44623                          
44624                     this.start(fe);
44625                     this.allItems.push(fe);
44626                     if (fe.items && fe.addxtype) {
44627                         fe.addxtype.apply(fe, fe.items);
44628                         delete fe.items;
44629                     }
44630                      this.end();
44631                     continue;
44632                 }
44633                 
44634                 
44635                  
44636                 this.add(fe);
44637               //  console.log('adding ' + ar[i].xtype);
44638             }
44639             if (ar[i].xtype == 'Button') {  
44640                 //console.log('adding button');
44641                 //console.log(ar[i]);
44642                 this.addButton(ar[i]);
44643                 this.allItems.push(fe);
44644                 continue;
44645             }
44646             
44647             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44648                 alert('end is not supported on xtype any more, use items');
44649             //    this.end();
44650             //    //console.log('adding end');
44651             }
44652             
44653         }
44654         return ret;
44655     },
44656     
44657     /**
44658      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44659      * option "monitorValid"
44660      */
44661     startMonitoring : function(){
44662         if(!this.bound){
44663             this.bound = true;
44664             Roo.TaskMgr.start({
44665                 run : this.bindHandler,
44666                 interval : this.monitorPoll || 200,
44667                 scope: this
44668             });
44669         }
44670     },
44671
44672     /**
44673      * Stops monitoring of the valid state of this form
44674      */
44675     stopMonitoring : function(){
44676         this.bound = false;
44677     },
44678
44679     // private
44680     bindHandler : function(){
44681         if(!this.bound){
44682             return false; // stops binding
44683         }
44684         var valid = true;
44685         this.items.each(function(f){
44686             if(!f.isValid(true)){
44687                 valid = false;
44688                 return false;
44689             }
44690         });
44691         for(var i = 0, len = this.buttons.length; i < len; i++){
44692             var btn = this.buttons[i];
44693             if(btn.formBind === true && btn.disabled === valid){
44694                 btn.setDisabled(!valid);
44695             }
44696         }
44697         this.fireEvent('clientvalidation', this, valid);
44698     }
44699     
44700     
44701     
44702     
44703     
44704     
44705     
44706     
44707 });
44708
44709
44710 // back compat
44711 Roo.Form = Roo.form.Form;
44712 /*
44713  * Based on:
44714  * Ext JS Library 1.1.1
44715  * Copyright(c) 2006-2007, Ext JS, LLC.
44716  *
44717  * Originally Released Under LGPL - original licence link has changed is not relivant.
44718  *
44719  * Fork - LGPL
44720  * <script type="text/javascript">
44721  */
44722
44723 // as we use this in bootstrap.
44724 Roo.namespace('Roo.form');
44725  /**
44726  * @class Roo.form.Action
44727  * Internal Class used to handle form actions
44728  * @constructor
44729  * @param {Roo.form.BasicForm} el The form element or its id
44730  * @param {Object} config Configuration options
44731  */
44732
44733  
44734  
44735 // define the action interface
44736 Roo.form.Action = function(form, options){
44737     this.form = form;
44738     this.options = options || {};
44739 };
44740 /**
44741  * Client Validation Failed
44742  * @const 
44743  */
44744 Roo.form.Action.CLIENT_INVALID = 'client';
44745 /**
44746  * Server Validation Failed
44747  * @const 
44748  */
44749 Roo.form.Action.SERVER_INVALID = 'server';
44750  /**
44751  * Connect to Server Failed
44752  * @const 
44753  */
44754 Roo.form.Action.CONNECT_FAILURE = 'connect';
44755 /**
44756  * Reading Data from Server Failed
44757  * @const 
44758  */
44759 Roo.form.Action.LOAD_FAILURE = 'load';
44760
44761 Roo.form.Action.prototype = {
44762     type : 'default',
44763     failureType : undefined,
44764     response : undefined,
44765     result : undefined,
44766
44767     // interface method
44768     run : function(options){
44769
44770     },
44771
44772     // interface method
44773     success : function(response){
44774
44775     },
44776
44777     // interface method
44778     handleResponse : function(response){
44779
44780     },
44781
44782     // default connection failure
44783     failure : function(response){
44784         
44785         this.response = response;
44786         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44787         this.form.afterAction(this, false);
44788     },
44789
44790     processResponse : function(response){
44791         this.response = response;
44792         if(!response.responseText){
44793             return true;
44794         }
44795         this.result = this.handleResponse(response);
44796         return this.result;
44797     },
44798
44799     // utility functions used internally
44800     getUrl : function(appendParams){
44801         var url = this.options.url || this.form.url || this.form.el.dom.action;
44802         if(appendParams){
44803             var p = this.getParams();
44804             if(p){
44805                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44806             }
44807         }
44808         return url;
44809     },
44810
44811     getMethod : function(){
44812         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44813     },
44814
44815     getParams : function(){
44816         var bp = this.form.baseParams;
44817         var p = this.options.params;
44818         if(p){
44819             if(typeof p == "object"){
44820                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44821             }else if(typeof p == 'string' && bp){
44822                 p += '&' + Roo.urlEncode(bp);
44823             }
44824         }else if(bp){
44825             p = Roo.urlEncode(bp);
44826         }
44827         return p;
44828     },
44829
44830     createCallback : function(){
44831         return {
44832             success: this.success,
44833             failure: this.failure,
44834             scope: this,
44835             timeout: (this.form.timeout*1000),
44836             upload: this.form.fileUpload ? this.success : undefined
44837         };
44838     }
44839 };
44840
44841 Roo.form.Action.Submit = function(form, options){
44842     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44843 };
44844
44845 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44846     type : 'submit',
44847
44848     haveProgress : false,
44849     uploadComplete : false,
44850     
44851     // uploadProgress indicator.
44852     uploadProgress : function()
44853     {
44854         if (!this.form.progressUrl) {
44855             return;
44856         }
44857         
44858         if (!this.haveProgress) {
44859             Roo.MessageBox.progress("Uploading", "Uploading");
44860         }
44861         if (this.uploadComplete) {
44862            Roo.MessageBox.hide();
44863            return;
44864         }
44865         
44866         this.haveProgress = true;
44867    
44868         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44869         
44870         var c = new Roo.data.Connection();
44871         c.request({
44872             url : this.form.progressUrl,
44873             params: {
44874                 id : uid
44875             },
44876             method: 'GET',
44877             success : function(req){
44878                //console.log(data);
44879                 var rdata = false;
44880                 var edata;
44881                 try  {
44882                    rdata = Roo.decode(req.responseText)
44883                 } catch (e) {
44884                     Roo.log("Invalid data from server..");
44885                     Roo.log(edata);
44886                     return;
44887                 }
44888                 if (!rdata || !rdata.success) {
44889                     Roo.log(rdata);
44890                     Roo.MessageBox.alert(Roo.encode(rdata));
44891                     return;
44892                 }
44893                 var data = rdata.data;
44894                 
44895                 if (this.uploadComplete) {
44896                    Roo.MessageBox.hide();
44897                    return;
44898                 }
44899                    
44900                 if (data){
44901                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44902                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44903                     );
44904                 }
44905                 this.uploadProgress.defer(2000,this);
44906             },
44907        
44908             failure: function(data) {
44909                 Roo.log('progress url failed ');
44910                 Roo.log(data);
44911             },
44912             scope : this
44913         });
44914            
44915     },
44916     
44917     
44918     run : function()
44919     {
44920         // run get Values on the form, so it syncs any secondary forms.
44921         this.form.getValues();
44922         
44923         var o = this.options;
44924         var method = this.getMethod();
44925         var isPost = method == 'POST';
44926         if(o.clientValidation === false || this.form.isValid()){
44927             
44928             if (this.form.progressUrl) {
44929                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44930                     (new Date() * 1) + '' + Math.random());
44931                     
44932             } 
44933             
44934             
44935             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44936                 form:this.form.el.dom,
44937                 url:this.getUrl(!isPost),
44938                 method: method,
44939                 params:isPost ? this.getParams() : null,
44940                 isUpload: this.form.fileUpload
44941             }));
44942             
44943             this.uploadProgress();
44944
44945         }else if (o.clientValidation !== false){ // client validation failed
44946             this.failureType = Roo.form.Action.CLIENT_INVALID;
44947             this.form.afterAction(this, false);
44948         }
44949     },
44950
44951     success : function(response)
44952     {
44953         this.uploadComplete= true;
44954         if (this.haveProgress) {
44955             Roo.MessageBox.hide();
44956         }
44957         
44958         
44959         var result = this.processResponse(response);
44960         if(result === true || result.success){
44961             this.form.afterAction(this, true);
44962             return;
44963         }
44964         if(result.errors){
44965             this.form.markInvalid(result.errors);
44966             this.failureType = Roo.form.Action.SERVER_INVALID;
44967         }
44968         this.form.afterAction(this, false);
44969     },
44970     failure : function(response)
44971     {
44972         this.uploadComplete= true;
44973         if (this.haveProgress) {
44974             Roo.MessageBox.hide();
44975         }
44976         
44977         this.response = response;
44978         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44979         this.form.afterAction(this, false);
44980     },
44981     
44982     handleResponse : function(response){
44983         if(this.form.errorReader){
44984             var rs = this.form.errorReader.read(response);
44985             var errors = [];
44986             if(rs.records){
44987                 for(var i = 0, len = rs.records.length; i < len; i++) {
44988                     var r = rs.records[i];
44989                     errors[i] = r.data;
44990                 }
44991             }
44992             if(errors.length < 1){
44993                 errors = null;
44994             }
44995             return {
44996                 success : rs.success,
44997                 errors : errors
44998             };
44999         }
45000         var ret = false;
45001         try {
45002             ret = Roo.decode(response.responseText);
45003         } catch (e) {
45004             ret = {
45005                 success: false,
45006                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45007                 errors : []
45008             };
45009         }
45010         return ret;
45011         
45012     }
45013 });
45014
45015
45016 Roo.form.Action.Load = function(form, options){
45017     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45018     this.reader = this.form.reader;
45019 };
45020
45021 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45022     type : 'load',
45023
45024     run : function(){
45025         
45026         Roo.Ajax.request(Roo.apply(
45027                 this.createCallback(), {
45028                     method:this.getMethod(),
45029                     url:this.getUrl(false),
45030                     params:this.getParams()
45031         }));
45032     },
45033
45034     success : function(response){
45035         
45036         var result = this.processResponse(response);
45037         if(result === true || !result.success || !result.data){
45038             this.failureType = Roo.form.Action.LOAD_FAILURE;
45039             this.form.afterAction(this, false);
45040             return;
45041         }
45042         this.form.clearInvalid();
45043         this.form.setValues(result.data);
45044         this.form.afterAction(this, true);
45045     },
45046
45047     handleResponse : function(response){
45048         if(this.form.reader){
45049             var rs = this.form.reader.read(response);
45050             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45051             return {
45052                 success : rs.success,
45053                 data : data
45054             };
45055         }
45056         return Roo.decode(response.responseText);
45057     }
45058 });
45059
45060 Roo.form.Action.ACTION_TYPES = {
45061     'load' : Roo.form.Action.Load,
45062     'submit' : Roo.form.Action.Submit
45063 };/*
45064  * Based on:
45065  * Ext JS Library 1.1.1
45066  * Copyright(c) 2006-2007, Ext JS, LLC.
45067  *
45068  * Originally Released Under LGPL - original licence link has changed is not relivant.
45069  *
45070  * Fork - LGPL
45071  * <script type="text/javascript">
45072  */
45073  
45074 /**
45075  * @class Roo.form.Layout
45076  * @extends Roo.Component
45077  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45078  * @constructor
45079  * @param {Object} config Configuration options
45080  */
45081 Roo.form.Layout = function(config){
45082     var xitems = [];
45083     if (config.items) {
45084         xitems = config.items;
45085         delete config.items;
45086     }
45087     Roo.form.Layout.superclass.constructor.call(this, config);
45088     this.stack = [];
45089     Roo.each(xitems, this.addxtype, this);
45090      
45091 };
45092
45093 Roo.extend(Roo.form.Layout, Roo.Component, {
45094     /**
45095      * @cfg {String/Object} autoCreate
45096      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45097      */
45098     /**
45099      * @cfg {String/Object/Function} style
45100      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45101      * a function which returns such a specification.
45102      */
45103     /**
45104      * @cfg {String} labelAlign
45105      * Valid values are "left," "top" and "right" (defaults to "left")
45106      */
45107     /**
45108      * @cfg {Number} labelWidth
45109      * Fixed width in pixels of all field labels (defaults to undefined)
45110      */
45111     /**
45112      * @cfg {Boolean} clear
45113      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45114      */
45115     clear : true,
45116     /**
45117      * @cfg {String} labelSeparator
45118      * The separator to use after field labels (defaults to ':')
45119      */
45120     labelSeparator : ':',
45121     /**
45122      * @cfg {Boolean} hideLabels
45123      * True to suppress the display of field labels in this layout (defaults to false)
45124      */
45125     hideLabels : false,
45126
45127     // private
45128     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45129     
45130     isLayout : true,
45131     
45132     // private
45133     onRender : function(ct, position){
45134         if(this.el){ // from markup
45135             this.el = Roo.get(this.el);
45136         }else {  // generate
45137             var cfg = this.getAutoCreate();
45138             this.el = ct.createChild(cfg, position);
45139         }
45140         if(this.style){
45141             this.el.applyStyles(this.style);
45142         }
45143         if(this.labelAlign){
45144             this.el.addClass('x-form-label-'+this.labelAlign);
45145         }
45146         if(this.hideLabels){
45147             this.labelStyle = "display:none";
45148             this.elementStyle = "padding-left:0;";
45149         }else{
45150             if(typeof this.labelWidth == 'number'){
45151                 this.labelStyle = "width:"+this.labelWidth+"px;";
45152                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45153             }
45154             if(this.labelAlign == 'top'){
45155                 this.labelStyle = "width:auto;";
45156                 this.elementStyle = "padding-left:0;";
45157             }
45158         }
45159         var stack = this.stack;
45160         var slen = stack.length;
45161         if(slen > 0){
45162             if(!this.fieldTpl){
45163                 var t = new Roo.Template(
45164                     '<div class="x-form-item {5}">',
45165                         '<label for="{0}" style="{2}">{1}{4}</label>',
45166                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45167                         '</div>',
45168                     '</div><div class="x-form-clear-left"></div>'
45169                 );
45170                 t.disableFormats = true;
45171                 t.compile();
45172                 Roo.form.Layout.prototype.fieldTpl = t;
45173             }
45174             for(var i = 0; i < slen; i++) {
45175                 if(stack[i].isFormField){
45176                     this.renderField(stack[i]);
45177                 }else{
45178                     this.renderComponent(stack[i]);
45179                 }
45180             }
45181         }
45182         if(this.clear){
45183             this.el.createChild({cls:'x-form-clear'});
45184         }
45185     },
45186
45187     // private
45188     renderField : function(f){
45189         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45190                f.id, //0
45191                f.fieldLabel, //1
45192                f.labelStyle||this.labelStyle||'', //2
45193                this.elementStyle||'', //3
45194                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45195                f.itemCls||this.itemCls||''  //5
45196        ], true).getPrevSibling());
45197     },
45198
45199     // private
45200     renderComponent : function(c){
45201         c.render(c.isLayout ? this.el : this.el.createChild());    
45202     },
45203     /**
45204      * Adds a object form elements (using the xtype property as the factory method.)
45205      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45206      * @param {Object} config 
45207      */
45208     addxtype : function(o)
45209     {
45210         // create the lement.
45211         o.form = this.form;
45212         var fe = Roo.factory(o, Roo.form);
45213         this.form.allItems.push(fe);
45214         this.stack.push(fe);
45215         
45216         if (fe.isFormField) {
45217             this.form.items.add(fe);
45218         }
45219          
45220         return fe;
45221     }
45222 });
45223
45224 /**
45225  * @class Roo.form.Column
45226  * @extends Roo.form.Layout
45227  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45228  * @constructor
45229  * @param {Object} config Configuration options
45230  */
45231 Roo.form.Column = function(config){
45232     Roo.form.Column.superclass.constructor.call(this, config);
45233 };
45234
45235 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45236     /**
45237      * @cfg {Number/String} width
45238      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45239      */
45240     /**
45241      * @cfg {String/Object} autoCreate
45242      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45243      */
45244
45245     // private
45246     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45247
45248     // private
45249     onRender : function(ct, position){
45250         Roo.form.Column.superclass.onRender.call(this, ct, position);
45251         if(this.width){
45252             this.el.setWidth(this.width);
45253         }
45254     }
45255 });
45256
45257
45258 /**
45259  * @class Roo.form.Row
45260  * @extends Roo.form.Layout
45261  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45262  * @constructor
45263  * @param {Object} config Configuration options
45264  */
45265
45266  
45267 Roo.form.Row = function(config){
45268     Roo.form.Row.superclass.constructor.call(this, config);
45269 };
45270  
45271 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45272       /**
45273      * @cfg {Number/String} width
45274      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45275      */
45276     /**
45277      * @cfg {Number/String} height
45278      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45279      */
45280     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45281     
45282     padWidth : 20,
45283     // private
45284     onRender : function(ct, position){
45285         //console.log('row render');
45286         if(!this.rowTpl){
45287             var t = new Roo.Template(
45288                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45289                     '<label for="{0}" style="{2}">{1}{4}</label>',
45290                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45291                     '</div>',
45292                 '</div>'
45293             );
45294             t.disableFormats = true;
45295             t.compile();
45296             Roo.form.Layout.prototype.rowTpl = t;
45297         }
45298         this.fieldTpl = this.rowTpl;
45299         
45300         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45301         var labelWidth = 100;
45302         
45303         if ((this.labelAlign != 'top')) {
45304             if (typeof this.labelWidth == 'number') {
45305                 labelWidth = this.labelWidth
45306             }
45307             this.padWidth =  20 + labelWidth;
45308             
45309         }
45310         
45311         Roo.form.Column.superclass.onRender.call(this, ct, position);
45312         if(this.width){
45313             this.el.setWidth(this.width);
45314         }
45315         if(this.height){
45316             this.el.setHeight(this.height);
45317         }
45318     },
45319     
45320     // private
45321     renderField : function(f){
45322         f.fieldEl = this.fieldTpl.append(this.el, [
45323                f.id, f.fieldLabel,
45324                f.labelStyle||this.labelStyle||'',
45325                this.elementStyle||'',
45326                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45327                f.itemCls||this.itemCls||'',
45328                f.width ? f.width + this.padWidth : 160 + this.padWidth
45329        ],true);
45330     }
45331 });
45332  
45333
45334 /**
45335  * @class Roo.form.FieldSet
45336  * @extends Roo.form.Layout
45337  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45338  * @constructor
45339  * @param {Object} config Configuration options
45340  */
45341 Roo.form.FieldSet = function(config){
45342     Roo.form.FieldSet.superclass.constructor.call(this, config);
45343 };
45344
45345 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45346     /**
45347      * @cfg {String} legend
45348      * The text to display as the legend for the FieldSet (defaults to '')
45349      */
45350     /**
45351      * @cfg {String/Object} autoCreate
45352      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45353      */
45354
45355     // private
45356     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45357
45358     // private
45359     onRender : function(ct, position){
45360         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45361         if(this.legend){
45362             this.setLegend(this.legend);
45363         }
45364     },
45365
45366     // private
45367     setLegend : function(text){
45368         if(this.rendered){
45369             this.el.child('legend').update(text);
45370         }
45371     }
45372 });/*
45373  * Based on:
45374  * Ext JS Library 1.1.1
45375  * Copyright(c) 2006-2007, Ext JS, LLC.
45376  *
45377  * Originally Released Under LGPL - original licence link has changed is not relivant.
45378  *
45379  * Fork - LGPL
45380  * <script type="text/javascript">
45381  */
45382 /**
45383  * @class Roo.form.VTypes
45384  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45385  * @singleton
45386  */
45387 Roo.form.VTypes = function(){
45388     // closure these in so they are only created once.
45389     var alpha = /^[a-zA-Z_]+$/;
45390     var alphanum = /^[a-zA-Z0-9_]+$/;
45391     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45392     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45393
45394     // All these messages and functions are configurable
45395     return {
45396         /**
45397          * The function used to validate email addresses
45398          * @param {String} value The email address
45399          */
45400         'email' : function(v){
45401             return email.test(v);
45402         },
45403         /**
45404          * The error text to display when the email validation function returns false
45405          * @type String
45406          */
45407         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45408         /**
45409          * The keystroke filter mask to be applied on email input
45410          * @type RegExp
45411          */
45412         'emailMask' : /[a-z0-9_\.\-@]/i,
45413
45414         /**
45415          * The function used to validate URLs
45416          * @param {String} value The URL
45417          */
45418         'url' : function(v){
45419             return url.test(v);
45420         },
45421         /**
45422          * The error text to display when the url validation function returns false
45423          * @type String
45424          */
45425         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45426         
45427         /**
45428          * The function used to validate alpha values
45429          * @param {String} value The value
45430          */
45431         'alpha' : function(v){
45432             return alpha.test(v);
45433         },
45434         /**
45435          * The error text to display when the alpha validation function returns false
45436          * @type String
45437          */
45438         'alphaText' : 'This field should only contain letters and _',
45439         /**
45440          * The keystroke filter mask to be applied on alpha input
45441          * @type RegExp
45442          */
45443         'alphaMask' : /[a-z_]/i,
45444
45445         /**
45446          * The function used to validate alphanumeric values
45447          * @param {String} value The value
45448          */
45449         'alphanum' : function(v){
45450             return alphanum.test(v);
45451         },
45452         /**
45453          * The error text to display when the alphanumeric validation function returns false
45454          * @type String
45455          */
45456         'alphanumText' : 'This field should only contain letters, numbers and _',
45457         /**
45458          * The keystroke filter mask to be applied on alphanumeric input
45459          * @type RegExp
45460          */
45461         'alphanumMask' : /[a-z0-9_]/i
45462     };
45463 }();//<script type="text/javascript">
45464
45465 /**
45466  * @class Roo.form.FCKeditor
45467  * @extends Roo.form.TextArea
45468  * Wrapper around the FCKEditor http://www.fckeditor.net
45469  * @constructor
45470  * Creates a new FCKeditor
45471  * @param {Object} config Configuration options
45472  */
45473 Roo.form.FCKeditor = function(config){
45474     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45475     this.addEvents({
45476          /**
45477          * @event editorinit
45478          * Fired when the editor is initialized - you can add extra handlers here..
45479          * @param {FCKeditor} this
45480          * @param {Object} the FCK object.
45481          */
45482         editorinit : true
45483     });
45484     
45485     
45486 };
45487 Roo.form.FCKeditor.editors = { };
45488 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45489 {
45490     //defaultAutoCreate : {
45491     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45492     //},
45493     // private
45494     /**
45495      * @cfg {Object} fck options - see fck manual for details.
45496      */
45497     fckconfig : false,
45498     
45499     /**
45500      * @cfg {Object} fck toolbar set (Basic or Default)
45501      */
45502     toolbarSet : 'Basic',
45503     /**
45504      * @cfg {Object} fck BasePath
45505      */ 
45506     basePath : '/fckeditor/',
45507     
45508     
45509     frame : false,
45510     
45511     value : '',
45512     
45513    
45514     onRender : function(ct, position)
45515     {
45516         if(!this.el){
45517             this.defaultAutoCreate = {
45518                 tag: "textarea",
45519                 style:"width:300px;height:60px;",
45520                 autocomplete: "off"
45521             };
45522         }
45523         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45524         /*
45525         if(this.grow){
45526             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45527             if(this.preventScrollbars){
45528                 this.el.setStyle("overflow", "hidden");
45529             }
45530             this.el.setHeight(this.growMin);
45531         }
45532         */
45533         //console.log('onrender' + this.getId() );
45534         Roo.form.FCKeditor.editors[this.getId()] = this;
45535          
45536
45537         this.replaceTextarea() ;
45538         
45539     },
45540     
45541     getEditor : function() {
45542         return this.fckEditor;
45543     },
45544     /**
45545      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45546      * @param {Mixed} value The value to set
45547      */
45548     
45549     
45550     setValue : function(value)
45551     {
45552         //console.log('setValue: ' + value);
45553         
45554         if(typeof(value) == 'undefined') { // not sure why this is happending...
45555             return;
45556         }
45557         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45558         
45559         //if(!this.el || !this.getEditor()) {
45560         //    this.value = value;
45561             //this.setValue.defer(100,this,[value]);    
45562         //    return;
45563         //} 
45564         
45565         if(!this.getEditor()) {
45566             return;
45567         }
45568         
45569         this.getEditor().SetData(value);
45570         
45571         //
45572
45573     },
45574
45575     /**
45576      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45577      * @return {Mixed} value The field value
45578      */
45579     getValue : function()
45580     {
45581         
45582         if (this.frame && this.frame.dom.style.display == 'none') {
45583             return Roo.form.FCKeditor.superclass.getValue.call(this);
45584         }
45585         
45586         if(!this.el || !this.getEditor()) {
45587            
45588            // this.getValue.defer(100,this); 
45589             return this.value;
45590         }
45591        
45592         
45593         var value=this.getEditor().GetData();
45594         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45595         return Roo.form.FCKeditor.superclass.getValue.call(this);
45596         
45597
45598     },
45599
45600     /**
45601      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45602      * @return {Mixed} value The field value
45603      */
45604     getRawValue : function()
45605     {
45606         if (this.frame && this.frame.dom.style.display == 'none') {
45607             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45608         }
45609         
45610         if(!this.el || !this.getEditor()) {
45611             //this.getRawValue.defer(100,this); 
45612             return this.value;
45613             return;
45614         }
45615         
45616         
45617         
45618         var value=this.getEditor().GetData();
45619         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45620         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45621          
45622     },
45623     
45624     setSize : function(w,h) {
45625         
45626         
45627         
45628         //if (this.frame && this.frame.dom.style.display == 'none') {
45629         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45630         //    return;
45631         //}
45632         //if(!this.el || !this.getEditor()) {
45633         //    this.setSize.defer(100,this, [w,h]); 
45634         //    return;
45635         //}
45636         
45637         
45638         
45639         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45640         
45641         this.frame.dom.setAttribute('width', w);
45642         this.frame.dom.setAttribute('height', h);
45643         this.frame.setSize(w,h);
45644         
45645     },
45646     
45647     toggleSourceEdit : function(value) {
45648         
45649       
45650          
45651         this.el.dom.style.display = value ? '' : 'none';
45652         this.frame.dom.style.display = value ?  'none' : '';
45653         
45654     },
45655     
45656     
45657     focus: function(tag)
45658     {
45659         if (this.frame.dom.style.display == 'none') {
45660             return Roo.form.FCKeditor.superclass.focus.call(this);
45661         }
45662         if(!this.el || !this.getEditor()) {
45663             this.focus.defer(100,this, [tag]); 
45664             return;
45665         }
45666         
45667         
45668         
45669         
45670         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45671         this.getEditor().Focus();
45672         if (tgs.length) {
45673             if (!this.getEditor().Selection.GetSelection()) {
45674                 this.focus.defer(100,this, [tag]); 
45675                 return;
45676             }
45677             
45678             
45679             var r = this.getEditor().EditorDocument.createRange();
45680             r.setStart(tgs[0],0);
45681             r.setEnd(tgs[0],0);
45682             this.getEditor().Selection.GetSelection().removeAllRanges();
45683             this.getEditor().Selection.GetSelection().addRange(r);
45684             this.getEditor().Focus();
45685         }
45686         
45687     },
45688     
45689     
45690     
45691     replaceTextarea : function()
45692     {
45693         if ( document.getElementById( this.getId() + '___Frame' ) )
45694             return ;
45695         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45696         //{
45697             // We must check the elements firstly using the Id and then the name.
45698         var oTextarea = document.getElementById( this.getId() );
45699         
45700         var colElementsByName = document.getElementsByName( this.getId() ) ;
45701          
45702         oTextarea.style.display = 'none' ;
45703
45704         if ( oTextarea.tabIndex ) {            
45705             this.TabIndex = oTextarea.tabIndex ;
45706         }
45707         
45708         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45709         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45710         this.frame = Roo.get(this.getId() + '___Frame')
45711     },
45712     
45713     _getConfigHtml : function()
45714     {
45715         var sConfig = '' ;
45716
45717         for ( var o in this.fckconfig ) {
45718             sConfig += sConfig.length > 0  ? '&amp;' : '';
45719             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45720         }
45721
45722         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45723     },
45724     
45725     
45726     _getIFrameHtml : function()
45727     {
45728         var sFile = 'fckeditor.html' ;
45729         /* no idea what this is about..
45730         try
45731         {
45732             if ( (/fcksource=true/i).test( window.top.location.search ) )
45733                 sFile = 'fckeditor.original.html' ;
45734         }
45735         catch (e) { 
45736         */
45737
45738         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45739         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45740         
45741         
45742         var html = '<iframe id="' + this.getId() +
45743             '___Frame" src="' + sLink +
45744             '" width="' + this.width +
45745             '" height="' + this.height + '"' +
45746             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45747             ' frameborder="0" scrolling="no"></iframe>' ;
45748
45749         return html ;
45750     },
45751     
45752     _insertHtmlBefore : function( html, element )
45753     {
45754         if ( element.insertAdjacentHTML )       {
45755             // IE
45756             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45757         } else { // Gecko
45758             var oRange = document.createRange() ;
45759             oRange.setStartBefore( element ) ;
45760             var oFragment = oRange.createContextualFragment( html );
45761             element.parentNode.insertBefore( oFragment, element ) ;
45762         }
45763     }
45764     
45765     
45766   
45767     
45768     
45769     
45770     
45771
45772 });
45773
45774 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45775
45776 function FCKeditor_OnComplete(editorInstance){
45777     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45778     f.fckEditor = editorInstance;
45779     //console.log("loaded");
45780     f.fireEvent('editorinit', f, editorInstance);
45781
45782   
45783
45784  
45785
45786
45787
45788
45789
45790
45791
45792
45793
45794
45795
45796
45797
45798
45799
45800 //<script type="text/javascript">
45801 /**
45802  * @class Roo.form.GridField
45803  * @extends Roo.form.Field
45804  * Embed a grid (or editable grid into a form)
45805  * STATUS ALPHA
45806  * 
45807  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45808  * it needs 
45809  * xgrid.store = Roo.data.Store
45810  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45811  * xgrid.store.reader = Roo.data.JsonReader 
45812  * 
45813  * 
45814  * @constructor
45815  * Creates a new GridField
45816  * @param {Object} config Configuration options
45817  */
45818 Roo.form.GridField = function(config){
45819     Roo.form.GridField.superclass.constructor.call(this, config);
45820      
45821 };
45822
45823 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45824     /**
45825      * @cfg {Number} width  - used to restrict width of grid..
45826      */
45827     width : 100,
45828     /**
45829      * @cfg {Number} height - used to restrict height of grid..
45830      */
45831     height : 50,
45832      /**
45833      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45834          * 
45835          *}
45836      */
45837     xgrid : false, 
45838     /**
45839      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45840      * {tag: "input", type: "checkbox", autocomplete: "off"})
45841      */
45842    // defaultAutoCreate : { tag: 'div' },
45843     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45844     /**
45845      * @cfg {String} addTitle Text to include for adding a title.
45846      */
45847     addTitle : false,
45848     //
45849     onResize : function(){
45850         Roo.form.Field.superclass.onResize.apply(this, arguments);
45851     },
45852
45853     initEvents : function(){
45854         // Roo.form.Checkbox.superclass.initEvents.call(this);
45855         // has no events...
45856        
45857     },
45858
45859
45860     getResizeEl : function(){
45861         return this.wrap;
45862     },
45863
45864     getPositionEl : function(){
45865         return this.wrap;
45866     },
45867
45868     // private
45869     onRender : function(ct, position){
45870         
45871         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45872         var style = this.style;
45873         delete this.style;
45874         
45875         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45876         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45877         this.viewEl = this.wrap.createChild({ tag: 'div' });
45878         if (style) {
45879             this.viewEl.applyStyles(style);
45880         }
45881         if (this.width) {
45882             this.viewEl.setWidth(this.width);
45883         }
45884         if (this.height) {
45885             this.viewEl.setHeight(this.height);
45886         }
45887         //if(this.inputValue !== undefined){
45888         //this.setValue(this.value);
45889         
45890         
45891         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45892         
45893         
45894         this.grid.render();
45895         this.grid.getDataSource().on('remove', this.refreshValue, this);
45896         this.grid.getDataSource().on('update', this.refreshValue, this);
45897         this.grid.on('afteredit', this.refreshValue, this);
45898  
45899     },
45900      
45901     
45902     /**
45903      * Sets the value of the item. 
45904      * @param {String} either an object  or a string..
45905      */
45906     setValue : function(v){
45907         //this.value = v;
45908         v = v || []; // empty set..
45909         // this does not seem smart - it really only affects memoryproxy grids..
45910         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45911             var ds = this.grid.getDataSource();
45912             // assumes a json reader..
45913             var data = {}
45914             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45915             ds.loadData( data);
45916         }
45917         // clear selection so it does not get stale.
45918         if (this.grid.sm) { 
45919             this.grid.sm.clearSelections();
45920         }
45921         
45922         Roo.form.GridField.superclass.setValue.call(this, v);
45923         this.refreshValue();
45924         // should load data in the grid really....
45925     },
45926     
45927     // private
45928     refreshValue: function() {
45929          var val = [];
45930         this.grid.getDataSource().each(function(r) {
45931             val.push(r.data);
45932         });
45933         this.el.dom.value = Roo.encode(val);
45934     }
45935     
45936      
45937     
45938     
45939 });/*
45940  * Based on:
45941  * Ext JS Library 1.1.1
45942  * Copyright(c) 2006-2007, Ext JS, LLC.
45943  *
45944  * Originally Released Under LGPL - original licence link has changed is not relivant.
45945  *
45946  * Fork - LGPL
45947  * <script type="text/javascript">
45948  */
45949 /**
45950  * @class Roo.form.DisplayField
45951  * @extends Roo.form.Field
45952  * A generic Field to display non-editable data.
45953  * @constructor
45954  * Creates a new Display Field item.
45955  * @param {Object} config Configuration options
45956  */
45957 Roo.form.DisplayField = function(config){
45958     Roo.form.DisplayField.superclass.constructor.call(this, config);
45959     
45960 };
45961
45962 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45963     inputType:      'hidden',
45964     allowBlank:     true,
45965     readOnly:         true,
45966     
45967  
45968     /**
45969      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45970      */
45971     focusClass : undefined,
45972     /**
45973      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45974      */
45975     fieldClass: 'x-form-field',
45976     
45977      /**
45978      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45979      */
45980     valueRenderer: undefined,
45981     
45982     width: 100,
45983     /**
45984      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45985      * {tag: "input", type: "checkbox", autocomplete: "off"})
45986      */
45987      
45988  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45989
45990     onResize : function(){
45991         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45992         
45993     },
45994
45995     initEvents : function(){
45996         // Roo.form.Checkbox.superclass.initEvents.call(this);
45997         // has no events...
45998        
45999     },
46000
46001
46002     getResizeEl : function(){
46003         return this.wrap;
46004     },
46005
46006     getPositionEl : function(){
46007         return this.wrap;
46008     },
46009
46010     // private
46011     onRender : function(ct, position){
46012         
46013         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46014         //if(this.inputValue !== undefined){
46015         this.wrap = this.el.wrap();
46016         
46017         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46018         
46019         if (this.bodyStyle) {
46020             this.viewEl.applyStyles(this.bodyStyle);
46021         }
46022         //this.viewEl.setStyle('padding', '2px');
46023         
46024         this.setValue(this.value);
46025         
46026     },
46027 /*
46028     // private
46029     initValue : Roo.emptyFn,
46030
46031   */
46032
46033         // private
46034     onClick : function(){
46035         
46036     },
46037
46038     /**
46039      * Sets the checked state of the checkbox.
46040      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46041      */
46042     setValue : function(v){
46043         this.value = v;
46044         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46045         // this might be called before we have a dom element..
46046         if (!this.viewEl) {
46047             return;
46048         }
46049         this.viewEl.dom.innerHTML = html;
46050         Roo.form.DisplayField.superclass.setValue.call(this, v);
46051
46052     }
46053 });/*
46054  * 
46055  * Licence- LGPL
46056  * 
46057  */
46058
46059 /**
46060  * @class Roo.form.DayPicker
46061  * @extends Roo.form.Field
46062  * A Day picker show [M] [T] [W] ....
46063  * @constructor
46064  * Creates a new Day Picker
46065  * @param {Object} config Configuration options
46066  */
46067 Roo.form.DayPicker= function(config){
46068     Roo.form.DayPicker.superclass.constructor.call(this, config);
46069      
46070 };
46071
46072 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46073     /**
46074      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46075      */
46076     focusClass : undefined,
46077     /**
46078      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46079      */
46080     fieldClass: "x-form-field",
46081    
46082     /**
46083      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46084      * {tag: "input", type: "checkbox", autocomplete: "off"})
46085      */
46086     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46087     
46088    
46089     actionMode : 'viewEl', 
46090     //
46091     // private
46092  
46093     inputType : 'hidden',
46094     
46095      
46096     inputElement: false, // real input element?
46097     basedOn: false, // ????
46098     
46099     isFormField: true, // not sure where this is needed!!!!
46100
46101     onResize : function(){
46102         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46103         if(!this.boxLabel){
46104             this.el.alignTo(this.wrap, 'c-c');
46105         }
46106     },
46107
46108     initEvents : function(){
46109         Roo.form.Checkbox.superclass.initEvents.call(this);
46110         this.el.on("click", this.onClick,  this);
46111         this.el.on("change", this.onClick,  this);
46112     },
46113
46114
46115     getResizeEl : function(){
46116         return this.wrap;
46117     },
46118
46119     getPositionEl : function(){
46120         return this.wrap;
46121     },
46122
46123     
46124     // private
46125     onRender : function(ct, position){
46126         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46127        
46128         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46129         
46130         var r1 = '<table><tr>';
46131         var r2 = '<tr class="x-form-daypick-icons">';
46132         for (var i=0; i < 7; i++) {
46133             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46134             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46135         }
46136         
46137         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46138         viewEl.select('img').on('click', this.onClick, this);
46139         this.viewEl = viewEl;   
46140         
46141         
46142         // this will not work on Chrome!!!
46143         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46144         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46145         
46146         
46147           
46148
46149     },
46150
46151     // private
46152     initValue : Roo.emptyFn,
46153
46154     /**
46155      * Returns the checked state of the checkbox.
46156      * @return {Boolean} True if checked, else false
46157      */
46158     getValue : function(){
46159         return this.el.dom.value;
46160         
46161     },
46162
46163         // private
46164     onClick : function(e){ 
46165         //this.setChecked(!this.checked);
46166         Roo.get(e.target).toggleClass('x-menu-item-checked');
46167         this.refreshValue();
46168         //if(this.el.dom.checked != this.checked){
46169         //    this.setValue(this.el.dom.checked);
46170        // }
46171     },
46172     
46173     // private
46174     refreshValue : function()
46175     {
46176         var val = '';
46177         this.viewEl.select('img',true).each(function(e,i,n)  {
46178             val += e.is(".x-menu-item-checked") ? String(n) : '';
46179         });
46180         this.setValue(val, true);
46181     },
46182
46183     /**
46184      * Sets the checked state of the checkbox.
46185      * On is always based on a string comparison between inputValue and the param.
46186      * @param {Boolean/String} value - the value to set 
46187      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46188      */
46189     setValue : function(v,suppressEvent){
46190         if (!this.el.dom) {
46191             return;
46192         }
46193         var old = this.el.dom.value ;
46194         this.el.dom.value = v;
46195         if (suppressEvent) {
46196             return ;
46197         }
46198          
46199         // update display..
46200         this.viewEl.select('img',true).each(function(e,i,n)  {
46201             
46202             var on = e.is(".x-menu-item-checked");
46203             var newv = v.indexOf(String(n)) > -1;
46204             if (on != newv) {
46205                 e.toggleClass('x-menu-item-checked');
46206             }
46207             
46208         });
46209         
46210         
46211         this.fireEvent('change', this, v, old);
46212         
46213         
46214     },
46215    
46216     // handle setting of hidden value by some other method!!?!?
46217     setFromHidden: function()
46218     {
46219         if(!this.el){
46220             return;
46221         }
46222         //console.log("SET FROM HIDDEN");
46223         //alert('setFrom hidden');
46224         this.setValue(this.el.dom.value);
46225     },
46226     
46227     onDestroy : function()
46228     {
46229         if(this.viewEl){
46230             Roo.get(this.viewEl).remove();
46231         }
46232          
46233         Roo.form.DayPicker.superclass.onDestroy.call(this);
46234     }
46235
46236 });/*
46237  * RooJS Library 1.1.1
46238  * Copyright(c) 2008-2011  Alan Knowles
46239  *
46240  * License - LGPL
46241  */
46242  
46243
46244 /**
46245  * @class Roo.form.ComboCheck
46246  * @extends Roo.form.ComboBox
46247  * A combobox for multiple select items.
46248  *
46249  * FIXME - could do with a reset button..
46250  * 
46251  * @constructor
46252  * Create a new ComboCheck
46253  * @param {Object} config Configuration options
46254  */
46255 Roo.form.ComboCheck = function(config){
46256     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46257     // should verify some data...
46258     // like
46259     // hiddenName = required..
46260     // displayField = required
46261     // valudField == required
46262     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46263     var _t = this;
46264     Roo.each(req, function(e) {
46265         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46266             throw "Roo.form.ComboCheck : missing value for: " + e;
46267         }
46268     });
46269     
46270     
46271 };
46272
46273 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46274      
46275      
46276     editable : false,
46277      
46278     selectedClass: 'x-menu-item-checked', 
46279     
46280     // private
46281     onRender : function(ct, position){
46282         var _t = this;
46283         
46284         
46285         
46286         if(!this.tpl){
46287             var cls = 'x-combo-list';
46288
46289             
46290             this.tpl =  new Roo.Template({
46291                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46292                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46293                    '<span>{' + this.displayField + '}</span>' +
46294                     '</div>' 
46295                 
46296             });
46297         }
46298  
46299         
46300         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46301         this.view.singleSelect = false;
46302         this.view.multiSelect = true;
46303         this.view.toggleSelect = true;
46304         this.pageTb.add(new Roo.Toolbar.Fill(), {
46305             
46306             text: 'Done',
46307             handler: function()
46308             {
46309                 _t.collapse();
46310             }
46311         });
46312     },
46313     
46314     onViewOver : function(e, t){
46315         // do nothing...
46316         return;
46317         
46318     },
46319     
46320     onViewClick : function(doFocus,index){
46321         return;
46322         
46323     },
46324     select: function () {
46325         //Roo.log("SELECT CALLED");
46326     },
46327      
46328     selectByValue : function(xv, scrollIntoView){
46329         var ar = this.getValueArray();
46330         var sels = [];
46331         
46332         Roo.each(ar, function(v) {
46333             if(v === undefined || v === null){
46334                 return;
46335             }
46336             var r = this.findRecord(this.valueField, v);
46337             if(r){
46338                 sels.push(this.store.indexOf(r))
46339                 
46340             }
46341         },this);
46342         this.view.select(sels);
46343         return false;
46344     },
46345     
46346     
46347     
46348     onSelect : function(record, index){
46349        // Roo.log("onselect Called");
46350        // this is only called by the clear button now..
46351         this.view.clearSelections();
46352         this.setValue('[]');
46353         if (this.value != this.valueBefore) {
46354             this.fireEvent('change', this, this.value, this.valueBefore);
46355             this.valueBefore = this.value;
46356         }
46357     },
46358     getValueArray : function()
46359     {
46360         var ar = [] ;
46361         
46362         try {
46363             //Roo.log(this.value);
46364             if (typeof(this.value) == 'undefined') {
46365                 return [];
46366             }
46367             var ar = Roo.decode(this.value);
46368             return  ar instanceof Array ? ar : []; //?? valid?
46369             
46370         } catch(e) {
46371             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46372             return [];
46373         }
46374          
46375     },
46376     expand : function ()
46377     {
46378         
46379         Roo.form.ComboCheck.superclass.expand.call(this);
46380         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46381         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46382         
46383
46384     },
46385     
46386     collapse : function(){
46387         Roo.form.ComboCheck.superclass.collapse.call(this);
46388         var sl = this.view.getSelectedIndexes();
46389         var st = this.store;
46390         var nv = [];
46391         var tv = [];
46392         var r;
46393         Roo.each(sl, function(i) {
46394             r = st.getAt(i);
46395             nv.push(r.get(this.valueField));
46396         },this);
46397         this.setValue(Roo.encode(nv));
46398         if (this.value != this.valueBefore) {
46399
46400             this.fireEvent('change', this, this.value, this.valueBefore);
46401             this.valueBefore = this.value;
46402         }
46403         
46404     },
46405     
46406     setValue : function(v){
46407         // Roo.log(v);
46408         this.value = v;
46409         
46410         var vals = this.getValueArray();
46411         var tv = [];
46412         Roo.each(vals, function(k) {
46413             var r = this.findRecord(this.valueField, k);
46414             if(r){
46415                 tv.push(r.data[this.displayField]);
46416             }else if(this.valueNotFoundText !== undefined){
46417                 tv.push( this.valueNotFoundText );
46418             }
46419         },this);
46420        // Roo.log(tv);
46421         
46422         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46423         this.hiddenField.value = v;
46424         this.value = v;
46425     }
46426     
46427 });/*
46428  * Based on:
46429  * Ext JS Library 1.1.1
46430  * Copyright(c) 2006-2007, Ext JS, LLC.
46431  *
46432  * Originally Released Under LGPL - original licence link has changed is not relivant.
46433  *
46434  * Fork - LGPL
46435  * <script type="text/javascript">
46436  */
46437  
46438 /**
46439  * @class Roo.form.Signature
46440  * @extends Roo.form.Field
46441  * Signature field.  
46442  * @constructor
46443  * 
46444  * @param {Object} config Configuration options
46445  */
46446
46447 Roo.form.Signature = function(config){
46448     Roo.form.Signature.superclass.constructor.call(this, config);
46449     
46450     this.addEvents({// not in used??
46451          /**
46452          * @event confirm
46453          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46454              * @param {Roo.form.Signature} combo This combo box
46455              */
46456         'confirm' : true,
46457         /**
46458          * @event reset
46459          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46460              * @param {Roo.form.ComboBox} combo This combo box
46461              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46462              */
46463         'reset' : true
46464     });
46465 };
46466
46467 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46468     /**
46469      * @cfg {Object} labels Label to use when rendering a form.
46470      * defaults to 
46471      * labels : { 
46472      *      clear : "Clear",
46473      *      confirm : "Confirm"
46474      *  }
46475      */
46476     labels : { 
46477         clear : "Clear",
46478         confirm : "Confirm"
46479     },
46480     /**
46481      * @cfg {Number} width The signature panel width (defaults to 300)
46482      */
46483     width: 300,
46484     /**
46485      * @cfg {Number} height The signature panel height (defaults to 100)
46486      */
46487     height : 100,
46488     /**
46489      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46490      */
46491     allowBlank : false,
46492     
46493     //private
46494     // {Object} signPanel The signature SVG panel element (defaults to {})
46495     signPanel : {},
46496     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46497     isMouseDown : false,
46498     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46499     isConfirmed : false,
46500     // {String} signatureTmp SVG mapping string (defaults to empty string)
46501     signatureTmp : '',
46502     
46503     
46504     defaultAutoCreate : { // modified by initCompnoent..
46505         tag: "input",
46506         type:"hidden"
46507     },
46508
46509     // private
46510     onRender : function(ct, position){
46511         
46512         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46513         
46514         this.wrap = this.el.wrap({
46515             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46516         });
46517         
46518         this.createToolbar(this);
46519         this.signPanel = this.wrap.createChild({
46520                 tag: 'div',
46521                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46522             }, this.el
46523         );
46524             
46525         this.svgID = Roo.id();
46526         this.svgEl = this.signPanel.createChild({
46527               xmlns : 'http://www.w3.org/2000/svg',
46528               tag : 'svg',
46529               id : this.svgID + "-svg",
46530               width: this.width,
46531               height: this.height,
46532               viewBox: '0 0 '+this.width+' '+this.height,
46533               cn : [
46534                 {
46535                     tag: "rect",
46536                     id: this.svgID + "-svg-r",
46537                     width: this.width,
46538                     height: this.height,
46539                     fill: "#ffa"
46540                 },
46541                 {
46542                     tag: "line",
46543                     id: this.svgID + "-svg-l",
46544                     x1: "0", // start
46545                     y1: (this.height*0.8), // start set the line in 80% of height
46546                     x2: this.width, // end
46547                     y2: (this.height*0.8), // end set the line in 80% of height
46548                     'stroke': "#666",
46549                     'stroke-width': "1",
46550                     'stroke-dasharray': "3",
46551                     'shape-rendering': "crispEdges",
46552                     'pointer-events': "none"
46553                 },
46554                 {
46555                     tag: "path",
46556                     id: this.svgID + "-svg-p",
46557                     'stroke': "navy",
46558                     'stroke-width': "3",
46559                     'fill': "none",
46560                     'pointer-events': 'none'
46561                 }
46562               ]
46563         });
46564         this.createSVG();
46565         this.svgBox = this.svgEl.dom.getScreenCTM();
46566     },
46567     createSVG : function(){ 
46568         var svg = this.signPanel;
46569         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46570         var t = this;
46571
46572         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46573         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46574         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46575         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46576         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46577         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46578         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46579         
46580     },
46581     isTouchEvent : function(e){
46582         return e.type.match(/^touch/);
46583     },
46584     getCoords : function (e) {
46585         var pt    = this.svgEl.dom.createSVGPoint();
46586         pt.x = e.clientX; 
46587         pt.y = e.clientY;
46588         if (this.isTouchEvent(e)) {
46589             pt.x =  e.targetTouches[0].clientX 
46590             pt.y = e.targetTouches[0].clientY;
46591         }
46592         var a = this.svgEl.dom.getScreenCTM();
46593         var b = a.inverse();
46594         var mx = pt.matrixTransform(b);
46595         return mx.x + ',' + mx.y;
46596     },
46597     //mouse event headler 
46598     down : function (e) {
46599         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46600         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46601         
46602         this.isMouseDown = true;
46603         
46604         e.preventDefault();
46605     },
46606     move : function (e) {
46607         if (this.isMouseDown) {
46608             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46609             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46610         }
46611         
46612         e.preventDefault();
46613     },
46614     up : function (e) {
46615         this.isMouseDown = false;
46616         var sp = this.signatureTmp.split(' ');
46617         
46618         if(sp.length > 1){
46619             if(!sp[sp.length-2].match(/^L/)){
46620                 sp.pop();
46621                 sp.pop();
46622                 sp.push("");
46623                 this.signatureTmp = sp.join(" ");
46624             }
46625         }
46626         if(this.getValue() != this.signatureTmp){
46627             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46628             this.isConfirmed = false;
46629         }
46630         e.preventDefault();
46631     },
46632     
46633     /**
46634      * Protected method that will not generally be called directly. It
46635      * is called when the editor creates its toolbar. Override this method if you need to
46636      * add custom toolbar buttons.
46637      * @param {HtmlEditor} editor
46638      */
46639     createToolbar : function(editor){
46640          function btn(id, toggle, handler){
46641             var xid = fid + '-'+ id ;
46642             return {
46643                 id : xid,
46644                 cmd : id,
46645                 cls : 'x-btn-icon x-edit-'+id,
46646                 enableToggle:toggle !== false,
46647                 scope: editor, // was editor...
46648                 handler:handler||editor.relayBtnCmd,
46649                 clickEvent:'mousedown',
46650                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46651                 tabIndex:-1
46652             };
46653         }
46654         
46655         
46656         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46657         this.tb = tb;
46658         this.tb.add(
46659            {
46660                 cls : ' x-signature-btn x-signature-'+id,
46661                 scope: editor, // was editor...
46662                 handler: this.reset,
46663                 clickEvent:'mousedown',
46664                 text: this.labels.clear
46665             },
46666             {
46667                  xtype : 'Fill',
46668                  xns: Roo.Toolbar
46669             }, 
46670             {
46671                 cls : '  x-signature-btn x-signature-'+id,
46672                 scope: editor, // was editor...
46673                 handler: this.confirmHandler,
46674                 clickEvent:'mousedown',
46675                 text: this.labels.confirm
46676             }
46677         );
46678     
46679     },
46680     //public
46681     /**
46682      * when user is clicked confirm then show this image.....
46683      * 
46684      * @return {String} Image Data URI
46685      */
46686     getImageDataURI : function(){
46687         var svg = this.svgEl.dom.parentNode.innerHTML;
46688         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46689         return src; 
46690     },
46691     /**
46692      * 
46693      * @return {Boolean} this.isConfirmed
46694      */
46695     getConfirmed : function(){
46696         return this.isConfirmed;
46697     },
46698     /**
46699      * 
46700      * @return {Number} this.width
46701      */
46702     getWidth : function(){
46703         return this.width;
46704     },
46705     /**
46706      * 
46707      * @return {Number} this.height
46708      */
46709     getHeight : function(){
46710         return this.height;
46711     },
46712     // private
46713     getSignature : function(){
46714         return this.signatureTmp;
46715     },
46716     // private
46717     reset : function(){
46718         this.signatureTmp = '';
46719         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46720         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46721         this.isConfirmed = false;
46722         Roo.form.Signature.superclass.reset.call(this);
46723     },
46724     setSignature : function(s){
46725         this.signatureTmp = s;
46726         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46727         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46728         this.setValue(s);
46729         this.isConfirmed = false;
46730         Roo.form.Signature.superclass.reset.call(this);
46731     }, 
46732     test : function(){
46733 //        Roo.log(this.signPanel.dom.contentWindow.up())
46734     },
46735     //private
46736     setConfirmed : function(){
46737         
46738         
46739         
46740 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46741     },
46742     // private
46743     confirmHandler : function(){
46744         if(!this.getSignature()){
46745             return;
46746         }
46747         
46748         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46749         this.setValue(this.getSignature());
46750         this.isConfirmed = true;
46751         
46752         this.fireEvent('confirm', this);
46753     },
46754     // private
46755     // Subclasses should provide the validation implementation by overriding this
46756     validateValue : function(value){
46757         if(this.allowBlank){
46758             return true;
46759         }
46760         
46761         if(this.isConfirmed){
46762             return true;
46763         }
46764         return false;
46765     }
46766 });/*
46767  * Based on:
46768  * Ext JS Library 1.1.1
46769  * Copyright(c) 2006-2007, Ext JS, LLC.
46770  *
46771  * Originally Released Under LGPL - original licence link has changed is not relivant.
46772  *
46773  * Fork - LGPL
46774  * <script type="text/javascript">
46775  */
46776  
46777
46778 /**
46779  * @class Roo.form.ComboBox
46780  * @extends Roo.form.TriggerField
46781  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46782  * @constructor
46783  * Create a new ComboBox.
46784  * @param {Object} config Configuration options
46785  */
46786 Roo.form.Select = function(config){
46787     Roo.form.Select.superclass.constructor.call(this, config);
46788      
46789 };
46790
46791 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46792     /**
46793      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46794      */
46795     /**
46796      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46797      * rendering into an Roo.Editor, defaults to false)
46798      */
46799     /**
46800      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46801      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46802      */
46803     /**
46804      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46805      */
46806     /**
46807      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46808      * the dropdown list (defaults to undefined, with no header element)
46809      */
46810
46811      /**
46812      * @cfg {String/Roo.Template} tpl The template to use to render the output
46813      */
46814      
46815     // private
46816     defaultAutoCreate : {tag: "select"  },
46817     /**
46818      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46819      */
46820     listWidth: undefined,
46821     /**
46822      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46823      * mode = 'remote' or 'text' if mode = 'local')
46824      */
46825     displayField: undefined,
46826     /**
46827      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46828      * mode = 'remote' or 'value' if mode = 'local'). 
46829      * Note: use of a valueField requires the user make a selection
46830      * in order for a value to be mapped.
46831      */
46832     valueField: undefined,
46833     
46834     
46835     /**
46836      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46837      * field's data value (defaults to the underlying DOM element's name)
46838      */
46839     hiddenName: undefined,
46840     /**
46841      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46842      */
46843     listClass: '',
46844     /**
46845      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46846      */
46847     selectedClass: 'x-combo-selected',
46848     /**
46849      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46850      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46851      * which displays a downward arrow icon).
46852      */
46853     triggerClass : 'x-form-arrow-trigger',
46854     /**
46855      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46856      */
46857     shadow:'sides',
46858     /**
46859      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46860      * anchor positions (defaults to 'tl-bl')
46861      */
46862     listAlign: 'tl-bl?',
46863     /**
46864      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46865      */
46866     maxHeight: 300,
46867     /**
46868      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46869      * query specified by the allQuery config option (defaults to 'query')
46870      */
46871     triggerAction: 'query',
46872     /**
46873      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46874      * (defaults to 4, does not apply if editable = false)
46875      */
46876     minChars : 4,
46877     /**
46878      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46879      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46880      */
46881     typeAhead: false,
46882     /**
46883      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46884      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46885      */
46886     queryDelay: 500,
46887     /**
46888      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46889      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46890      */
46891     pageSize: 0,
46892     /**
46893      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46894      * when editable = true (defaults to false)
46895      */
46896     selectOnFocus:false,
46897     /**
46898      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46899      */
46900     queryParam: 'query',
46901     /**
46902      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46903      * when mode = 'remote' (defaults to 'Loading...')
46904      */
46905     loadingText: 'Loading...',
46906     /**
46907      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46908      */
46909     resizable: false,
46910     /**
46911      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46912      */
46913     handleHeight : 8,
46914     /**
46915      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46916      * traditional select (defaults to true)
46917      */
46918     editable: true,
46919     /**
46920      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46921      */
46922     allQuery: '',
46923     /**
46924      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46925      */
46926     mode: 'remote',
46927     /**
46928      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46929      * listWidth has a higher value)
46930      */
46931     minListWidth : 70,
46932     /**
46933      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46934      * allow the user to set arbitrary text into the field (defaults to false)
46935      */
46936     forceSelection:false,
46937     /**
46938      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46939      * if typeAhead = true (defaults to 250)
46940      */
46941     typeAheadDelay : 250,
46942     /**
46943      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46944      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46945      */
46946     valueNotFoundText : undefined,
46947     
46948     /**
46949      * @cfg {String} defaultValue The value displayed after loading the store.
46950      */
46951     defaultValue: '',
46952     
46953     /**
46954      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46955      */
46956     blockFocus : false,
46957     
46958     /**
46959      * @cfg {Boolean} disableClear Disable showing of clear button.
46960      */
46961     disableClear : false,
46962     /**
46963      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46964      */
46965     alwaysQuery : false,
46966     
46967     //private
46968     addicon : false,
46969     editicon: false,
46970     
46971     // element that contains real text value.. (when hidden is used..)
46972      
46973     // private
46974     onRender : function(ct, position){
46975         Roo.form.Field.prototype.onRender.call(this, ct, position);
46976         
46977         if(this.store){
46978             this.store.on('beforeload', this.onBeforeLoad, this);
46979             this.store.on('load', this.onLoad, this);
46980             this.store.on('loadexception', this.onLoadException, this);
46981             this.store.load({});
46982         }
46983         
46984         
46985         
46986     },
46987
46988     // private
46989     initEvents : function(){
46990         //Roo.form.ComboBox.superclass.initEvents.call(this);
46991  
46992     },
46993
46994     onDestroy : function(){
46995        
46996         if(this.store){
46997             this.store.un('beforeload', this.onBeforeLoad, this);
46998             this.store.un('load', this.onLoad, this);
46999             this.store.un('loadexception', this.onLoadException, this);
47000         }
47001         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47002     },
47003
47004     // private
47005     fireKey : function(e){
47006         if(e.isNavKeyPress() && !this.list.isVisible()){
47007             this.fireEvent("specialkey", this, e);
47008         }
47009     },
47010
47011     // private
47012     onResize: function(w, h){
47013         
47014         return; 
47015     
47016         
47017     },
47018
47019     /**
47020      * Allow or prevent the user from directly editing the field text.  If false is passed,
47021      * the user will only be able to select from the items defined in the dropdown list.  This method
47022      * is the runtime equivalent of setting the 'editable' config option at config time.
47023      * @param {Boolean} value True to allow the user to directly edit the field text
47024      */
47025     setEditable : function(value){
47026          
47027     },
47028
47029     // private
47030     onBeforeLoad : function(){
47031         
47032         Roo.log("Select before load");
47033         return;
47034     
47035         this.innerList.update(this.loadingText ?
47036                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47037         //this.restrictHeight();
47038         this.selectedIndex = -1;
47039     },
47040
47041     // private
47042     onLoad : function(){
47043
47044     
47045         var dom = this.el.dom;
47046         dom.innerHTML = '';
47047          var od = dom.ownerDocument;
47048          
47049         if (this.emptyText) {
47050             var op = od.createElement('option');
47051             op.setAttribute('value', '');
47052             op.innerHTML = String.format('{0}', this.emptyText);
47053             dom.appendChild(op);
47054         }
47055         if(this.store.getCount() > 0){
47056            
47057             var vf = this.valueField;
47058             var df = this.displayField;
47059             this.store.data.each(function(r) {
47060                 // which colmsn to use... testing - cdoe / title..
47061                 var op = od.createElement('option');
47062                 op.setAttribute('value', r.data[vf]);
47063                 op.innerHTML = String.format('{0}', r.data[df]);
47064                 dom.appendChild(op);
47065             });
47066             if (typeof(this.defaultValue != 'undefined')) {
47067                 this.setValue(this.defaultValue);
47068             }
47069             
47070              
47071         }else{
47072             //this.onEmptyResults();
47073         }
47074         //this.el.focus();
47075     },
47076     // private
47077     onLoadException : function()
47078     {
47079         dom.innerHTML = '';
47080             
47081         Roo.log("Select on load exception");
47082         return;
47083     
47084         this.collapse();
47085         Roo.log(this.store.reader.jsonData);
47086         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47087             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47088         }
47089         
47090         
47091     },
47092     // private
47093     onTypeAhead : function(){
47094          
47095     },
47096
47097     // private
47098     onSelect : function(record, index){
47099         Roo.log('on select?');
47100         return;
47101         if(this.fireEvent('beforeselect', this, record, index) !== false){
47102             this.setFromData(index > -1 ? record.data : false);
47103             this.collapse();
47104             this.fireEvent('select', this, record, index);
47105         }
47106     },
47107
47108     /**
47109      * Returns the currently selected field value or empty string if no value is set.
47110      * @return {String} value The selected value
47111      */
47112     getValue : function(){
47113         var dom = this.el.dom;
47114         this.value = dom.options[dom.selectedIndex].value;
47115         return this.value;
47116         
47117     },
47118
47119     /**
47120      * Clears any text/value currently set in the field
47121      */
47122     clearValue : function(){
47123         this.value = '';
47124         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47125         
47126     },
47127
47128     /**
47129      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47130      * will be displayed in the field.  If the value does not match the data value of an existing item,
47131      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47132      * Otherwise the field will be blank (although the value will still be set).
47133      * @param {String} value The value to match
47134      */
47135     setValue : function(v){
47136         var d = this.el.dom;
47137         for (var i =0; i < d.options.length;i++) {
47138             if (v == d.options[i].value) {
47139                 d.selectedIndex = i;
47140                 this.value = v;
47141                 return;
47142             }
47143         }
47144         this.clearValue();
47145     },
47146     /**
47147      * @property {Object} the last set data for the element
47148      */
47149     
47150     lastData : false,
47151     /**
47152      * Sets the value of the field based on a object which is related to the record format for the store.
47153      * @param {Object} value the value to set as. or false on reset?
47154      */
47155     setFromData : function(o){
47156         Roo.log('setfrom data?');
47157          
47158         
47159         
47160     },
47161     // private
47162     reset : function(){
47163         this.clearValue();
47164     },
47165     // private
47166     findRecord : function(prop, value){
47167         
47168         return false;
47169     
47170         var record;
47171         if(this.store.getCount() > 0){
47172             this.store.each(function(r){
47173                 if(r.data[prop] == value){
47174                     record = r;
47175                     return false;
47176                 }
47177                 return true;
47178             });
47179         }
47180         return record;
47181     },
47182     
47183     getName: function()
47184     {
47185         // returns hidden if it's set..
47186         if (!this.rendered) {return ''};
47187         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47188         
47189     },
47190      
47191
47192     
47193
47194     // private
47195     onEmptyResults : function(){
47196         Roo.log('empty results');
47197         //this.collapse();
47198     },
47199
47200     /**
47201      * Returns true if the dropdown list is expanded, else false.
47202      */
47203     isExpanded : function(){
47204         return false;
47205     },
47206
47207     /**
47208      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47209      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47210      * @param {String} value The data value of the item to select
47211      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47212      * selected item if it is not currently in view (defaults to true)
47213      * @return {Boolean} True if the value matched an item in the list, else false
47214      */
47215     selectByValue : function(v, scrollIntoView){
47216         Roo.log('select By Value');
47217         return false;
47218     
47219         if(v !== undefined && v !== null){
47220             var r = this.findRecord(this.valueField || this.displayField, v);
47221             if(r){
47222                 this.select(this.store.indexOf(r), scrollIntoView);
47223                 return true;
47224             }
47225         }
47226         return false;
47227     },
47228
47229     /**
47230      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47231      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47232      * @param {Number} index The zero-based index of the list item to select
47233      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47234      * selected item if it is not currently in view (defaults to true)
47235      */
47236     select : function(index, scrollIntoView){
47237         Roo.log('select ');
47238         return  ;
47239         
47240         this.selectedIndex = index;
47241         this.view.select(index);
47242         if(scrollIntoView !== false){
47243             var el = this.view.getNode(index);
47244             if(el){
47245                 this.innerList.scrollChildIntoView(el, false);
47246             }
47247         }
47248     },
47249
47250       
47251
47252     // private
47253     validateBlur : function(){
47254         
47255         return;
47256         
47257     },
47258
47259     // private
47260     initQuery : function(){
47261         this.doQuery(this.getRawValue());
47262     },
47263
47264     // private
47265     doForce : function(){
47266         if(this.el.dom.value.length > 0){
47267             this.el.dom.value =
47268                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47269              
47270         }
47271     },
47272
47273     /**
47274      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47275      * query allowing the query action to be canceled if needed.
47276      * @param {String} query The SQL query to execute
47277      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47278      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47279      * saved in the current store (defaults to false)
47280      */
47281     doQuery : function(q, forceAll){
47282         
47283         Roo.log('doQuery?');
47284         if(q === undefined || q === null){
47285             q = '';
47286         }
47287         var qe = {
47288             query: q,
47289             forceAll: forceAll,
47290             combo: this,
47291             cancel:false
47292         };
47293         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47294             return false;
47295         }
47296         q = qe.query;
47297         forceAll = qe.forceAll;
47298         if(forceAll === true || (q.length >= this.minChars)){
47299             if(this.lastQuery != q || this.alwaysQuery){
47300                 this.lastQuery = q;
47301                 if(this.mode == 'local'){
47302                     this.selectedIndex = -1;
47303                     if(forceAll){
47304                         this.store.clearFilter();
47305                     }else{
47306                         this.store.filter(this.displayField, q);
47307                     }
47308                     this.onLoad();
47309                 }else{
47310                     this.store.baseParams[this.queryParam] = q;
47311                     this.store.load({
47312                         params: this.getParams(q)
47313                     });
47314                     this.expand();
47315                 }
47316             }else{
47317                 this.selectedIndex = -1;
47318                 this.onLoad();   
47319             }
47320         }
47321     },
47322
47323     // private
47324     getParams : function(q){
47325         var p = {};
47326         //p[this.queryParam] = q;
47327         if(this.pageSize){
47328             p.start = 0;
47329             p.limit = this.pageSize;
47330         }
47331         return p;
47332     },
47333
47334     /**
47335      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47336      */
47337     collapse : function(){
47338         
47339     },
47340
47341     // private
47342     collapseIf : function(e){
47343         
47344     },
47345
47346     /**
47347      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47348      */
47349     expand : function(){
47350         
47351     } ,
47352
47353     // private
47354      
47355
47356     /** 
47357     * @cfg {Boolean} grow 
47358     * @hide 
47359     */
47360     /** 
47361     * @cfg {Number} growMin 
47362     * @hide 
47363     */
47364     /** 
47365     * @cfg {Number} growMax 
47366     * @hide 
47367     */
47368     /**
47369      * @hide
47370      * @method autoSize
47371      */
47372     
47373     setWidth : function()
47374     {
47375         
47376     },
47377     getResizeEl : function(){
47378         return this.el;
47379     }
47380 });//<script type="text/javasscript">
47381  
47382
47383 /**
47384  * @class Roo.DDView
47385  * A DnD enabled version of Roo.View.
47386  * @param {Element/String} container The Element in which to create the View.
47387  * @param {String} tpl The template string used to create the markup for each element of the View
47388  * @param {Object} config The configuration properties. These include all the config options of
47389  * {@link Roo.View} plus some specific to this class.<br>
47390  * <p>
47391  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47392  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47393  * <p>
47394  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47395 .x-view-drag-insert-above {
47396         border-top:1px dotted #3366cc;
47397 }
47398 .x-view-drag-insert-below {
47399         border-bottom:1px dotted #3366cc;
47400 }
47401 </code></pre>
47402  * 
47403  */
47404  
47405 Roo.DDView = function(container, tpl, config) {
47406     Roo.DDView.superclass.constructor.apply(this, arguments);
47407     this.getEl().setStyle("outline", "0px none");
47408     this.getEl().unselectable();
47409     if (this.dragGroup) {
47410                 this.setDraggable(this.dragGroup.split(","));
47411     }
47412     if (this.dropGroup) {
47413                 this.setDroppable(this.dropGroup.split(","));
47414     }
47415     if (this.deletable) {
47416         this.setDeletable();
47417     }
47418     this.isDirtyFlag = false;
47419         this.addEvents({
47420                 "drop" : true
47421         });
47422 };
47423
47424 Roo.extend(Roo.DDView, Roo.View, {
47425 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47426 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47427 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47428 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47429
47430         isFormField: true,
47431
47432         reset: Roo.emptyFn,
47433         
47434         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47435
47436         validate: function() {
47437                 return true;
47438         },
47439         
47440         destroy: function() {
47441                 this.purgeListeners();
47442                 this.getEl.removeAllListeners();
47443                 this.getEl().remove();
47444                 if (this.dragZone) {
47445                         if (this.dragZone.destroy) {
47446                                 this.dragZone.destroy();
47447                         }
47448                 }
47449                 if (this.dropZone) {
47450                         if (this.dropZone.destroy) {
47451                                 this.dropZone.destroy();
47452                         }
47453                 }
47454         },
47455
47456 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47457         getName: function() {
47458                 return this.name;
47459         },
47460
47461 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47462         setValue: function(v) {
47463                 if (!this.store) {
47464                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47465                 }
47466                 var data = {};
47467                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47468                 this.store.proxy = new Roo.data.MemoryProxy(data);
47469                 this.store.load();
47470         },
47471
47472 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47473         getValue: function() {
47474                 var result = '(';
47475                 this.store.each(function(rec) {
47476                         result += rec.id + ',';
47477                 });
47478                 return result.substr(0, result.length - 1) + ')';
47479         },
47480         
47481         getIds: function() {
47482                 var i = 0, result = new Array(this.store.getCount());
47483                 this.store.each(function(rec) {
47484                         result[i++] = rec.id;
47485                 });
47486                 return result;
47487         },
47488         
47489         isDirty: function() {
47490                 return this.isDirtyFlag;
47491         },
47492
47493 /**
47494  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47495  *      whole Element becomes the target, and this causes the drop gesture to append.
47496  */
47497     getTargetFromEvent : function(e) {
47498                 var target = e.getTarget();
47499                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47500                 target = target.parentNode;
47501                 }
47502                 if (!target) {
47503                         target = this.el.dom.lastChild || this.el.dom;
47504                 }
47505                 return target;
47506     },
47507
47508 /**
47509  *      Create the drag data which consists of an object which has the property "ddel" as
47510  *      the drag proxy element. 
47511  */
47512     getDragData : function(e) {
47513         var target = this.findItemFromChild(e.getTarget());
47514                 if(target) {
47515                         this.handleSelection(e);
47516                         var selNodes = this.getSelectedNodes();
47517             var dragData = {
47518                 source: this,
47519                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47520                 nodes: selNodes,
47521                 records: []
47522                         };
47523                         var selectedIndices = this.getSelectedIndexes();
47524                         for (var i = 0; i < selectedIndices.length; i++) {
47525                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47526                         }
47527                         if (selNodes.length == 1) {
47528                                 dragData.ddel = target.cloneNode(true); // the div element
47529                         } else {
47530                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47531                                 div.className = 'multi-proxy';
47532                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47533                                         div.appendChild(selNodes[i].cloneNode(true));
47534                                 }
47535                                 dragData.ddel = div;
47536                         }
47537             //console.log(dragData)
47538             //console.log(dragData.ddel.innerHTML)
47539                         return dragData;
47540                 }
47541         //console.log('nodragData')
47542                 return false;
47543     },
47544     
47545 /**     Specify to which ddGroup items in this DDView may be dragged. */
47546     setDraggable: function(ddGroup) {
47547         if (ddGroup instanceof Array) {
47548                 Roo.each(ddGroup, this.setDraggable, this);
47549                 return;
47550         }
47551         if (this.dragZone) {
47552                 this.dragZone.addToGroup(ddGroup);
47553         } else {
47554                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47555                                 containerScroll: true,
47556                                 ddGroup: ddGroup 
47557
47558                         });
47559 //                      Draggability implies selection. DragZone's mousedown selects the element.
47560                         if (!this.multiSelect) { this.singleSelect = true; }
47561
47562 //                      Wire the DragZone's handlers up to methods in *this*
47563                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47564                 }
47565     },
47566
47567 /**     Specify from which ddGroup this DDView accepts drops. */
47568     setDroppable: function(ddGroup) {
47569         if (ddGroup instanceof Array) {
47570                 Roo.each(ddGroup, this.setDroppable, this);
47571                 return;
47572         }
47573         if (this.dropZone) {
47574                 this.dropZone.addToGroup(ddGroup);
47575         } else {
47576                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47577                                 containerScroll: true,
47578                                 ddGroup: ddGroup
47579                         });
47580
47581 //                      Wire the DropZone's handlers up to methods in *this*
47582                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47583                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47584                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47585                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47586                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47587                 }
47588     },
47589
47590 /**     Decide whether to drop above or below a View node. */
47591     getDropPoint : function(e, n, dd){
47592         if (n == this.el.dom) { return "above"; }
47593                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47594                 var c = t + (b - t) / 2;
47595                 var y = Roo.lib.Event.getPageY(e);
47596                 if(y <= c) {
47597                         return "above";
47598                 }else{
47599                         return "below";
47600                 }
47601     },
47602
47603     onNodeEnter : function(n, dd, e, data){
47604                 return false;
47605     },
47606     
47607     onNodeOver : function(n, dd, e, data){
47608                 var pt = this.getDropPoint(e, n, dd);
47609                 // set the insert point style on the target node
47610                 var dragElClass = this.dropNotAllowed;
47611                 if (pt) {
47612                         var targetElClass;
47613                         if (pt == "above"){
47614                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47615                                 targetElClass = "x-view-drag-insert-above";
47616                         } else {
47617                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47618                                 targetElClass = "x-view-drag-insert-below";
47619                         }
47620                         if (this.lastInsertClass != targetElClass){
47621                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47622                                 this.lastInsertClass = targetElClass;
47623                         }
47624                 }
47625                 return dragElClass;
47626         },
47627
47628     onNodeOut : function(n, dd, e, data){
47629                 this.removeDropIndicators(n);
47630     },
47631
47632     onNodeDrop : function(n, dd, e, data){
47633         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47634                 return false;
47635         }
47636         var pt = this.getDropPoint(e, n, dd);
47637                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47638                 if (pt == "below") { insertAt++; }
47639                 for (var i = 0; i < data.records.length; i++) {
47640                         var r = data.records[i];
47641                         var dup = this.store.getById(r.id);
47642                         if (dup && (dd != this.dragZone)) {
47643                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
47644                         } else {
47645                                 if (data.copy) {
47646                                         this.store.insert(insertAt++, r.copy());
47647                                 } else {
47648                                         data.source.isDirtyFlag = true;
47649                                         r.store.remove(r);
47650                                         this.store.insert(insertAt++, r);
47651                                 }
47652                                 this.isDirtyFlag = true;
47653                         }
47654                 }
47655                 this.dragZone.cachedTarget = null;
47656                 return true;
47657     },
47658
47659     removeDropIndicators : function(n){
47660                 if(n){
47661                         Roo.fly(n).removeClass([
47662                                 "x-view-drag-insert-above",
47663                                 "x-view-drag-insert-below"]);
47664                         this.lastInsertClass = "_noclass";
47665                 }
47666     },
47667
47668 /**
47669  *      Utility method. Add a delete option to the DDView's context menu.
47670  *      @param {String} imageUrl The URL of the "delete" icon image.
47671  */
47672         setDeletable: function(imageUrl) {
47673                 if (!this.singleSelect && !this.multiSelect) {
47674                         this.singleSelect = true;
47675                 }
47676                 var c = this.getContextMenu();
47677                 this.contextMenu.on("itemclick", function(item) {
47678                         switch (item.id) {
47679                                 case "delete":
47680                                         this.remove(this.getSelectedIndexes());
47681                                         break;
47682                         }
47683                 }, this);
47684                 this.contextMenu.add({
47685                         icon: imageUrl,
47686                         id: "delete",
47687                         text: 'Delete'
47688                 });
47689         },
47690         
47691 /**     Return the context menu for this DDView. */
47692         getContextMenu: function() {
47693                 if (!this.contextMenu) {
47694 //                      Create the View's context menu
47695                         this.contextMenu = new Roo.menu.Menu({
47696                                 id: this.id + "-contextmenu"
47697                         });
47698                         this.el.on("contextmenu", this.showContextMenu, this);
47699                 }
47700                 return this.contextMenu;
47701         },
47702         
47703         disableContextMenu: function() {
47704                 if (this.contextMenu) {
47705                         this.el.un("contextmenu", this.showContextMenu, this);
47706                 }
47707         },
47708
47709         showContextMenu: function(e, item) {
47710         item = this.findItemFromChild(e.getTarget());
47711                 if (item) {
47712                         e.stopEvent();
47713                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47714                         this.contextMenu.showAt(e.getXY());
47715             }
47716     },
47717
47718 /**
47719  *      Remove {@link Roo.data.Record}s at the specified indices.
47720  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47721  */
47722     remove: function(selectedIndices) {
47723                 selectedIndices = [].concat(selectedIndices);
47724                 for (var i = 0; i < selectedIndices.length; i++) {
47725                         var rec = this.store.getAt(selectedIndices[i]);
47726                         this.store.remove(rec);
47727                 }
47728     },
47729
47730 /**
47731  *      Double click fires the event, but also, if this is draggable, and there is only one other
47732  *      related DropZone, it transfers the selected node.
47733  */
47734     onDblClick : function(e){
47735         var item = this.findItemFromChild(e.getTarget());
47736         if(item){
47737             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47738                 return false;
47739             }
47740             if (this.dragGroup) {
47741                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47742                     while (targets.indexOf(this.dropZone) > -1) {
47743                             targets.remove(this.dropZone);
47744                                 }
47745                     if (targets.length == 1) {
47746                                         this.dragZone.cachedTarget = null;
47747                         var el = Roo.get(targets[0].getEl());
47748                         var box = el.getBox(true);
47749                         targets[0].onNodeDrop(el.dom, {
47750                                 target: el.dom,
47751                                 xy: [box.x, box.y + box.height - 1]
47752                         }, null, this.getDragData(e));
47753                     }
47754                 }
47755         }
47756     },
47757     
47758     handleSelection: function(e) {
47759                 this.dragZone.cachedTarget = null;
47760         var item = this.findItemFromChild(e.getTarget());
47761         if (!item) {
47762                 this.clearSelections(true);
47763                 return;
47764         }
47765                 if (item && (this.multiSelect || this.singleSelect)){
47766                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47767                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47768                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47769                                 this.unselect(item);
47770                         } else {
47771                                 this.select(item, this.multiSelect && e.ctrlKey);
47772                                 this.lastSelection = item;
47773                         }
47774                 }
47775     },
47776
47777     onItemClick : function(item, index, e){
47778                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47779                         return false;
47780                 }
47781                 return true;
47782     },
47783
47784     unselect : function(nodeInfo, suppressEvent){
47785                 var node = this.getNode(nodeInfo);
47786                 if(node && this.isSelected(node)){
47787                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47788                                 Roo.fly(node).removeClass(this.selectedClass);
47789                                 this.selections.remove(node);
47790                                 if(!suppressEvent){
47791                                         this.fireEvent("selectionchange", this, this.selections);
47792                                 }
47793                         }
47794                 }
47795     }
47796 });
47797 /*
47798  * Based on:
47799  * Ext JS Library 1.1.1
47800  * Copyright(c) 2006-2007, Ext JS, LLC.
47801  *
47802  * Originally Released Under LGPL - original licence link has changed is not relivant.
47803  *
47804  * Fork - LGPL
47805  * <script type="text/javascript">
47806  */
47807  
47808 /**
47809  * @class Roo.LayoutManager
47810  * @extends Roo.util.Observable
47811  * Base class for layout managers.
47812  */
47813 Roo.LayoutManager = function(container, config){
47814     Roo.LayoutManager.superclass.constructor.call(this);
47815     this.el = Roo.get(container);
47816     // ie scrollbar fix
47817     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47818         document.body.scroll = "no";
47819     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47820         this.el.position('relative');
47821     }
47822     this.id = this.el.id;
47823     this.el.addClass("x-layout-container");
47824     /** false to disable window resize monitoring @type Boolean */
47825     this.monitorWindowResize = true;
47826     this.regions = {};
47827     this.addEvents({
47828         /**
47829          * @event layout
47830          * Fires when a layout is performed. 
47831          * @param {Roo.LayoutManager} this
47832          */
47833         "layout" : true,
47834         /**
47835          * @event regionresized
47836          * Fires when the user resizes a region. 
47837          * @param {Roo.LayoutRegion} region The resized region
47838          * @param {Number} newSize The new size (width for east/west, height for north/south)
47839          */
47840         "regionresized" : true,
47841         /**
47842          * @event regioncollapsed
47843          * Fires when a region is collapsed. 
47844          * @param {Roo.LayoutRegion} region The collapsed region
47845          */
47846         "regioncollapsed" : true,
47847         /**
47848          * @event regionexpanded
47849          * Fires when a region is expanded.  
47850          * @param {Roo.LayoutRegion} region The expanded region
47851          */
47852         "regionexpanded" : true
47853     });
47854     this.updating = false;
47855     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47856 };
47857
47858 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47859     /**
47860      * Returns true if this layout is currently being updated
47861      * @return {Boolean}
47862      */
47863     isUpdating : function(){
47864         return this.updating; 
47865     },
47866     
47867     /**
47868      * Suspend the LayoutManager from doing auto-layouts while
47869      * making multiple add or remove calls
47870      */
47871     beginUpdate : function(){
47872         this.updating = true;    
47873     },
47874     
47875     /**
47876      * Restore auto-layouts and optionally disable the manager from performing a layout
47877      * @param {Boolean} noLayout true to disable a layout update 
47878      */
47879     endUpdate : function(noLayout){
47880         this.updating = false;
47881         if(!noLayout){
47882             this.layout();
47883         }    
47884     },
47885     
47886     layout: function(){
47887         
47888     },
47889     
47890     onRegionResized : function(region, newSize){
47891         this.fireEvent("regionresized", region, newSize);
47892         this.layout();
47893     },
47894     
47895     onRegionCollapsed : function(region){
47896         this.fireEvent("regioncollapsed", region);
47897     },
47898     
47899     onRegionExpanded : function(region){
47900         this.fireEvent("regionexpanded", region);
47901     },
47902         
47903     /**
47904      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47905      * performs box-model adjustments.
47906      * @return {Object} The size as an object {width: (the width), height: (the height)}
47907      */
47908     getViewSize : function(){
47909         var size;
47910         if(this.el.dom != document.body){
47911             size = this.el.getSize();
47912         }else{
47913             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47914         }
47915         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47916         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47917         return size;
47918     },
47919     
47920     /**
47921      * Returns the Element this layout is bound to.
47922      * @return {Roo.Element}
47923      */
47924     getEl : function(){
47925         return this.el;
47926     },
47927     
47928     /**
47929      * Returns the specified region.
47930      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47931      * @return {Roo.LayoutRegion}
47932      */
47933     getRegion : function(target){
47934         return this.regions[target.toLowerCase()];
47935     },
47936     
47937     onWindowResize : function(){
47938         if(this.monitorWindowResize){
47939             this.layout();
47940         }
47941     }
47942 });/*
47943  * Based on:
47944  * Ext JS Library 1.1.1
47945  * Copyright(c) 2006-2007, Ext JS, LLC.
47946  *
47947  * Originally Released Under LGPL - original licence link has changed is not relivant.
47948  *
47949  * Fork - LGPL
47950  * <script type="text/javascript">
47951  */
47952 /**
47953  * @class Roo.BorderLayout
47954  * @extends Roo.LayoutManager
47955  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47956  * please see: <br><br>
47957  * <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>
47958  * <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>
47959  * Example:
47960  <pre><code>
47961  var layout = new Roo.BorderLayout(document.body, {
47962     north: {
47963         initialSize: 25,
47964         titlebar: false
47965     },
47966     west: {
47967         split:true,
47968         initialSize: 200,
47969         minSize: 175,
47970         maxSize: 400,
47971         titlebar: true,
47972         collapsible: true
47973     },
47974     east: {
47975         split:true,
47976         initialSize: 202,
47977         minSize: 175,
47978         maxSize: 400,
47979         titlebar: true,
47980         collapsible: true
47981     },
47982     south: {
47983         split:true,
47984         initialSize: 100,
47985         minSize: 100,
47986         maxSize: 200,
47987         titlebar: true,
47988         collapsible: true
47989     },
47990     center: {
47991         titlebar: true,
47992         autoScroll:true,
47993         resizeTabs: true,
47994         minTabWidth: 50,
47995         preferredTabWidth: 150
47996     }
47997 });
47998
47999 // shorthand
48000 var CP = Roo.ContentPanel;
48001
48002 layout.beginUpdate();
48003 layout.add("north", new CP("north", "North"));
48004 layout.add("south", new CP("south", {title: "South", closable: true}));
48005 layout.add("west", new CP("west", {title: "West"}));
48006 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48007 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48008 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48009 layout.getRegion("center").showPanel("center1");
48010 layout.endUpdate();
48011 </code></pre>
48012
48013 <b>The container the layout is rendered into can be either the body element or any other element.
48014 If it is not the body element, the container needs to either be an absolute positioned element,
48015 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48016 the container size if it is not the body element.</b>
48017
48018 * @constructor
48019 * Create a new BorderLayout
48020 * @param {String/HTMLElement/Element} container The container this layout is bound to
48021 * @param {Object} config Configuration options
48022  */
48023 Roo.BorderLayout = function(container, config){
48024     config = config || {};
48025     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48026     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48027     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48028         var target = this.factory.validRegions[i];
48029         if(config[target]){
48030             this.addRegion(target, config[target]);
48031         }
48032     }
48033 };
48034
48035 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48036     /**
48037      * Creates and adds a new region if it doesn't already exist.
48038      * @param {String} target The target region key (north, south, east, west or center).
48039      * @param {Object} config The regions config object
48040      * @return {BorderLayoutRegion} The new region
48041      */
48042     addRegion : function(target, config){
48043         if(!this.regions[target]){
48044             var r = this.factory.create(target, this, config);
48045             this.bindRegion(target, r);
48046         }
48047         return this.regions[target];
48048     },
48049
48050     // private (kinda)
48051     bindRegion : function(name, r){
48052         this.regions[name] = r;
48053         r.on("visibilitychange", this.layout, this);
48054         r.on("paneladded", this.layout, this);
48055         r.on("panelremoved", this.layout, this);
48056         r.on("invalidated", this.layout, this);
48057         r.on("resized", this.onRegionResized, this);
48058         r.on("collapsed", this.onRegionCollapsed, this);
48059         r.on("expanded", this.onRegionExpanded, this);
48060     },
48061
48062     /**
48063      * Performs a layout update.
48064      */
48065     layout : function(){
48066         if(this.updating) return;
48067         var size = this.getViewSize();
48068         var w = size.width;
48069         var h = size.height;
48070         var centerW = w;
48071         var centerH = h;
48072         var centerY = 0;
48073         var centerX = 0;
48074         //var x = 0, y = 0;
48075
48076         var rs = this.regions;
48077         var north = rs["north"];
48078         var south = rs["south"]; 
48079         var west = rs["west"];
48080         var east = rs["east"];
48081         var center = rs["center"];
48082         //if(this.hideOnLayout){ // not supported anymore
48083             //c.el.setStyle("display", "none");
48084         //}
48085         if(north && north.isVisible()){
48086             var b = north.getBox();
48087             var m = north.getMargins();
48088             b.width = w - (m.left+m.right);
48089             b.x = m.left;
48090             b.y = m.top;
48091             centerY = b.height + b.y + m.bottom;
48092             centerH -= centerY;
48093             north.updateBox(this.safeBox(b));
48094         }
48095         if(south && south.isVisible()){
48096             var b = south.getBox();
48097             var m = south.getMargins();
48098             b.width = w - (m.left+m.right);
48099             b.x = m.left;
48100             var totalHeight = (b.height + m.top + m.bottom);
48101             b.y = h - totalHeight + m.top;
48102             centerH -= totalHeight;
48103             south.updateBox(this.safeBox(b));
48104         }
48105         if(west && west.isVisible()){
48106             var b = west.getBox();
48107             var m = west.getMargins();
48108             b.height = centerH - (m.top+m.bottom);
48109             b.x = m.left;
48110             b.y = centerY + m.top;
48111             var totalWidth = (b.width + m.left + m.right);
48112             centerX += totalWidth;
48113             centerW -= totalWidth;
48114             west.updateBox(this.safeBox(b));
48115         }
48116         if(east && east.isVisible()){
48117             var b = east.getBox();
48118             var m = east.getMargins();
48119             b.height = centerH - (m.top+m.bottom);
48120             var totalWidth = (b.width + m.left + m.right);
48121             b.x = w - totalWidth + m.left;
48122             b.y = centerY + m.top;
48123             centerW -= totalWidth;
48124             east.updateBox(this.safeBox(b));
48125         }
48126         if(center){
48127             var m = center.getMargins();
48128             var centerBox = {
48129                 x: centerX + m.left,
48130                 y: centerY + m.top,
48131                 width: centerW - (m.left+m.right),
48132                 height: centerH - (m.top+m.bottom)
48133             };
48134             //if(this.hideOnLayout){
48135                 //center.el.setStyle("display", "block");
48136             //}
48137             center.updateBox(this.safeBox(centerBox));
48138         }
48139         this.el.repaint();
48140         this.fireEvent("layout", this);
48141     },
48142
48143     // private
48144     safeBox : function(box){
48145         box.width = Math.max(0, box.width);
48146         box.height = Math.max(0, box.height);
48147         return box;
48148     },
48149
48150     /**
48151      * Adds a ContentPanel (or subclass) to this layout.
48152      * @param {String} target The target region key (north, south, east, west or center).
48153      * @param {Roo.ContentPanel} panel The panel to add
48154      * @return {Roo.ContentPanel} The added panel
48155      */
48156     add : function(target, panel){
48157          
48158         target = target.toLowerCase();
48159         return this.regions[target].add(panel);
48160     },
48161
48162     /**
48163      * Remove a ContentPanel (or subclass) to this layout.
48164      * @param {String} target The target region key (north, south, east, west or center).
48165      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48166      * @return {Roo.ContentPanel} The removed panel
48167      */
48168     remove : function(target, panel){
48169         target = target.toLowerCase();
48170         return this.regions[target].remove(panel);
48171     },
48172
48173     /**
48174      * Searches all regions for a panel with the specified id
48175      * @param {String} panelId
48176      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48177      */
48178     findPanel : function(panelId){
48179         var rs = this.regions;
48180         for(var target in rs){
48181             if(typeof rs[target] != "function"){
48182                 var p = rs[target].getPanel(panelId);
48183                 if(p){
48184                     return p;
48185                 }
48186             }
48187         }
48188         return null;
48189     },
48190
48191     /**
48192      * Searches all regions for a panel with the specified id and activates (shows) it.
48193      * @param {String/ContentPanel} panelId The panels id or the panel itself
48194      * @return {Roo.ContentPanel} The shown panel or null
48195      */
48196     showPanel : function(panelId) {
48197       var rs = this.regions;
48198       for(var target in rs){
48199          var r = rs[target];
48200          if(typeof r != "function"){
48201             if(r.hasPanel(panelId)){
48202                return r.showPanel(panelId);
48203             }
48204          }
48205       }
48206       return null;
48207    },
48208
48209    /**
48210      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48211      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48212      */
48213     restoreState : function(provider){
48214         if(!provider){
48215             provider = Roo.state.Manager;
48216         }
48217         var sm = new Roo.LayoutStateManager();
48218         sm.init(this, provider);
48219     },
48220
48221     /**
48222      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48223      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48224      * a valid ContentPanel config object.  Example:
48225      * <pre><code>
48226 // Create the main layout
48227 var layout = new Roo.BorderLayout('main-ct', {
48228     west: {
48229         split:true,
48230         minSize: 175,
48231         titlebar: true
48232     },
48233     center: {
48234         title:'Components'
48235     }
48236 }, 'main-ct');
48237
48238 // Create and add multiple ContentPanels at once via configs
48239 layout.batchAdd({
48240    west: {
48241        id: 'source-files',
48242        autoCreate:true,
48243        title:'Ext Source Files',
48244        autoScroll:true,
48245        fitToFrame:true
48246    },
48247    center : {
48248        el: cview,
48249        autoScroll:true,
48250        fitToFrame:true,
48251        toolbar: tb,
48252        resizeEl:'cbody'
48253    }
48254 });
48255 </code></pre>
48256      * @param {Object} regions An object containing ContentPanel configs by region name
48257      */
48258     batchAdd : function(regions){
48259         this.beginUpdate();
48260         for(var rname in regions){
48261             var lr = this.regions[rname];
48262             if(lr){
48263                 this.addTypedPanels(lr, regions[rname]);
48264             }
48265         }
48266         this.endUpdate();
48267     },
48268
48269     // private
48270     addTypedPanels : function(lr, ps){
48271         if(typeof ps == 'string'){
48272             lr.add(new Roo.ContentPanel(ps));
48273         }
48274         else if(ps instanceof Array){
48275             for(var i =0, len = ps.length; i < len; i++){
48276                 this.addTypedPanels(lr, ps[i]);
48277             }
48278         }
48279         else if(!ps.events){ // raw config?
48280             var el = ps.el;
48281             delete ps.el; // prevent conflict
48282             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48283         }
48284         else {  // panel object assumed!
48285             lr.add(ps);
48286         }
48287     },
48288     /**
48289      * Adds a xtype elements to the layout.
48290      * <pre><code>
48291
48292 layout.addxtype({
48293        xtype : 'ContentPanel',
48294        region: 'west',
48295        items: [ .... ]
48296    }
48297 );
48298
48299 layout.addxtype({
48300         xtype : 'NestedLayoutPanel',
48301         region: 'west',
48302         layout: {
48303            center: { },
48304            west: { }   
48305         },
48306         items : [ ... list of content panels or nested layout panels.. ]
48307    }
48308 );
48309 </code></pre>
48310      * @param {Object} cfg Xtype definition of item to add.
48311      */
48312     addxtype : function(cfg)
48313     {
48314         // basically accepts a pannel...
48315         // can accept a layout region..!?!?
48316         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48317         
48318         if (!cfg.xtype.match(/Panel$/)) {
48319             return false;
48320         }
48321         var ret = false;
48322         
48323         if (typeof(cfg.region) == 'undefined') {
48324             Roo.log("Failed to add Panel, region was not set");
48325             Roo.log(cfg);
48326             return false;
48327         }
48328         var region = cfg.region;
48329         delete cfg.region;
48330         
48331           
48332         var xitems = [];
48333         if (cfg.items) {
48334             xitems = cfg.items;
48335             delete cfg.items;
48336         }
48337         var nb = false;
48338         
48339         switch(cfg.xtype) 
48340         {
48341             case 'ContentPanel':  // ContentPanel (el, cfg)
48342             case 'ScrollPanel':  // ContentPanel (el, cfg)
48343             case 'ViewPanel': 
48344                 if(cfg.autoCreate) {
48345                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48346                 } else {
48347                     var el = this.el.createChild();
48348                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48349                 }
48350                 
48351                 this.add(region, ret);
48352                 break;
48353             
48354             
48355             case 'TreePanel': // our new panel!
48356                 cfg.el = this.el.createChild();
48357                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48358                 this.add(region, ret);
48359                 break;
48360             
48361             case 'NestedLayoutPanel': 
48362                 // create a new Layout (which is  a Border Layout...
48363                 var el = this.el.createChild();
48364                 var clayout = cfg.layout;
48365                 delete cfg.layout;
48366                 clayout.items   = clayout.items  || [];
48367                 // replace this exitems with the clayout ones..
48368                 xitems = clayout.items;
48369                  
48370                 
48371                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48372                     cfg.background = false;
48373                 }
48374                 var layout = new Roo.BorderLayout(el, clayout);
48375                 
48376                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48377                 //console.log('adding nested layout panel '  + cfg.toSource());
48378                 this.add(region, ret);
48379                 nb = {}; /// find first...
48380                 break;
48381                 
48382             case 'GridPanel': 
48383             
48384                 // needs grid and region
48385                 
48386                 //var el = this.getRegion(region).el.createChild();
48387                 var el = this.el.createChild();
48388                 // create the grid first...
48389                 
48390                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48391                 delete cfg.grid;
48392                 if (region == 'center' && this.active ) {
48393                     cfg.background = false;
48394                 }
48395                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48396                 
48397                 this.add(region, ret);
48398                 if (cfg.background) {
48399                     ret.on('activate', function(gp) {
48400                         if (!gp.grid.rendered) {
48401                             gp.grid.render();
48402                         }
48403                     });
48404                 } else {
48405                     grid.render();
48406                 }
48407                 break;
48408            
48409            
48410            
48411                 
48412                 
48413                 
48414             default: 
48415                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48416                 return null;
48417              // GridPanel (grid, cfg)
48418             
48419         }
48420         this.beginUpdate();
48421         // add children..
48422         var region = '';
48423         var abn = {};
48424         Roo.each(xitems, function(i)  {
48425             region = nb && i.region ? i.region : false;
48426             
48427             var add = ret.addxtype(i);
48428            
48429             if (region) {
48430                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48431                 if (!i.background) {
48432                     abn[region] = nb[region] ;
48433                 }
48434             }
48435             
48436         });
48437         this.endUpdate();
48438
48439         // make the last non-background panel active..
48440         //if (nb) { Roo.log(abn); }
48441         if (nb) {
48442             
48443             for(var r in abn) {
48444                 region = this.getRegion(r);
48445                 if (region) {
48446                     // tried using nb[r], but it does not work..
48447                      
48448                     region.showPanel(abn[r]);
48449                    
48450                 }
48451             }
48452         }
48453         return ret;
48454         
48455     }
48456 });
48457
48458 /**
48459  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48460  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48461  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48462  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48463  * <pre><code>
48464 // shorthand
48465 var CP = Roo.ContentPanel;
48466
48467 var layout = Roo.BorderLayout.create({
48468     north: {
48469         initialSize: 25,
48470         titlebar: false,
48471         panels: [new CP("north", "North")]
48472     },
48473     west: {
48474         split:true,
48475         initialSize: 200,
48476         minSize: 175,
48477         maxSize: 400,
48478         titlebar: true,
48479         collapsible: true,
48480         panels: [new CP("west", {title: "West"})]
48481     },
48482     east: {
48483         split:true,
48484         initialSize: 202,
48485         minSize: 175,
48486         maxSize: 400,
48487         titlebar: true,
48488         collapsible: true,
48489         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48490     },
48491     south: {
48492         split:true,
48493         initialSize: 100,
48494         minSize: 100,
48495         maxSize: 200,
48496         titlebar: true,
48497         collapsible: true,
48498         panels: [new CP("south", {title: "South", closable: true})]
48499     },
48500     center: {
48501         titlebar: true,
48502         autoScroll:true,
48503         resizeTabs: true,
48504         minTabWidth: 50,
48505         preferredTabWidth: 150,
48506         panels: [
48507             new CP("center1", {title: "Close Me", closable: true}),
48508             new CP("center2", {title: "Center Panel", closable: false})
48509         ]
48510     }
48511 }, document.body);
48512
48513 layout.getRegion("center").showPanel("center1");
48514 </code></pre>
48515  * @param config
48516  * @param targetEl
48517  */
48518 Roo.BorderLayout.create = function(config, targetEl){
48519     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48520     layout.beginUpdate();
48521     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48522     for(var j = 0, jlen = regions.length; j < jlen; j++){
48523         var lr = regions[j];
48524         if(layout.regions[lr] && config[lr].panels){
48525             var r = layout.regions[lr];
48526             var ps = config[lr].panels;
48527             layout.addTypedPanels(r, ps);
48528         }
48529     }
48530     layout.endUpdate();
48531     return layout;
48532 };
48533
48534 // private
48535 Roo.BorderLayout.RegionFactory = {
48536     // private
48537     validRegions : ["north","south","east","west","center"],
48538
48539     // private
48540     create : function(target, mgr, config){
48541         target = target.toLowerCase();
48542         if(config.lightweight || config.basic){
48543             return new Roo.BasicLayoutRegion(mgr, config, target);
48544         }
48545         switch(target){
48546             case "north":
48547                 return new Roo.NorthLayoutRegion(mgr, config);
48548             case "south":
48549                 return new Roo.SouthLayoutRegion(mgr, config);
48550             case "east":
48551                 return new Roo.EastLayoutRegion(mgr, config);
48552             case "west":
48553                 return new Roo.WestLayoutRegion(mgr, config);
48554             case "center":
48555                 return new Roo.CenterLayoutRegion(mgr, config);
48556         }
48557         throw 'Layout region "'+target+'" not supported.';
48558     }
48559 };/*
48560  * Based on:
48561  * Ext JS Library 1.1.1
48562  * Copyright(c) 2006-2007, Ext JS, LLC.
48563  *
48564  * Originally Released Under LGPL - original licence link has changed is not relivant.
48565  *
48566  * Fork - LGPL
48567  * <script type="text/javascript">
48568  */
48569  
48570 /**
48571  * @class Roo.BasicLayoutRegion
48572  * @extends Roo.util.Observable
48573  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48574  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48575  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48576  */
48577 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48578     this.mgr = mgr;
48579     this.position  = pos;
48580     this.events = {
48581         /**
48582          * @scope Roo.BasicLayoutRegion
48583          */
48584         
48585         /**
48586          * @event beforeremove
48587          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48588          * @param {Roo.LayoutRegion} this
48589          * @param {Roo.ContentPanel} panel The panel
48590          * @param {Object} e The cancel event object
48591          */
48592         "beforeremove" : true,
48593         /**
48594          * @event invalidated
48595          * Fires when the layout for this region is changed.
48596          * @param {Roo.LayoutRegion} this
48597          */
48598         "invalidated" : true,
48599         /**
48600          * @event visibilitychange
48601          * Fires when this region is shown or hidden 
48602          * @param {Roo.LayoutRegion} this
48603          * @param {Boolean} visibility true or false
48604          */
48605         "visibilitychange" : true,
48606         /**
48607          * @event paneladded
48608          * Fires when a panel is added. 
48609          * @param {Roo.LayoutRegion} this
48610          * @param {Roo.ContentPanel} panel The panel
48611          */
48612         "paneladded" : true,
48613         /**
48614          * @event panelremoved
48615          * Fires when a panel is removed. 
48616          * @param {Roo.LayoutRegion} this
48617          * @param {Roo.ContentPanel} panel The panel
48618          */
48619         "panelremoved" : true,
48620         /**
48621          * @event collapsed
48622          * Fires when this region is collapsed.
48623          * @param {Roo.LayoutRegion} this
48624          */
48625         "collapsed" : true,
48626         /**
48627          * @event expanded
48628          * Fires when this region is expanded.
48629          * @param {Roo.LayoutRegion} this
48630          */
48631         "expanded" : true,
48632         /**
48633          * @event slideshow
48634          * Fires when this region is slid into view.
48635          * @param {Roo.LayoutRegion} this
48636          */
48637         "slideshow" : true,
48638         /**
48639          * @event slidehide
48640          * Fires when this region slides out of view. 
48641          * @param {Roo.LayoutRegion} this
48642          */
48643         "slidehide" : true,
48644         /**
48645          * @event panelactivated
48646          * Fires when a panel is activated. 
48647          * @param {Roo.LayoutRegion} this
48648          * @param {Roo.ContentPanel} panel The activated panel
48649          */
48650         "panelactivated" : true,
48651         /**
48652          * @event resized
48653          * Fires when the user resizes this region. 
48654          * @param {Roo.LayoutRegion} this
48655          * @param {Number} newSize The new size (width for east/west, height for north/south)
48656          */
48657         "resized" : true
48658     };
48659     /** A collection of panels in this region. @type Roo.util.MixedCollection */
48660     this.panels = new Roo.util.MixedCollection();
48661     this.panels.getKey = this.getPanelId.createDelegate(this);
48662     this.box = null;
48663     this.activePanel = null;
48664     // ensure listeners are added...
48665     
48666     if (config.listeners || config.events) {
48667         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
48668             listeners : config.listeners || {},
48669             events : config.events || {}
48670         });
48671     }
48672     
48673     if(skipConfig !== true){
48674         this.applyConfig(config);
48675     }
48676 };
48677
48678 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
48679     getPanelId : function(p){
48680         return p.getId();
48681     },
48682     
48683     applyConfig : function(config){
48684         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48685         this.config = config;
48686         
48687     },
48688     
48689     /**
48690      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
48691      * the width, for horizontal (north, south) the height.
48692      * @param {Number} newSize The new width or height
48693      */
48694     resizeTo : function(newSize){
48695         var el = this.el ? this.el :
48696                  (this.activePanel ? this.activePanel.getEl() : null);
48697         if(el){
48698             switch(this.position){
48699                 case "east":
48700                 case "west":
48701                     el.setWidth(newSize);
48702                     this.fireEvent("resized", this, newSize);
48703                 break;
48704                 case "north":
48705                 case "south":
48706                     el.setHeight(newSize);
48707                     this.fireEvent("resized", this, newSize);
48708                 break;                
48709             }
48710         }
48711     },
48712     
48713     getBox : function(){
48714         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48715     },
48716     
48717     getMargins : function(){
48718         return this.margins;
48719     },
48720     
48721     updateBox : function(box){
48722         this.box = box;
48723         var el = this.activePanel.getEl();
48724         el.dom.style.left = box.x + "px";
48725         el.dom.style.top = box.y + "px";
48726         this.activePanel.setSize(box.width, box.height);
48727     },
48728     
48729     /**
48730      * Returns the container element for this region.
48731      * @return {Roo.Element}
48732      */
48733     getEl : function(){
48734         return this.activePanel;
48735     },
48736     
48737     /**
48738      * Returns true if this region is currently visible.
48739      * @return {Boolean}
48740      */
48741     isVisible : function(){
48742         return this.activePanel ? true : false;
48743     },
48744     
48745     setActivePanel : function(panel){
48746         panel = this.getPanel(panel);
48747         if(this.activePanel && this.activePanel != panel){
48748             this.activePanel.setActiveState(false);
48749             this.activePanel.getEl().setLeftTop(-10000,-10000);
48750         }
48751         this.activePanel = panel;
48752         panel.setActiveState(true);
48753         if(this.box){
48754             panel.setSize(this.box.width, this.box.height);
48755         }
48756         this.fireEvent("panelactivated", this, panel);
48757         this.fireEvent("invalidated");
48758     },
48759     
48760     /**
48761      * Show the specified panel.
48762      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48763      * @return {Roo.ContentPanel} The shown panel or null
48764      */
48765     showPanel : function(panel){
48766         if(panel = this.getPanel(panel)){
48767             this.setActivePanel(panel);
48768         }
48769         return panel;
48770     },
48771     
48772     /**
48773      * Get the active panel for this region.
48774      * @return {Roo.ContentPanel} The active panel or null
48775      */
48776     getActivePanel : function(){
48777         return this.activePanel;
48778     },
48779     
48780     /**
48781      * Add the passed ContentPanel(s)
48782      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48783      * @return {Roo.ContentPanel} The panel added (if only one was added)
48784      */
48785     add : function(panel){
48786         if(arguments.length > 1){
48787             for(var i = 0, len = arguments.length; i < len; i++) {
48788                 this.add(arguments[i]);
48789             }
48790             return null;
48791         }
48792         if(this.hasPanel(panel)){
48793             this.showPanel(panel);
48794             return panel;
48795         }
48796         var el = panel.getEl();
48797         if(el.dom.parentNode != this.mgr.el.dom){
48798             this.mgr.el.dom.appendChild(el.dom);
48799         }
48800         if(panel.setRegion){
48801             panel.setRegion(this);
48802         }
48803         this.panels.add(panel);
48804         el.setStyle("position", "absolute");
48805         if(!panel.background){
48806             this.setActivePanel(panel);
48807             if(this.config.initialSize && this.panels.getCount()==1){
48808                 this.resizeTo(this.config.initialSize);
48809             }
48810         }
48811         this.fireEvent("paneladded", this, panel);
48812         return panel;
48813     },
48814     
48815     /**
48816      * Returns true if the panel is in this region.
48817      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48818      * @return {Boolean}
48819      */
48820     hasPanel : function(panel){
48821         if(typeof panel == "object"){ // must be panel obj
48822             panel = panel.getId();
48823         }
48824         return this.getPanel(panel) ? true : false;
48825     },
48826     
48827     /**
48828      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48829      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48830      * @param {Boolean} preservePanel Overrides the config preservePanel option
48831      * @return {Roo.ContentPanel} The panel that was removed
48832      */
48833     remove : function(panel, preservePanel){
48834         panel = this.getPanel(panel);
48835         if(!panel){
48836             return null;
48837         }
48838         var e = {};
48839         this.fireEvent("beforeremove", this, panel, e);
48840         if(e.cancel === true){
48841             return null;
48842         }
48843         var panelId = panel.getId();
48844         this.panels.removeKey(panelId);
48845         return panel;
48846     },
48847     
48848     /**
48849      * Returns the panel specified or null if it's not in this region.
48850      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48851      * @return {Roo.ContentPanel}
48852      */
48853     getPanel : function(id){
48854         if(typeof id == "object"){ // must be panel obj
48855             return id;
48856         }
48857         return this.panels.get(id);
48858     },
48859     
48860     /**
48861      * Returns this regions position (north/south/east/west/center).
48862      * @return {String} 
48863      */
48864     getPosition: function(){
48865         return this.position;    
48866     }
48867 });/*
48868  * Based on:
48869  * Ext JS Library 1.1.1
48870  * Copyright(c) 2006-2007, Ext JS, LLC.
48871  *
48872  * Originally Released Under LGPL - original licence link has changed is not relivant.
48873  *
48874  * Fork - LGPL
48875  * <script type="text/javascript">
48876  */
48877  
48878 /**
48879  * @class Roo.LayoutRegion
48880  * @extends Roo.BasicLayoutRegion
48881  * This class represents a region in a layout manager.
48882  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48883  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48884  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48885  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48886  * @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})
48887  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48888  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48889  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48890  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48891  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48892  * @cfg {String}    title           The title for the region (overrides panel titles)
48893  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48894  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48895  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48896  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48897  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48898  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48899  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48900  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48901  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48902  * @cfg {Boolean}   showPin         True to show a pin button
48903  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48904  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48905  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48906  * @cfg {Number}    width           For East/West panels
48907  * @cfg {Number}    height          For North/South panels
48908  * @cfg {Boolean}   split           To show the splitter
48909  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48910  */
48911 Roo.LayoutRegion = function(mgr, config, pos){
48912     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48913     var dh = Roo.DomHelper;
48914     /** This region's container element 
48915     * @type Roo.Element */
48916     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48917     /** This region's title element 
48918     * @type Roo.Element */
48919
48920     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48921         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48922         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48923     ]}, true);
48924     this.titleEl.enableDisplayMode();
48925     /** This region's title text element 
48926     * @type HTMLElement */
48927     this.titleTextEl = this.titleEl.dom.firstChild;
48928     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48929     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48930     this.closeBtn.enableDisplayMode();
48931     this.closeBtn.on("click", this.closeClicked, this);
48932     this.closeBtn.hide();
48933
48934     this.createBody(config);
48935     this.visible = true;
48936     this.collapsed = false;
48937
48938     if(config.hideWhenEmpty){
48939         this.hide();
48940         this.on("paneladded", this.validateVisibility, this);
48941         this.on("panelremoved", this.validateVisibility, this);
48942     }
48943     this.applyConfig(config);
48944 };
48945
48946 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48947
48948     createBody : function(){
48949         /** This region's body element 
48950         * @type Roo.Element */
48951         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48952     },
48953
48954     applyConfig : function(c){
48955         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48956             var dh = Roo.DomHelper;
48957             if(c.titlebar !== false){
48958                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48959                 this.collapseBtn.on("click", this.collapse, this);
48960                 this.collapseBtn.enableDisplayMode();
48961
48962                 if(c.showPin === true || this.showPin){
48963                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48964                     this.stickBtn.enableDisplayMode();
48965                     this.stickBtn.on("click", this.expand, this);
48966                     this.stickBtn.hide();
48967                 }
48968             }
48969             /** This region's collapsed element
48970             * @type Roo.Element */
48971             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48972                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48973             ]}, true);
48974             if(c.floatable !== false){
48975                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48976                this.collapsedEl.on("click", this.collapseClick, this);
48977             }
48978
48979             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48980                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48981                    id: "message", unselectable: "on", style:{"float":"left"}});
48982                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
48983              }
48984             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
48985             this.expandBtn.on("click", this.expand, this);
48986         }
48987         if(this.collapseBtn){
48988             this.collapseBtn.setVisible(c.collapsible == true);
48989         }
48990         this.cmargins = c.cmargins || this.cmargins ||
48991                          (this.position == "west" || this.position == "east" ?
48992                              {top: 0, left: 2, right:2, bottom: 0} :
48993                              {top: 2, left: 0, right:0, bottom: 2});
48994         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48995         this.bottomTabs = c.tabPosition != "top";
48996         this.autoScroll = c.autoScroll || false;
48997         if(this.autoScroll){
48998             this.bodyEl.setStyle("overflow", "auto");
48999         }else{
49000             this.bodyEl.setStyle("overflow", "hidden");
49001         }
49002         //if(c.titlebar !== false){
49003             if((!c.titlebar && !c.title) || c.titlebar === false){
49004                 this.titleEl.hide();
49005             }else{
49006                 this.titleEl.show();
49007                 if(c.title){
49008                     this.titleTextEl.innerHTML = c.title;
49009                 }
49010             }
49011         //}
49012         this.duration = c.duration || .30;
49013         this.slideDuration = c.slideDuration || .45;
49014         this.config = c;
49015         if(c.collapsed){
49016             this.collapse(true);
49017         }
49018         if(c.hidden){
49019             this.hide();
49020         }
49021     },
49022     /**
49023      * Returns true if this region is currently visible.
49024      * @return {Boolean}
49025      */
49026     isVisible : function(){
49027         return this.visible;
49028     },
49029
49030     /**
49031      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49032      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49033      */
49034     setCollapsedTitle : function(title){
49035         title = title || "&#160;";
49036         if(this.collapsedTitleTextEl){
49037             this.collapsedTitleTextEl.innerHTML = title;
49038         }
49039     },
49040
49041     getBox : function(){
49042         var b;
49043         if(!this.collapsed){
49044             b = this.el.getBox(false, true);
49045         }else{
49046             b = this.collapsedEl.getBox(false, true);
49047         }
49048         return b;
49049     },
49050
49051     getMargins : function(){
49052         return this.collapsed ? this.cmargins : this.margins;
49053     },
49054
49055     highlight : function(){
49056         this.el.addClass("x-layout-panel-dragover");
49057     },
49058
49059     unhighlight : function(){
49060         this.el.removeClass("x-layout-panel-dragover");
49061     },
49062
49063     updateBox : function(box){
49064         this.box = box;
49065         if(!this.collapsed){
49066             this.el.dom.style.left = box.x + "px";
49067             this.el.dom.style.top = box.y + "px";
49068             this.updateBody(box.width, box.height);
49069         }else{
49070             this.collapsedEl.dom.style.left = box.x + "px";
49071             this.collapsedEl.dom.style.top = box.y + "px";
49072             this.collapsedEl.setSize(box.width, box.height);
49073         }
49074         if(this.tabs){
49075             this.tabs.autoSizeTabs();
49076         }
49077     },
49078
49079     updateBody : function(w, h){
49080         if(w !== null){
49081             this.el.setWidth(w);
49082             w -= this.el.getBorderWidth("rl");
49083             if(this.config.adjustments){
49084                 w += this.config.adjustments[0];
49085             }
49086         }
49087         if(h !== null){
49088             this.el.setHeight(h);
49089             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49090             h -= this.el.getBorderWidth("tb");
49091             if(this.config.adjustments){
49092                 h += this.config.adjustments[1];
49093             }
49094             this.bodyEl.setHeight(h);
49095             if(this.tabs){
49096                 h = this.tabs.syncHeight(h);
49097             }
49098         }
49099         if(this.panelSize){
49100             w = w !== null ? w : this.panelSize.width;
49101             h = h !== null ? h : this.panelSize.height;
49102         }
49103         if(this.activePanel){
49104             var el = this.activePanel.getEl();
49105             w = w !== null ? w : el.getWidth();
49106             h = h !== null ? h : el.getHeight();
49107             this.panelSize = {width: w, height: h};
49108             this.activePanel.setSize(w, h);
49109         }
49110         if(Roo.isIE && this.tabs){
49111             this.tabs.el.repaint();
49112         }
49113     },
49114
49115     /**
49116      * Returns the container element for this region.
49117      * @return {Roo.Element}
49118      */
49119     getEl : function(){
49120         return this.el;
49121     },
49122
49123     /**
49124      * Hides this region.
49125      */
49126     hide : function(){
49127         if(!this.collapsed){
49128             this.el.dom.style.left = "-2000px";
49129             this.el.hide();
49130         }else{
49131             this.collapsedEl.dom.style.left = "-2000px";
49132             this.collapsedEl.hide();
49133         }
49134         this.visible = false;
49135         this.fireEvent("visibilitychange", this, false);
49136     },
49137
49138     /**
49139      * Shows this region if it was previously hidden.
49140      */
49141     show : function(){
49142         if(!this.collapsed){
49143             this.el.show();
49144         }else{
49145             this.collapsedEl.show();
49146         }
49147         this.visible = true;
49148         this.fireEvent("visibilitychange", this, true);
49149     },
49150
49151     closeClicked : function(){
49152         if(this.activePanel){
49153             this.remove(this.activePanel);
49154         }
49155     },
49156
49157     collapseClick : function(e){
49158         if(this.isSlid){
49159            e.stopPropagation();
49160            this.slideIn();
49161         }else{
49162            e.stopPropagation();
49163            this.slideOut();
49164         }
49165     },
49166
49167     /**
49168      * Collapses this region.
49169      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49170      */
49171     collapse : function(skipAnim){
49172         if(this.collapsed) return;
49173         this.collapsed = true;
49174         if(this.split){
49175             this.split.el.hide();
49176         }
49177         if(this.config.animate && skipAnim !== true){
49178             this.fireEvent("invalidated", this);
49179             this.animateCollapse();
49180         }else{
49181             this.el.setLocation(-20000,-20000);
49182             this.el.hide();
49183             this.collapsedEl.show();
49184             this.fireEvent("collapsed", this);
49185             this.fireEvent("invalidated", this);
49186         }
49187     },
49188
49189     animateCollapse : function(){
49190         // overridden
49191     },
49192
49193     /**
49194      * Expands this region if it was previously collapsed.
49195      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49196      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49197      */
49198     expand : function(e, skipAnim){
49199         if(e) e.stopPropagation();
49200         if(!this.collapsed || this.el.hasActiveFx()) return;
49201         if(this.isSlid){
49202             this.afterSlideIn();
49203             skipAnim = true;
49204         }
49205         this.collapsed = false;
49206         if(this.config.animate && skipAnim !== true){
49207             this.animateExpand();
49208         }else{
49209             this.el.show();
49210             if(this.split){
49211                 this.split.el.show();
49212             }
49213             this.collapsedEl.setLocation(-2000,-2000);
49214             this.collapsedEl.hide();
49215             this.fireEvent("invalidated", this);
49216             this.fireEvent("expanded", this);
49217         }
49218     },
49219
49220     animateExpand : function(){
49221         // overridden
49222     },
49223
49224     initTabs : function()
49225     {
49226         this.bodyEl.setStyle("overflow", "hidden");
49227         var ts = new Roo.TabPanel(
49228                 this.bodyEl.dom,
49229                 {
49230                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49231                     disableTooltips: this.config.disableTabTips,
49232                     toolbar : this.config.toolbar
49233                 }
49234         );
49235         if(this.config.hideTabs){
49236             ts.stripWrap.setDisplayed(false);
49237         }
49238         this.tabs = ts;
49239         ts.resizeTabs = this.config.resizeTabs === true;
49240         ts.minTabWidth = this.config.minTabWidth || 40;
49241         ts.maxTabWidth = this.config.maxTabWidth || 250;
49242         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49243         ts.monitorResize = false;
49244         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49245         ts.bodyEl.addClass('x-layout-tabs-body');
49246         this.panels.each(this.initPanelAsTab, this);
49247     },
49248
49249     initPanelAsTab : function(panel){
49250         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49251                     this.config.closeOnTab && panel.isClosable());
49252         if(panel.tabTip !== undefined){
49253             ti.setTooltip(panel.tabTip);
49254         }
49255         ti.on("activate", function(){
49256               this.setActivePanel(panel);
49257         }, this);
49258         if(this.config.closeOnTab){
49259             ti.on("beforeclose", function(t, e){
49260                 e.cancel = true;
49261                 this.remove(panel);
49262             }, this);
49263         }
49264         return ti;
49265     },
49266
49267     updatePanelTitle : function(panel, title){
49268         if(this.activePanel == panel){
49269             this.updateTitle(title);
49270         }
49271         if(this.tabs){
49272             var ti = this.tabs.getTab(panel.getEl().id);
49273             ti.setText(title);
49274             if(panel.tabTip !== undefined){
49275                 ti.setTooltip(panel.tabTip);
49276             }
49277         }
49278     },
49279
49280     updateTitle : function(title){
49281         if(this.titleTextEl && !this.config.title){
49282             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49283         }
49284     },
49285
49286     setActivePanel : function(panel){
49287         panel = this.getPanel(panel);
49288         if(this.activePanel && this.activePanel != panel){
49289             this.activePanel.setActiveState(false);
49290         }
49291         this.activePanel = panel;
49292         panel.setActiveState(true);
49293         if(this.panelSize){
49294             panel.setSize(this.panelSize.width, this.panelSize.height);
49295         }
49296         if(this.closeBtn){
49297             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49298         }
49299         this.updateTitle(panel.getTitle());
49300         if(this.tabs){
49301             this.fireEvent("invalidated", this);
49302         }
49303         this.fireEvent("panelactivated", this, panel);
49304     },
49305
49306     /**
49307      * Shows the specified panel.
49308      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49309      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49310      */
49311     showPanel : function(panel){
49312         if(panel = this.getPanel(panel)){
49313             if(this.tabs){
49314                 var tab = this.tabs.getTab(panel.getEl().id);
49315                 if(tab.isHidden()){
49316                     this.tabs.unhideTab(tab.id);
49317                 }
49318                 tab.activate();
49319             }else{
49320                 this.setActivePanel(panel);
49321             }
49322         }
49323         return panel;
49324     },
49325
49326     /**
49327      * Get the active panel for this region.
49328      * @return {Roo.ContentPanel} The active panel or null
49329      */
49330     getActivePanel : function(){
49331         return this.activePanel;
49332     },
49333
49334     validateVisibility : function(){
49335         if(this.panels.getCount() < 1){
49336             this.updateTitle("&#160;");
49337             this.closeBtn.hide();
49338             this.hide();
49339         }else{
49340             if(!this.isVisible()){
49341                 this.show();
49342             }
49343         }
49344     },
49345
49346     /**
49347      * Adds the passed ContentPanel(s) to this region.
49348      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49349      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49350      */
49351     add : function(panel){
49352         if(arguments.length > 1){
49353             for(var i = 0, len = arguments.length; i < len; i++) {
49354                 this.add(arguments[i]);
49355             }
49356             return null;
49357         }
49358         if(this.hasPanel(panel)){
49359             this.showPanel(panel);
49360             return panel;
49361         }
49362         panel.setRegion(this);
49363         this.panels.add(panel);
49364         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49365             this.bodyEl.dom.appendChild(panel.getEl().dom);
49366             if(panel.background !== true){
49367                 this.setActivePanel(panel);
49368             }
49369             this.fireEvent("paneladded", this, panel);
49370             return panel;
49371         }
49372         if(!this.tabs){
49373             this.initTabs();
49374         }else{
49375             this.initPanelAsTab(panel);
49376         }
49377         if(panel.background !== true){
49378             this.tabs.activate(panel.getEl().id);
49379         }
49380         this.fireEvent("paneladded", this, panel);
49381         return panel;
49382     },
49383
49384     /**
49385      * Hides the tab for the specified panel.
49386      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49387      */
49388     hidePanel : function(panel){
49389         if(this.tabs && (panel = this.getPanel(panel))){
49390             this.tabs.hideTab(panel.getEl().id);
49391         }
49392     },
49393
49394     /**
49395      * Unhides the tab for a previously hidden panel.
49396      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49397      */
49398     unhidePanel : function(panel){
49399         if(this.tabs && (panel = this.getPanel(panel))){
49400             this.tabs.unhideTab(panel.getEl().id);
49401         }
49402     },
49403
49404     clearPanels : function(){
49405         while(this.panels.getCount() > 0){
49406              this.remove(this.panels.first());
49407         }
49408     },
49409
49410     /**
49411      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49412      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49413      * @param {Boolean} preservePanel Overrides the config preservePanel option
49414      * @return {Roo.ContentPanel} The panel that was removed
49415      */
49416     remove : function(panel, preservePanel){
49417         panel = this.getPanel(panel);
49418         if(!panel){
49419             return null;
49420         }
49421         var e = {};
49422         this.fireEvent("beforeremove", this, panel, e);
49423         if(e.cancel === true){
49424             return null;
49425         }
49426         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49427         var panelId = panel.getId();
49428         this.panels.removeKey(panelId);
49429         if(preservePanel){
49430             document.body.appendChild(panel.getEl().dom);
49431         }
49432         if(this.tabs){
49433             this.tabs.removeTab(panel.getEl().id);
49434         }else if (!preservePanel){
49435             this.bodyEl.dom.removeChild(panel.getEl().dom);
49436         }
49437         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49438             var p = this.panels.first();
49439             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49440             tempEl.appendChild(p.getEl().dom);
49441             this.bodyEl.update("");
49442             this.bodyEl.dom.appendChild(p.getEl().dom);
49443             tempEl = null;
49444             this.updateTitle(p.getTitle());
49445             this.tabs = null;
49446             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49447             this.setActivePanel(p);
49448         }
49449         panel.setRegion(null);
49450         if(this.activePanel == panel){
49451             this.activePanel = null;
49452         }
49453         if(this.config.autoDestroy !== false && preservePanel !== true){
49454             try{panel.destroy();}catch(e){}
49455         }
49456         this.fireEvent("panelremoved", this, panel);
49457         return panel;
49458     },
49459
49460     /**
49461      * Returns the TabPanel component used by this region
49462      * @return {Roo.TabPanel}
49463      */
49464     getTabs : function(){
49465         return this.tabs;
49466     },
49467
49468     createTool : function(parentEl, className){
49469         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49470             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49471         btn.addClassOnOver("x-layout-tools-button-over");
49472         return btn;
49473     }
49474 });/*
49475  * Based on:
49476  * Ext JS Library 1.1.1
49477  * Copyright(c) 2006-2007, Ext JS, LLC.
49478  *
49479  * Originally Released Under LGPL - original licence link has changed is not relivant.
49480  *
49481  * Fork - LGPL
49482  * <script type="text/javascript">
49483  */
49484  
49485
49486
49487 /**
49488  * @class Roo.SplitLayoutRegion
49489  * @extends Roo.LayoutRegion
49490  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49491  */
49492 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49493     this.cursor = cursor;
49494     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49495 };
49496
49497 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49498     splitTip : "Drag to resize.",
49499     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49500     useSplitTips : false,
49501
49502     applyConfig : function(config){
49503         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49504         if(config.split){
49505             if(!this.split){
49506                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49507                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49508                 /** The SplitBar for this region 
49509                 * @type Roo.SplitBar */
49510                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49511                 this.split.on("moved", this.onSplitMove, this);
49512                 this.split.useShim = config.useShim === true;
49513                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49514                 if(this.useSplitTips){
49515                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49516                 }
49517                 if(config.collapsible){
49518                     this.split.el.on("dblclick", this.collapse,  this);
49519                 }
49520             }
49521             if(typeof config.minSize != "undefined"){
49522                 this.split.minSize = config.minSize;
49523             }
49524             if(typeof config.maxSize != "undefined"){
49525                 this.split.maxSize = config.maxSize;
49526             }
49527             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49528                 this.hideSplitter();
49529             }
49530         }
49531     },
49532
49533     getHMaxSize : function(){
49534          var cmax = this.config.maxSize || 10000;
49535          var center = this.mgr.getRegion("center");
49536          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49537     },
49538
49539     getVMaxSize : function(){
49540          var cmax = this.config.maxSize || 10000;
49541          var center = this.mgr.getRegion("center");
49542          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49543     },
49544
49545     onSplitMove : function(split, newSize){
49546         this.fireEvent("resized", this, newSize);
49547     },
49548     
49549     /** 
49550      * Returns the {@link Roo.SplitBar} for this region.
49551      * @return {Roo.SplitBar}
49552      */
49553     getSplitBar : function(){
49554         return this.split;
49555     },
49556     
49557     hide : function(){
49558         this.hideSplitter();
49559         Roo.SplitLayoutRegion.superclass.hide.call(this);
49560     },
49561
49562     hideSplitter : function(){
49563         if(this.split){
49564             this.split.el.setLocation(-2000,-2000);
49565             this.split.el.hide();
49566         }
49567     },
49568
49569     show : function(){
49570         if(this.split){
49571             this.split.el.show();
49572         }
49573         Roo.SplitLayoutRegion.superclass.show.call(this);
49574     },
49575     
49576     beforeSlide: function(){
49577         if(Roo.isGecko){// firefox overflow auto bug workaround
49578             this.bodyEl.clip();
49579             if(this.tabs) this.tabs.bodyEl.clip();
49580             if(this.activePanel){
49581                 this.activePanel.getEl().clip();
49582                 
49583                 if(this.activePanel.beforeSlide){
49584                     this.activePanel.beforeSlide();
49585                 }
49586             }
49587         }
49588     },
49589     
49590     afterSlide : function(){
49591         if(Roo.isGecko){// firefox overflow auto bug workaround
49592             this.bodyEl.unclip();
49593             if(this.tabs) this.tabs.bodyEl.unclip();
49594             if(this.activePanel){
49595                 this.activePanel.getEl().unclip();
49596                 if(this.activePanel.afterSlide){
49597                     this.activePanel.afterSlide();
49598                 }
49599             }
49600         }
49601     },
49602
49603     initAutoHide : function(){
49604         if(this.autoHide !== false){
49605             if(!this.autoHideHd){
49606                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49607                 this.autoHideHd = {
49608                     "mouseout": function(e){
49609                         if(!e.within(this.el, true)){
49610                             st.delay(500);
49611                         }
49612                     },
49613                     "mouseover" : function(e){
49614                         st.cancel();
49615                     },
49616                     scope : this
49617                 };
49618             }
49619             this.el.on(this.autoHideHd);
49620         }
49621     },
49622
49623     clearAutoHide : function(){
49624         if(this.autoHide !== false){
49625             this.el.un("mouseout", this.autoHideHd.mouseout);
49626             this.el.un("mouseover", this.autoHideHd.mouseover);
49627         }
49628     },
49629
49630     clearMonitor : function(){
49631         Roo.get(document).un("click", this.slideInIf, this);
49632     },
49633
49634     // these names are backwards but not changed for compat
49635     slideOut : function(){
49636         if(this.isSlid || this.el.hasActiveFx()){
49637             return;
49638         }
49639         this.isSlid = true;
49640         if(this.collapseBtn){
49641             this.collapseBtn.hide();
49642         }
49643         this.closeBtnState = this.closeBtn.getStyle('display');
49644         this.closeBtn.hide();
49645         if(this.stickBtn){
49646             this.stickBtn.show();
49647         }
49648         this.el.show();
49649         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49650         this.beforeSlide();
49651         this.el.setStyle("z-index", 10001);
49652         this.el.slideIn(this.getSlideAnchor(), {
49653             callback: function(){
49654                 this.afterSlide();
49655                 this.initAutoHide();
49656                 Roo.get(document).on("click", this.slideInIf, this);
49657                 this.fireEvent("slideshow", this);
49658             },
49659             scope: this,
49660             block: true
49661         });
49662     },
49663
49664     afterSlideIn : function(){
49665         this.clearAutoHide();
49666         this.isSlid = false;
49667         this.clearMonitor();
49668         this.el.setStyle("z-index", "");
49669         if(this.collapseBtn){
49670             this.collapseBtn.show();
49671         }
49672         this.closeBtn.setStyle('display', this.closeBtnState);
49673         if(this.stickBtn){
49674             this.stickBtn.hide();
49675         }
49676         this.fireEvent("slidehide", this);
49677     },
49678
49679     slideIn : function(cb){
49680         if(!this.isSlid || this.el.hasActiveFx()){
49681             Roo.callback(cb);
49682             return;
49683         }
49684         this.isSlid = false;
49685         this.beforeSlide();
49686         this.el.slideOut(this.getSlideAnchor(), {
49687             callback: function(){
49688                 this.el.setLeftTop(-10000, -10000);
49689                 this.afterSlide();
49690                 this.afterSlideIn();
49691                 Roo.callback(cb);
49692             },
49693             scope: this,
49694             block: true
49695         });
49696     },
49697     
49698     slideInIf : function(e){
49699         if(!e.within(this.el)){
49700             this.slideIn();
49701         }
49702     },
49703
49704     animateCollapse : function(){
49705         this.beforeSlide();
49706         this.el.setStyle("z-index", 20000);
49707         var anchor = this.getSlideAnchor();
49708         this.el.slideOut(anchor, {
49709             callback : function(){
49710                 this.el.setStyle("z-index", "");
49711                 this.collapsedEl.slideIn(anchor, {duration:.3});
49712                 this.afterSlide();
49713                 this.el.setLocation(-10000,-10000);
49714                 this.el.hide();
49715                 this.fireEvent("collapsed", this);
49716             },
49717             scope: this,
49718             block: true
49719         });
49720     },
49721
49722     animateExpand : function(){
49723         this.beforeSlide();
49724         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49725         this.el.setStyle("z-index", 20000);
49726         this.collapsedEl.hide({
49727             duration:.1
49728         });
49729         this.el.slideIn(this.getSlideAnchor(), {
49730             callback : function(){
49731                 this.el.setStyle("z-index", "");
49732                 this.afterSlide();
49733                 if(this.split){
49734                     this.split.el.show();
49735                 }
49736                 this.fireEvent("invalidated", this);
49737                 this.fireEvent("expanded", this);
49738             },
49739             scope: this,
49740             block: true
49741         });
49742     },
49743
49744     anchors : {
49745         "west" : "left",
49746         "east" : "right",
49747         "north" : "top",
49748         "south" : "bottom"
49749     },
49750
49751     sanchors : {
49752         "west" : "l",
49753         "east" : "r",
49754         "north" : "t",
49755         "south" : "b"
49756     },
49757
49758     canchors : {
49759         "west" : "tl-tr",
49760         "east" : "tr-tl",
49761         "north" : "tl-bl",
49762         "south" : "bl-tl"
49763     },
49764
49765     getAnchor : function(){
49766         return this.anchors[this.position];
49767     },
49768
49769     getCollapseAnchor : function(){
49770         return this.canchors[this.position];
49771     },
49772
49773     getSlideAnchor : function(){
49774         return this.sanchors[this.position];
49775     },
49776
49777     getAlignAdj : function(){
49778         var cm = this.cmargins;
49779         switch(this.position){
49780             case "west":
49781                 return [0, 0];
49782             break;
49783             case "east":
49784                 return [0, 0];
49785             break;
49786             case "north":
49787                 return [0, 0];
49788             break;
49789             case "south":
49790                 return [0, 0];
49791             break;
49792         }
49793     },
49794
49795     getExpandAdj : function(){
49796         var c = this.collapsedEl, cm = this.cmargins;
49797         switch(this.position){
49798             case "west":
49799                 return [-(cm.right+c.getWidth()+cm.left), 0];
49800             break;
49801             case "east":
49802                 return [cm.right+c.getWidth()+cm.left, 0];
49803             break;
49804             case "north":
49805                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49806             break;
49807             case "south":
49808                 return [0, cm.top+cm.bottom+c.getHeight()];
49809             break;
49810         }
49811     }
49812 });/*
49813  * Based on:
49814  * Ext JS Library 1.1.1
49815  * Copyright(c) 2006-2007, Ext JS, LLC.
49816  *
49817  * Originally Released Under LGPL - original licence link has changed is not relivant.
49818  *
49819  * Fork - LGPL
49820  * <script type="text/javascript">
49821  */
49822 /*
49823  * These classes are private internal classes
49824  */
49825 Roo.CenterLayoutRegion = function(mgr, config){
49826     Roo.LayoutRegion.call(this, mgr, config, "center");
49827     this.visible = true;
49828     this.minWidth = config.minWidth || 20;
49829     this.minHeight = config.minHeight || 20;
49830 };
49831
49832 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49833     hide : function(){
49834         // center panel can't be hidden
49835     },
49836     
49837     show : function(){
49838         // center panel can't be hidden
49839     },
49840     
49841     getMinWidth: function(){
49842         return this.minWidth;
49843     },
49844     
49845     getMinHeight: function(){
49846         return this.minHeight;
49847     }
49848 });
49849
49850
49851 Roo.NorthLayoutRegion = function(mgr, config){
49852     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49853     if(this.split){
49854         this.split.placement = Roo.SplitBar.TOP;
49855         this.split.orientation = Roo.SplitBar.VERTICAL;
49856         this.split.el.addClass("x-layout-split-v");
49857     }
49858     var size = config.initialSize || config.height;
49859     if(typeof size != "undefined"){
49860         this.el.setHeight(size);
49861     }
49862 };
49863 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49864     orientation: Roo.SplitBar.VERTICAL,
49865     getBox : function(){
49866         if(this.collapsed){
49867             return this.collapsedEl.getBox();
49868         }
49869         var box = this.el.getBox();
49870         if(this.split){
49871             box.height += this.split.el.getHeight();
49872         }
49873         return box;
49874     },
49875     
49876     updateBox : function(box){
49877         if(this.split && !this.collapsed){
49878             box.height -= this.split.el.getHeight();
49879             this.split.el.setLeft(box.x);
49880             this.split.el.setTop(box.y+box.height);
49881             this.split.el.setWidth(box.width);
49882         }
49883         if(this.collapsed){
49884             this.updateBody(box.width, null);
49885         }
49886         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49887     }
49888 });
49889
49890 Roo.SouthLayoutRegion = function(mgr, config){
49891     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49892     if(this.split){
49893         this.split.placement = Roo.SplitBar.BOTTOM;
49894         this.split.orientation = Roo.SplitBar.VERTICAL;
49895         this.split.el.addClass("x-layout-split-v");
49896     }
49897     var size = config.initialSize || config.height;
49898     if(typeof size != "undefined"){
49899         this.el.setHeight(size);
49900     }
49901 };
49902 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49903     orientation: Roo.SplitBar.VERTICAL,
49904     getBox : function(){
49905         if(this.collapsed){
49906             return this.collapsedEl.getBox();
49907         }
49908         var box = this.el.getBox();
49909         if(this.split){
49910             var sh = this.split.el.getHeight();
49911             box.height += sh;
49912             box.y -= sh;
49913         }
49914         return box;
49915     },
49916     
49917     updateBox : function(box){
49918         if(this.split && !this.collapsed){
49919             var sh = this.split.el.getHeight();
49920             box.height -= sh;
49921             box.y += sh;
49922             this.split.el.setLeft(box.x);
49923             this.split.el.setTop(box.y-sh);
49924             this.split.el.setWidth(box.width);
49925         }
49926         if(this.collapsed){
49927             this.updateBody(box.width, null);
49928         }
49929         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49930     }
49931 });
49932
49933 Roo.EastLayoutRegion = function(mgr, config){
49934     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49935     if(this.split){
49936         this.split.placement = Roo.SplitBar.RIGHT;
49937         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49938         this.split.el.addClass("x-layout-split-h");
49939     }
49940     var size = config.initialSize || config.width;
49941     if(typeof size != "undefined"){
49942         this.el.setWidth(size);
49943     }
49944 };
49945 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49946     orientation: Roo.SplitBar.HORIZONTAL,
49947     getBox : function(){
49948         if(this.collapsed){
49949             return this.collapsedEl.getBox();
49950         }
49951         var box = this.el.getBox();
49952         if(this.split){
49953             var sw = this.split.el.getWidth();
49954             box.width += sw;
49955             box.x -= sw;
49956         }
49957         return box;
49958     },
49959
49960     updateBox : function(box){
49961         if(this.split && !this.collapsed){
49962             var sw = this.split.el.getWidth();
49963             box.width -= sw;
49964             this.split.el.setLeft(box.x);
49965             this.split.el.setTop(box.y);
49966             this.split.el.setHeight(box.height);
49967             box.x += sw;
49968         }
49969         if(this.collapsed){
49970             this.updateBody(null, box.height);
49971         }
49972         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49973     }
49974 });
49975
49976 Roo.WestLayoutRegion = function(mgr, config){
49977     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49978     if(this.split){
49979         this.split.placement = Roo.SplitBar.LEFT;
49980         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49981         this.split.el.addClass("x-layout-split-h");
49982     }
49983     var size = config.initialSize || config.width;
49984     if(typeof size != "undefined"){
49985         this.el.setWidth(size);
49986     }
49987 };
49988 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
49989     orientation: Roo.SplitBar.HORIZONTAL,
49990     getBox : function(){
49991         if(this.collapsed){
49992             return this.collapsedEl.getBox();
49993         }
49994         var box = this.el.getBox();
49995         if(this.split){
49996             box.width += this.split.el.getWidth();
49997         }
49998         return box;
49999     },
50000     
50001     updateBox : function(box){
50002         if(this.split && !this.collapsed){
50003             var sw = this.split.el.getWidth();
50004             box.width -= sw;
50005             this.split.el.setLeft(box.x+box.width);
50006             this.split.el.setTop(box.y);
50007             this.split.el.setHeight(box.height);
50008         }
50009         if(this.collapsed){
50010             this.updateBody(null, box.height);
50011         }
50012         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50013     }
50014 });
50015 /*
50016  * Based on:
50017  * Ext JS Library 1.1.1
50018  * Copyright(c) 2006-2007, Ext JS, LLC.
50019  *
50020  * Originally Released Under LGPL - original licence link has changed is not relivant.
50021  *
50022  * Fork - LGPL
50023  * <script type="text/javascript">
50024  */
50025  
50026  
50027 /*
50028  * Private internal class for reading and applying state
50029  */
50030 Roo.LayoutStateManager = function(layout){
50031      // default empty state
50032      this.state = {
50033         north: {},
50034         south: {},
50035         east: {},
50036         west: {}       
50037     };
50038 };
50039
50040 Roo.LayoutStateManager.prototype = {
50041     init : function(layout, provider){
50042         this.provider = provider;
50043         var state = provider.get(layout.id+"-layout-state");
50044         if(state){
50045             var wasUpdating = layout.isUpdating();
50046             if(!wasUpdating){
50047                 layout.beginUpdate();
50048             }
50049             for(var key in state){
50050                 if(typeof state[key] != "function"){
50051                     var rstate = state[key];
50052                     var r = layout.getRegion(key);
50053                     if(r && rstate){
50054                         if(rstate.size){
50055                             r.resizeTo(rstate.size);
50056                         }
50057                         if(rstate.collapsed == true){
50058                             r.collapse(true);
50059                         }else{
50060                             r.expand(null, true);
50061                         }
50062                     }
50063                 }
50064             }
50065             if(!wasUpdating){
50066                 layout.endUpdate();
50067             }
50068             this.state = state; 
50069         }
50070         this.layout = layout;
50071         layout.on("regionresized", this.onRegionResized, this);
50072         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50073         layout.on("regionexpanded", this.onRegionExpanded, this);
50074     },
50075     
50076     storeState : function(){
50077         this.provider.set(this.layout.id+"-layout-state", this.state);
50078     },
50079     
50080     onRegionResized : function(region, newSize){
50081         this.state[region.getPosition()].size = newSize;
50082         this.storeState();
50083     },
50084     
50085     onRegionCollapsed : function(region){
50086         this.state[region.getPosition()].collapsed = true;
50087         this.storeState();
50088     },
50089     
50090     onRegionExpanded : function(region){
50091         this.state[region.getPosition()].collapsed = false;
50092         this.storeState();
50093     }
50094 };/*
50095  * Based on:
50096  * Ext JS Library 1.1.1
50097  * Copyright(c) 2006-2007, Ext JS, LLC.
50098  *
50099  * Originally Released Under LGPL - original licence link has changed is not relivant.
50100  *
50101  * Fork - LGPL
50102  * <script type="text/javascript">
50103  */
50104 /**
50105  * @class Roo.ContentPanel
50106  * @extends Roo.util.Observable
50107  * A basic ContentPanel element.
50108  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50109  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50110  * @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
50111  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50112  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50113  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50114  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50115  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50116  * @cfg {String} title          The title for this panel
50117  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50118  * @cfg {String} url            Calls {@link #setUrl} with this value
50119  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50120  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50121  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50122  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50123
50124  * @constructor
50125  * Create a new ContentPanel.
50126  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50127  * @param {String/Object} config A string to set only the title or a config object
50128  * @param {String} content (optional) Set the HTML content for this panel
50129  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50130  */
50131 Roo.ContentPanel = function(el, config, content){
50132     
50133      
50134     /*
50135     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50136         config = el;
50137         el = Roo.id();
50138     }
50139     if (config && config.parentLayout) { 
50140         el = config.parentLayout.el.createChild(); 
50141     }
50142     */
50143     if(el.autoCreate){ // xtype is available if this is called from factory
50144         config = el;
50145         el = Roo.id();
50146     }
50147     this.el = Roo.get(el);
50148     if(!this.el && config && config.autoCreate){
50149         if(typeof config.autoCreate == "object"){
50150             if(!config.autoCreate.id){
50151                 config.autoCreate.id = config.id||el;
50152             }
50153             this.el = Roo.DomHelper.append(document.body,
50154                         config.autoCreate, true);
50155         }else{
50156             this.el = Roo.DomHelper.append(document.body,
50157                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50158         }
50159     }
50160     this.closable = false;
50161     this.loaded = false;
50162     this.active = false;
50163     if(typeof config == "string"){
50164         this.title = config;
50165     }else{
50166         Roo.apply(this, config);
50167     }
50168     
50169     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50170         this.wrapEl = this.el.wrap();
50171         this.toolbar.container = this.el.insertSibling(false, 'before');
50172         this.toolbar = new Roo.Toolbar(this.toolbar);
50173     }
50174     
50175     // xtype created footer. - not sure if will work as we normally have to render first..
50176     if (this.footer && !this.footer.el && this.footer.xtype) {
50177         if (!this.wrapEl) {
50178             this.wrapEl = this.el.wrap();
50179         }
50180     
50181         this.footer.container = this.wrapEl.createChild();
50182          
50183         this.footer = Roo.factory(this.footer, Roo);
50184         
50185     }
50186     
50187     if(this.resizeEl){
50188         this.resizeEl = Roo.get(this.resizeEl, true);
50189     }else{
50190         this.resizeEl = this.el;
50191     }
50192     // handle view.xtype
50193     
50194  
50195     
50196     
50197     this.addEvents({
50198         /**
50199          * @event activate
50200          * Fires when this panel is activated. 
50201          * @param {Roo.ContentPanel} this
50202          */
50203         "activate" : true,
50204         /**
50205          * @event deactivate
50206          * Fires when this panel is activated. 
50207          * @param {Roo.ContentPanel} this
50208          */
50209         "deactivate" : true,
50210
50211         /**
50212          * @event resize
50213          * Fires when this panel is resized if fitToFrame is true.
50214          * @param {Roo.ContentPanel} this
50215          * @param {Number} width The width after any component adjustments
50216          * @param {Number} height The height after any component adjustments
50217          */
50218         "resize" : true,
50219         
50220          /**
50221          * @event render
50222          * Fires when this tab is created
50223          * @param {Roo.ContentPanel} this
50224          */
50225         "render" : true
50226         
50227         
50228         
50229     });
50230     
50231
50232     
50233     
50234     if(this.autoScroll){
50235         this.resizeEl.setStyle("overflow", "auto");
50236     } else {
50237         // fix randome scrolling
50238         this.el.on('scroll', function() {
50239             Roo.log('fix random scolling');
50240             this.scrollTo('top',0); 
50241         });
50242     }
50243     content = content || this.content;
50244     if(content){
50245         this.setContent(content);
50246     }
50247     if(config && config.url){
50248         this.setUrl(this.url, this.params, this.loadOnce);
50249     }
50250     
50251     
50252     
50253     Roo.ContentPanel.superclass.constructor.call(this);
50254     
50255     if (this.view && typeof(this.view.xtype) != 'undefined') {
50256         this.view.el = this.el.appendChild(document.createElement("div"));
50257         this.view = Roo.factory(this.view); 
50258         this.view.render  &&  this.view.render(false, '');  
50259     }
50260     
50261     
50262     this.fireEvent('render', this);
50263 };
50264
50265 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50266     tabTip:'',
50267     setRegion : function(region){
50268         this.region = region;
50269         if(region){
50270            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50271         }else{
50272            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50273         } 
50274     },
50275     
50276     /**
50277      * Returns the toolbar for this Panel if one was configured. 
50278      * @return {Roo.Toolbar} 
50279      */
50280     getToolbar : function(){
50281         return this.toolbar;
50282     },
50283     
50284     setActiveState : function(active){
50285         this.active = active;
50286         if(!active){
50287             this.fireEvent("deactivate", this);
50288         }else{
50289             this.fireEvent("activate", this);
50290         }
50291     },
50292     /**
50293      * Updates this panel's element
50294      * @param {String} content The new content
50295      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50296     */
50297     setContent : function(content, loadScripts){
50298         this.el.update(content, loadScripts);
50299     },
50300
50301     ignoreResize : function(w, h){
50302         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50303             return true;
50304         }else{
50305             this.lastSize = {width: w, height: h};
50306             return false;
50307         }
50308     },
50309     /**
50310      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50311      * @return {Roo.UpdateManager} The UpdateManager
50312      */
50313     getUpdateManager : function(){
50314         return this.el.getUpdateManager();
50315     },
50316      /**
50317      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50318      * @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:
50319 <pre><code>
50320 panel.load({
50321     url: "your-url.php",
50322     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50323     callback: yourFunction,
50324     scope: yourObject, //(optional scope)
50325     discardUrl: false,
50326     nocache: false,
50327     text: "Loading...",
50328     timeout: 30,
50329     scripts: false
50330 });
50331 </code></pre>
50332      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50333      * 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.
50334      * @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}
50335      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50336      * @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.
50337      * @return {Roo.ContentPanel} this
50338      */
50339     load : function(){
50340         var um = this.el.getUpdateManager();
50341         um.update.apply(um, arguments);
50342         return this;
50343     },
50344
50345
50346     /**
50347      * 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.
50348      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50349      * @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)
50350      * @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)
50351      * @return {Roo.UpdateManager} The UpdateManager
50352      */
50353     setUrl : function(url, params, loadOnce){
50354         if(this.refreshDelegate){
50355             this.removeListener("activate", this.refreshDelegate);
50356         }
50357         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50358         this.on("activate", this.refreshDelegate);
50359         return this.el.getUpdateManager();
50360     },
50361     
50362     _handleRefresh : function(url, params, loadOnce){
50363         if(!loadOnce || !this.loaded){
50364             var updater = this.el.getUpdateManager();
50365             updater.update(url, params, this._setLoaded.createDelegate(this));
50366         }
50367     },
50368     
50369     _setLoaded : function(){
50370         this.loaded = true;
50371     }, 
50372     
50373     /**
50374      * Returns this panel's id
50375      * @return {String} 
50376      */
50377     getId : function(){
50378         return this.el.id;
50379     },
50380     
50381     /** 
50382      * Returns this panel's element - used by regiosn to add.
50383      * @return {Roo.Element} 
50384      */
50385     getEl : function(){
50386         return this.wrapEl || this.el;
50387     },
50388     
50389     adjustForComponents : function(width, height)
50390     {
50391         //Roo.log('adjustForComponents ');
50392         if(this.resizeEl != this.el){
50393             width -= this.el.getFrameWidth('lr');
50394             height -= this.el.getFrameWidth('tb');
50395         }
50396         if(this.toolbar){
50397             var te = this.toolbar.getEl();
50398             height -= te.getHeight();
50399             te.setWidth(width);
50400         }
50401         if(this.footer){
50402             var te = this.footer.getEl();
50403             Roo.log("footer:" + te.getHeight());
50404             
50405             height -= te.getHeight();
50406             te.setWidth(width);
50407         }
50408         
50409         
50410         if(this.adjustments){
50411             width += this.adjustments[0];
50412             height += this.adjustments[1];
50413         }
50414         return {"width": width, "height": height};
50415     },
50416     
50417     setSize : function(width, height){
50418         if(this.fitToFrame && !this.ignoreResize(width, height)){
50419             if(this.fitContainer && this.resizeEl != this.el){
50420                 this.el.setSize(width, height);
50421             }
50422             var size = this.adjustForComponents(width, height);
50423             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50424             this.fireEvent('resize', this, size.width, size.height);
50425         }
50426     },
50427     
50428     /**
50429      * Returns this panel's title
50430      * @return {String} 
50431      */
50432     getTitle : function(){
50433         return this.title;
50434     },
50435     
50436     /**
50437      * Set this panel's title
50438      * @param {String} title
50439      */
50440     setTitle : function(title){
50441         this.title = title;
50442         if(this.region){
50443             this.region.updatePanelTitle(this, title);
50444         }
50445     },
50446     
50447     /**
50448      * Returns true is this panel was configured to be closable
50449      * @return {Boolean} 
50450      */
50451     isClosable : function(){
50452         return this.closable;
50453     },
50454     
50455     beforeSlide : function(){
50456         this.el.clip();
50457         this.resizeEl.clip();
50458     },
50459     
50460     afterSlide : function(){
50461         this.el.unclip();
50462         this.resizeEl.unclip();
50463     },
50464     
50465     /**
50466      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50467      *   Will fail silently if the {@link #setUrl} method has not been called.
50468      *   This does not activate the panel, just updates its content.
50469      */
50470     refresh : function(){
50471         if(this.refreshDelegate){
50472            this.loaded = false;
50473            this.refreshDelegate();
50474         }
50475     },
50476     
50477     /**
50478      * Destroys this panel
50479      */
50480     destroy : function(){
50481         this.el.removeAllListeners();
50482         var tempEl = document.createElement("span");
50483         tempEl.appendChild(this.el.dom);
50484         tempEl.innerHTML = "";
50485         this.el.remove();
50486         this.el = null;
50487     },
50488     
50489     /**
50490      * form - if the content panel contains a form - this is a reference to it.
50491      * @type {Roo.form.Form}
50492      */
50493     form : false,
50494     /**
50495      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50496      *    This contains a reference to it.
50497      * @type {Roo.View}
50498      */
50499     view : false,
50500     
50501       /**
50502      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50503      * <pre><code>
50504
50505 layout.addxtype({
50506        xtype : 'Form',
50507        items: [ .... ]
50508    }
50509 );
50510
50511 </code></pre>
50512      * @param {Object} cfg Xtype definition of item to add.
50513      */
50514     
50515     addxtype : function(cfg) {
50516         // add form..
50517         if (cfg.xtype.match(/^Form$/)) {
50518             
50519             var el;
50520             //if (this.footer) {
50521             //    el = this.footer.container.insertSibling(false, 'before');
50522             //} else {
50523                 el = this.el.createChild();
50524             //}
50525
50526             this.form = new  Roo.form.Form(cfg);
50527             
50528             
50529             if ( this.form.allItems.length) this.form.render(el.dom);
50530             return this.form;
50531         }
50532         // should only have one of theses..
50533         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50534             // views.. should not be just added - used named prop 'view''
50535             
50536             cfg.el = this.el.appendChild(document.createElement("div"));
50537             // factory?
50538             
50539             var ret = new Roo.factory(cfg);
50540              
50541              ret.render && ret.render(false, ''); // render blank..
50542             this.view = ret;
50543             return ret;
50544         }
50545         return false;
50546     }
50547 });
50548
50549 /**
50550  * @class Roo.GridPanel
50551  * @extends Roo.ContentPanel
50552  * @constructor
50553  * Create a new GridPanel.
50554  * @param {Roo.grid.Grid} grid The grid for this panel
50555  * @param {String/Object} config A string to set only the panel's title, or a config object
50556  */
50557 Roo.GridPanel = function(grid, config){
50558     
50559   
50560     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50561         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50562         
50563     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50564     
50565     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50566     
50567     if(this.toolbar){
50568         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50569     }
50570     // xtype created footer. - not sure if will work as we normally have to render first..
50571     if (this.footer && !this.footer.el && this.footer.xtype) {
50572         
50573         this.footer.container = this.grid.getView().getFooterPanel(true);
50574         this.footer.dataSource = this.grid.dataSource;
50575         this.footer = Roo.factory(this.footer, Roo);
50576         
50577     }
50578     
50579     grid.monitorWindowResize = false; // turn off autosizing
50580     grid.autoHeight = false;
50581     grid.autoWidth = false;
50582     this.grid = grid;
50583     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50584 };
50585
50586 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50587     getId : function(){
50588         return this.grid.id;
50589     },
50590     
50591     /**
50592      * Returns the grid for this panel
50593      * @return {Roo.grid.Grid} 
50594      */
50595     getGrid : function(){
50596         return this.grid;    
50597     },
50598     
50599     setSize : function(width, height){
50600         if(!this.ignoreResize(width, height)){
50601             var grid = this.grid;
50602             var size = this.adjustForComponents(width, height);
50603             grid.getGridEl().setSize(size.width, size.height);
50604             grid.autoSize();
50605         }
50606     },
50607     
50608     beforeSlide : function(){
50609         this.grid.getView().scroller.clip();
50610     },
50611     
50612     afterSlide : function(){
50613         this.grid.getView().scroller.unclip();
50614     },
50615     
50616     destroy : function(){
50617         this.grid.destroy();
50618         delete this.grid;
50619         Roo.GridPanel.superclass.destroy.call(this); 
50620     }
50621 });
50622
50623
50624 /**
50625  * @class Roo.NestedLayoutPanel
50626  * @extends Roo.ContentPanel
50627  * @constructor
50628  * Create a new NestedLayoutPanel.
50629  * 
50630  * 
50631  * @param {Roo.BorderLayout} layout The layout for this panel
50632  * @param {String/Object} config A string to set only the title or a config object
50633  */
50634 Roo.NestedLayoutPanel = function(layout, config)
50635 {
50636     // construct with only one argument..
50637     /* FIXME - implement nicer consturctors
50638     if (layout.layout) {
50639         config = layout;
50640         layout = config.layout;
50641         delete config.layout;
50642     }
50643     if (layout.xtype && !layout.getEl) {
50644         // then layout needs constructing..
50645         layout = Roo.factory(layout, Roo);
50646     }
50647     */
50648     
50649     
50650     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50651     
50652     layout.monitorWindowResize = false; // turn off autosizing
50653     this.layout = layout;
50654     this.layout.getEl().addClass("x-layout-nested-layout");
50655     
50656     
50657     
50658     
50659 };
50660
50661 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
50662
50663     setSize : function(width, height){
50664         if(!this.ignoreResize(width, height)){
50665             var size = this.adjustForComponents(width, height);
50666             var el = this.layout.getEl();
50667             el.setSize(size.width, size.height);
50668             var touch = el.dom.offsetWidth;
50669             this.layout.layout();
50670             // ie requires a double layout on the first pass
50671             if(Roo.isIE && !this.initialized){
50672                 this.initialized = true;
50673                 this.layout.layout();
50674             }
50675         }
50676     },
50677     
50678     // activate all subpanels if not currently active..
50679     
50680     setActiveState : function(active){
50681         this.active = active;
50682         if(!active){
50683             this.fireEvent("deactivate", this);
50684             return;
50685         }
50686         
50687         this.fireEvent("activate", this);
50688         // not sure if this should happen before or after..
50689         if (!this.layout) {
50690             return; // should not happen..
50691         }
50692         var reg = false;
50693         for (var r in this.layout.regions) {
50694             reg = this.layout.getRegion(r);
50695             if (reg.getActivePanel()) {
50696                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
50697                 reg.setActivePanel(reg.getActivePanel());
50698                 continue;
50699             }
50700             if (!reg.panels.length) {
50701                 continue;
50702             }
50703             reg.showPanel(reg.getPanel(0));
50704         }
50705         
50706         
50707         
50708         
50709     },
50710     
50711     /**
50712      * Returns the nested BorderLayout for this panel
50713      * @return {Roo.BorderLayout} 
50714      */
50715     getLayout : function(){
50716         return this.layout;
50717     },
50718     
50719      /**
50720      * Adds a xtype elements to the layout of the nested panel
50721      * <pre><code>
50722
50723 panel.addxtype({
50724        xtype : 'ContentPanel',
50725        region: 'west',
50726        items: [ .... ]
50727    }
50728 );
50729
50730 panel.addxtype({
50731         xtype : 'NestedLayoutPanel',
50732         region: 'west',
50733         layout: {
50734            center: { },
50735            west: { }   
50736         },
50737         items : [ ... list of content panels or nested layout panels.. ]
50738    }
50739 );
50740 </code></pre>
50741      * @param {Object} cfg Xtype definition of item to add.
50742      */
50743     addxtype : function(cfg) {
50744         return this.layout.addxtype(cfg);
50745     
50746     }
50747 });
50748
50749 Roo.ScrollPanel = function(el, config, content){
50750     config = config || {};
50751     config.fitToFrame = true;
50752     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50753     
50754     this.el.dom.style.overflow = "hidden";
50755     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50756     this.el.removeClass("x-layout-inactive-content");
50757     this.el.on("mousewheel", this.onWheel, this);
50758
50759     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50760     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50761     up.unselectable(); down.unselectable();
50762     up.on("click", this.scrollUp, this);
50763     down.on("click", this.scrollDown, this);
50764     up.addClassOnOver("x-scroller-btn-over");
50765     down.addClassOnOver("x-scroller-btn-over");
50766     up.addClassOnClick("x-scroller-btn-click");
50767     down.addClassOnClick("x-scroller-btn-click");
50768     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50769
50770     this.resizeEl = this.el;
50771     this.el = wrap; this.up = up; this.down = down;
50772 };
50773
50774 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50775     increment : 100,
50776     wheelIncrement : 5,
50777     scrollUp : function(){
50778         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50779     },
50780
50781     scrollDown : function(){
50782         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50783     },
50784
50785     afterScroll : function(){
50786         var el = this.resizeEl;
50787         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50788         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50789         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50790     },
50791
50792     setSize : function(){
50793         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50794         this.afterScroll();
50795     },
50796
50797     onWheel : function(e){
50798         var d = e.getWheelDelta();
50799         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50800         this.afterScroll();
50801         e.stopEvent();
50802     },
50803
50804     setContent : function(content, loadScripts){
50805         this.resizeEl.update(content, loadScripts);
50806     }
50807
50808 });
50809
50810
50811
50812
50813
50814
50815
50816
50817
50818 /**
50819  * @class Roo.TreePanel
50820  * @extends Roo.ContentPanel
50821  * @constructor
50822  * Create a new TreePanel. - defaults to fit/scoll contents.
50823  * @param {String/Object} config A string to set only the panel's title, or a config object
50824  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50825  */
50826 Roo.TreePanel = function(config){
50827     var el = config.el;
50828     var tree = config.tree;
50829     delete config.tree; 
50830     delete config.el; // hopefull!
50831     
50832     // wrapper for IE7 strict & safari scroll issue
50833     
50834     var treeEl = el.createChild();
50835     config.resizeEl = treeEl;
50836     
50837     
50838     
50839     Roo.TreePanel.superclass.constructor.call(this, el, config);
50840  
50841  
50842     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50843     //console.log(tree);
50844     this.on('activate', function()
50845     {
50846         if (this.tree.rendered) {
50847             return;
50848         }
50849         //console.log('render tree');
50850         this.tree.render();
50851     });
50852     // this should not be needed.. - it's actually the 'el' that resizes?
50853     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50854     
50855     //this.on('resize',  function (cp, w, h) {
50856     //        this.tree.innerCt.setWidth(w);
50857     //        this.tree.innerCt.setHeight(h);
50858     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50859     //});
50860
50861         
50862     
50863 };
50864
50865 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50866     fitToFrame : true,
50867     autoScroll : true
50868 });
50869
50870
50871
50872
50873
50874
50875
50876
50877
50878
50879
50880 /*
50881  * Based on:
50882  * Ext JS Library 1.1.1
50883  * Copyright(c) 2006-2007, Ext JS, LLC.
50884  *
50885  * Originally Released Under LGPL - original licence link has changed is not relivant.
50886  *
50887  * Fork - LGPL
50888  * <script type="text/javascript">
50889  */
50890  
50891
50892 /**
50893  * @class Roo.ReaderLayout
50894  * @extends Roo.BorderLayout
50895  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50896  * center region containing two nested regions (a top one for a list view and one for item preview below),
50897  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50898  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50899  * expedites the setup of the overall layout and regions for this common application style.
50900  * Example:
50901  <pre><code>
50902 var reader = new Roo.ReaderLayout();
50903 var CP = Roo.ContentPanel;  // shortcut for adding
50904
50905 reader.beginUpdate();
50906 reader.add("north", new CP("north", "North"));
50907 reader.add("west", new CP("west", {title: "West"}));
50908 reader.add("east", new CP("east", {title: "East"}));
50909
50910 reader.regions.listView.add(new CP("listView", "List"));
50911 reader.regions.preview.add(new CP("preview", "Preview"));
50912 reader.endUpdate();
50913 </code></pre>
50914 * @constructor
50915 * Create a new ReaderLayout
50916 * @param {Object} config Configuration options
50917 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50918 * document.body if omitted)
50919 */
50920 Roo.ReaderLayout = function(config, renderTo){
50921     var c = config || {size:{}};
50922     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50923         north: c.north !== false ? Roo.apply({
50924             split:false,
50925             initialSize: 32,
50926             titlebar: false
50927         }, c.north) : false,
50928         west: c.west !== false ? Roo.apply({
50929             split:true,
50930             initialSize: 200,
50931             minSize: 175,
50932             maxSize: 400,
50933             titlebar: true,
50934             collapsible: true,
50935             animate: true,
50936             margins:{left:5,right:0,bottom:5,top:5},
50937             cmargins:{left:5,right:5,bottom:5,top:5}
50938         }, c.west) : false,
50939         east: c.east !== false ? Roo.apply({
50940             split:true,
50941             initialSize: 200,
50942             minSize: 175,
50943             maxSize: 400,
50944             titlebar: true,
50945             collapsible: true,
50946             animate: true,
50947             margins:{left:0,right:5,bottom:5,top:5},
50948             cmargins:{left:5,right:5,bottom:5,top:5}
50949         }, c.east) : false,
50950         center: Roo.apply({
50951             tabPosition: 'top',
50952             autoScroll:false,
50953             closeOnTab: true,
50954             titlebar:false,
50955             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50956         }, c.center)
50957     });
50958
50959     this.el.addClass('x-reader');
50960
50961     this.beginUpdate();
50962
50963     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50964         south: c.preview !== false ? Roo.apply({
50965             split:true,
50966             initialSize: 200,
50967             minSize: 100,
50968             autoScroll:true,
50969             collapsible:true,
50970             titlebar: true,
50971             cmargins:{top:5,left:0, right:0, bottom:0}
50972         }, c.preview) : false,
50973         center: Roo.apply({
50974             autoScroll:false,
50975             titlebar:false,
50976             minHeight:200
50977         }, c.listView)
50978     });
50979     this.add('center', new Roo.NestedLayoutPanel(inner,
50980             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50981
50982     this.endUpdate();
50983
50984     this.regions.preview = inner.getRegion('south');
50985     this.regions.listView = inner.getRegion('center');
50986 };
50987
50988 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
50989  * Based on:
50990  * Ext JS Library 1.1.1
50991  * Copyright(c) 2006-2007, Ext JS, LLC.
50992  *
50993  * Originally Released Under LGPL - original licence link has changed is not relivant.
50994  *
50995  * Fork - LGPL
50996  * <script type="text/javascript">
50997  */
50998  
50999 /**
51000  * @class Roo.grid.Grid
51001  * @extends Roo.util.Observable
51002  * This class represents the primary interface of a component based grid control.
51003  * <br><br>Usage:<pre><code>
51004  var grid = new Roo.grid.Grid("my-container-id", {
51005      ds: myDataStore,
51006      cm: myColModel,
51007      selModel: mySelectionModel,
51008      autoSizeColumns: true,
51009      monitorWindowResize: false,
51010      trackMouseOver: true
51011  });
51012  // set any options
51013  grid.render();
51014  * </code></pre>
51015  * <b>Common Problems:</b><br/>
51016  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51017  * element will correct this<br/>
51018  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51019  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51020  * are unpredictable.<br/>
51021  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51022  * grid to calculate dimensions/offsets.<br/>
51023   * @constructor
51024  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51025  * The container MUST have some type of size defined for the grid to fill. The container will be
51026  * automatically set to position relative if it isn't already.
51027  * @param {Object} config A config object that sets properties on this grid.
51028  */
51029 Roo.grid.Grid = function(container, config){
51030         // initialize the container
51031         this.container = Roo.get(container);
51032         this.container.update("");
51033         this.container.setStyle("overflow", "hidden");
51034     this.container.addClass('x-grid-container');
51035
51036     this.id = this.container.id;
51037
51038     Roo.apply(this, config);
51039     // check and correct shorthanded configs
51040     if(this.ds){
51041         this.dataSource = this.ds;
51042         delete this.ds;
51043     }
51044     if(this.cm){
51045         this.colModel = this.cm;
51046         delete this.cm;
51047     }
51048     if(this.sm){
51049         this.selModel = this.sm;
51050         delete this.sm;
51051     }
51052
51053     if (this.selModel) {
51054         this.selModel = Roo.factory(this.selModel, Roo.grid);
51055         this.sm = this.selModel;
51056         this.sm.xmodule = this.xmodule || false;
51057     }
51058     if (typeof(this.colModel.config) == 'undefined') {
51059         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51060         this.cm = this.colModel;
51061         this.cm.xmodule = this.xmodule || false;
51062     }
51063     if (this.dataSource) {
51064         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51065         this.ds = this.dataSource;
51066         this.ds.xmodule = this.xmodule || false;
51067          
51068     }
51069     
51070     
51071     
51072     if(this.width){
51073         this.container.setWidth(this.width);
51074     }
51075
51076     if(this.height){
51077         this.container.setHeight(this.height);
51078     }
51079     /** @private */
51080         this.addEvents({
51081         // raw events
51082         /**
51083          * @event click
51084          * The raw click event for the entire grid.
51085          * @param {Roo.EventObject} e
51086          */
51087         "click" : true,
51088         /**
51089          * @event dblclick
51090          * The raw dblclick event for the entire grid.
51091          * @param {Roo.EventObject} e
51092          */
51093         "dblclick" : true,
51094         /**
51095          * @event contextmenu
51096          * The raw contextmenu event for the entire grid.
51097          * @param {Roo.EventObject} e
51098          */
51099         "contextmenu" : true,
51100         /**
51101          * @event mousedown
51102          * The raw mousedown event for the entire grid.
51103          * @param {Roo.EventObject} e
51104          */
51105         "mousedown" : true,
51106         /**
51107          * @event mouseup
51108          * The raw mouseup event for the entire grid.
51109          * @param {Roo.EventObject} e
51110          */
51111         "mouseup" : true,
51112         /**
51113          * @event mouseover
51114          * The raw mouseover event for the entire grid.
51115          * @param {Roo.EventObject} e
51116          */
51117         "mouseover" : true,
51118         /**
51119          * @event mouseout
51120          * The raw mouseout event for the entire grid.
51121          * @param {Roo.EventObject} e
51122          */
51123         "mouseout" : true,
51124         /**
51125          * @event keypress
51126          * The raw keypress event for the entire grid.
51127          * @param {Roo.EventObject} e
51128          */
51129         "keypress" : true,
51130         /**
51131          * @event keydown
51132          * The raw keydown event for the entire grid.
51133          * @param {Roo.EventObject} e
51134          */
51135         "keydown" : true,
51136
51137         // custom events
51138
51139         /**
51140          * @event cellclick
51141          * Fires when a cell is clicked
51142          * @param {Grid} this
51143          * @param {Number} rowIndex
51144          * @param {Number} columnIndex
51145          * @param {Roo.EventObject} e
51146          */
51147         "cellclick" : true,
51148         /**
51149          * @event celldblclick
51150          * Fires when a cell is double clicked
51151          * @param {Grid} this
51152          * @param {Number} rowIndex
51153          * @param {Number} columnIndex
51154          * @param {Roo.EventObject} e
51155          */
51156         "celldblclick" : true,
51157         /**
51158          * @event rowclick
51159          * Fires when a row is clicked
51160          * @param {Grid} this
51161          * @param {Number} rowIndex
51162          * @param {Roo.EventObject} e
51163          */
51164         "rowclick" : true,
51165         /**
51166          * @event rowdblclick
51167          * Fires when a row is double clicked
51168          * @param {Grid} this
51169          * @param {Number} rowIndex
51170          * @param {Roo.EventObject} e
51171          */
51172         "rowdblclick" : true,
51173         /**
51174          * @event headerclick
51175          * Fires when a header is clicked
51176          * @param {Grid} this
51177          * @param {Number} columnIndex
51178          * @param {Roo.EventObject} e
51179          */
51180         "headerclick" : true,
51181         /**
51182          * @event headerdblclick
51183          * Fires when a header cell is double clicked
51184          * @param {Grid} this
51185          * @param {Number} columnIndex
51186          * @param {Roo.EventObject} e
51187          */
51188         "headerdblclick" : true,
51189         /**
51190          * @event rowcontextmenu
51191          * Fires when a row is right clicked
51192          * @param {Grid} this
51193          * @param {Number} rowIndex
51194          * @param {Roo.EventObject} e
51195          */
51196         "rowcontextmenu" : true,
51197         /**
51198          * @event cellcontextmenu
51199          * Fires when a cell is right clicked
51200          * @param {Grid} this
51201          * @param {Number} rowIndex
51202          * @param {Number} cellIndex
51203          * @param {Roo.EventObject} e
51204          */
51205          "cellcontextmenu" : true,
51206         /**
51207          * @event headercontextmenu
51208          * Fires when a header is right clicked
51209          * @param {Grid} this
51210          * @param {Number} columnIndex
51211          * @param {Roo.EventObject} e
51212          */
51213         "headercontextmenu" : true,
51214         /**
51215          * @event bodyscroll
51216          * Fires when the body element is scrolled
51217          * @param {Number} scrollLeft
51218          * @param {Number} scrollTop
51219          */
51220         "bodyscroll" : true,
51221         /**
51222          * @event columnresize
51223          * Fires when the user resizes a column
51224          * @param {Number} columnIndex
51225          * @param {Number} newSize
51226          */
51227         "columnresize" : true,
51228         /**
51229          * @event columnmove
51230          * Fires when the user moves a column
51231          * @param {Number} oldIndex
51232          * @param {Number} newIndex
51233          */
51234         "columnmove" : true,
51235         /**
51236          * @event startdrag
51237          * Fires when row(s) start being dragged
51238          * @param {Grid} this
51239          * @param {Roo.GridDD} dd The drag drop object
51240          * @param {event} e The raw browser event
51241          */
51242         "startdrag" : true,
51243         /**
51244          * @event enddrag
51245          * Fires when a drag operation is complete
51246          * @param {Grid} this
51247          * @param {Roo.GridDD} dd The drag drop object
51248          * @param {event} e The raw browser event
51249          */
51250         "enddrag" : true,
51251         /**
51252          * @event dragdrop
51253          * Fires when dragged row(s) are dropped on a valid DD target
51254          * @param {Grid} this
51255          * @param {Roo.GridDD} dd The drag drop object
51256          * @param {String} targetId The target drag drop object
51257          * @param {event} e The raw browser event
51258          */
51259         "dragdrop" : true,
51260         /**
51261          * @event dragover
51262          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51263          * @param {Grid} this
51264          * @param {Roo.GridDD} dd The drag drop object
51265          * @param {String} targetId The target drag drop object
51266          * @param {event} e The raw browser event
51267          */
51268         "dragover" : true,
51269         /**
51270          * @event dragenter
51271          *  Fires when the dragged row(s) first cross another DD target while being dragged
51272          * @param {Grid} this
51273          * @param {Roo.GridDD} dd The drag drop object
51274          * @param {String} targetId The target drag drop object
51275          * @param {event} e The raw browser event
51276          */
51277         "dragenter" : true,
51278         /**
51279          * @event dragout
51280          * Fires when the dragged row(s) leave another DD target while being dragged
51281          * @param {Grid} this
51282          * @param {Roo.GridDD} dd The drag drop object
51283          * @param {String} targetId The target drag drop object
51284          * @param {event} e The raw browser event
51285          */
51286         "dragout" : true,
51287         /**
51288          * @event rowclass
51289          * Fires when a row is rendered, so you can change add a style to it.
51290          * @param {GridView} gridview   The grid view
51291          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51292          */
51293         'rowclass' : true,
51294
51295         /**
51296          * @event render
51297          * Fires when the grid is rendered
51298          * @param {Grid} grid
51299          */
51300         'render' : true
51301     });
51302
51303     Roo.grid.Grid.superclass.constructor.call(this);
51304 };
51305 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51306     
51307     /**
51308      * @cfg {String} ddGroup - drag drop group.
51309      */
51310
51311     /**
51312      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51313      */
51314     minColumnWidth : 25,
51315
51316     /**
51317      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51318      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51319      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51320      */
51321     autoSizeColumns : false,
51322
51323     /**
51324      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51325      */
51326     autoSizeHeaders : true,
51327
51328     /**
51329      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51330      */
51331     monitorWindowResize : true,
51332
51333     /**
51334      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51335      * rows measured to get a columns size. Default is 0 (all rows).
51336      */
51337     maxRowsToMeasure : 0,
51338
51339     /**
51340      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51341      */
51342     trackMouseOver : true,
51343
51344     /**
51345     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51346     */
51347     
51348     /**
51349     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51350     */
51351     enableDragDrop : false,
51352     
51353     /**
51354     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51355     */
51356     enableColumnMove : true,
51357     
51358     /**
51359     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51360     */
51361     enableColumnHide : true,
51362     
51363     /**
51364     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51365     */
51366     enableRowHeightSync : false,
51367     
51368     /**
51369     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51370     */
51371     stripeRows : true,
51372     
51373     /**
51374     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51375     */
51376     autoHeight : false,
51377
51378     /**
51379      * @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.
51380      */
51381     autoExpandColumn : false,
51382
51383     /**
51384     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51385     * Default is 50.
51386     */
51387     autoExpandMin : 50,
51388
51389     /**
51390     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51391     */
51392     autoExpandMax : 1000,
51393
51394     /**
51395     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51396     */
51397     view : null,
51398
51399     /**
51400     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51401     */
51402     loadMask : false,
51403     /**
51404     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51405     */
51406     dropTarget: false,
51407     
51408    
51409     
51410     // private
51411     rendered : false,
51412
51413     /**
51414     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51415     * of a fixed width. Default is false.
51416     */
51417     /**
51418     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51419     */
51420     /**
51421      * Called once after all setup has been completed and the grid is ready to be rendered.
51422      * @return {Roo.grid.Grid} this
51423      */
51424     render : function()
51425     {
51426         var c = this.container;
51427         // try to detect autoHeight/width mode
51428         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51429             this.autoHeight = true;
51430         }
51431         var view = this.getView();
51432         view.init(this);
51433
51434         c.on("click", this.onClick, this);
51435         c.on("dblclick", this.onDblClick, this);
51436         c.on("contextmenu", this.onContextMenu, this);
51437         c.on("keydown", this.onKeyDown, this);
51438         if (Roo.isTouch) {
51439             c.on("touchstart", this.onTouchStart, this);
51440         }
51441
51442         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51443
51444         this.getSelectionModel().init(this);
51445
51446         view.render();
51447
51448         if(this.loadMask){
51449             this.loadMask = new Roo.LoadMask(this.container,
51450                     Roo.apply({store:this.dataSource}, this.loadMask));
51451         }
51452         
51453         
51454         if (this.toolbar && this.toolbar.xtype) {
51455             this.toolbar.container = this.getView().getHeaderPanel(true);
51456             this.toolbar = new Roo.Toolbar(this.toolbar);
51457         }
51458         if (this.footer && this.footer.xtype) {
51459             this.footer.dataSource = this.getDataSource();
51460             this.footer.container = this.getView().getFooterPanel(true);
51461             this.footer = Roo.factory(this.footer, Roo);
51462         }
51463         if (this.dropTarget && this.dropTarget.xtype) {
51464             delete this.dropTarget.xtype;
51465             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51466         }
51467         
51468         
51469         this.rendered = true;
51470         this.fireEvent('render', this);
51471         return this;
51472     },
51473
51474         /**
51475          * Reconfigures the grid to use a different Store and Column Model.
51476          * The View will be bound to the new objects and refreshed.
51477          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51478          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51479          */
51480     reconfigure : function(dataSource, colModel){
51481         if(this.loadMask){
51482             this.loadMask.destroy();
51483             this.loadMask = new Roo.LoadMask(this.container,
51484                     Roo.apply({store:dataSource}, this.loadMask));
51485         }
51486         this.view.bind(dataSource, colModel);
51487         this.dataSource = dataSource;
51488         this.colModel = colModel;
51489         this.view.refresh(true);
51490     },
51491
51492     // private
51493     onKeyDown : function(e){
51494         this.fireEvent("keydown", e);
51495     },
51496
51497     /**
51498      * Destroy this grid.
51499      * @param {Boolean} removeEl True to remove the element
51500      */
51501     destroy : function(removeEl, keepListeners){
51502         if(this.loadMask){
51503             this.loadMask.destroy();
51504         }
51505         var c = this.container;
51506         c.removeAllListeners();
51507         this.view.destroy();
51508         this.colModel.purgeListeners();
51509         if(!keepListeners){
51510             this.purgeListeners();
51511         }
51512         c.update("");
51513         if(removeEl === true){
51514             c.remove();
51515         }
51516     },
51517
51518     // private
51519     processEvent : function(name, e){
51520         // does this fire select???
51521         Roo.log('grid:processEvent '  + name);
51522         
51523         if (name != 'touchstart' ) {
51524             this.fireEvent(name, e);    
51525         }
51526         
51527         var t = e.getTarget();
51528         var v = this.view;
51529         var header = v.findHeaderIndex(t);
51530         if(header !== false){
51531             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
51532         }else{
51533             var row = v.findRowIndex(t);
51534             var cell = v.findCellIndex(t);
51535             if (name == 'touchstart') {
51536                 // first touch is always a click.
51537                 // hopefull this happens after selection is updated.?
51538                 name = false;
51539                 
51540                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51541                     var cs = this.selModel.getSelectedCell();
51542                     if (row == cs[0] && cell == cs[1]){
51543                         name = 'dblclick';
51544                     }
51545                 }
51546                 if (typeof(this.selModel.getSelections) != 'undefined') {
51547                     var cs = this.selModel.getSelections();
51548                     var ds = this.dataSource;
51549                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51550                         name = 'dblclick';
51551                     }
51552                 }
51553                 if (!name) {
51554                     return;
51555                 }
51556             }
51557             
51558             
51559             if(row !== false){
51560                 this.fireEvent("row" + name, this, row, e);
51561                 if(cell !== false){
51562                     this.fireEvent("cell" + name, this, row, cell, e);
51563                 }
51564             }
51565         }
51566     },
51567
51568     // private
51569     onClick : function(e){
51570         this.processEvent("click", e);
51571     },
51572    // private
51573     onTouchStart : function(e){
51574         this.processEvent("touchstart", e);
51575     },
51576
51577     // private
51578     onContextMenu : function(e, t){
51579         this.processEvent("contextmenu", e);
51580     },
51581
51582     // private
51583     onDblClick : function(e){
51584         this.processEvent("dblclick", e);
51585     },
51586
51587     // private
51588     walkCells : function(row, col, step, fn, scope){
51589         var cm = this.colModel, clen = cm.getColumnCount();
51590         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51591         if(step < 0){
51592             if(col < 0){
51593                 row--;
51594                 first = false;
51595             }
51596             while(row >= 0){
51597                 if(!first){
51598                     col = clen-1;
51599                 }
51600                 first = false;
51601                 while(col >= 0){
51602                     if(fn.call(scope || this, row, col, cm) === true){
51603                         return [row, col];
51604                     }
51605                     col--;
51606                 }
51607                 row--;
51608             }
51609         } else {
51610             if(col >= clen){
51611                 row++;
51612                 first = false;
51613             }
51614             while(row < rlen){
51615                 if(!first){
51616                     col = 0;
51617                 }
51618                 first = false;
51619                 while(col < clen){
51620                     if(fn.call(scope || this, row, col, cm) === true){
51621                         return [row, col];
51622                     }
51623                     col++;
51624                 }
51625                 row++;
51626             }
51627         }
51628         return null;
51629     },
51630
51631     // private
51632     getSelections : function(){
51633         return this.selModel.getSelections();
51634     },
51635
51636     /**
51637      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
51638      * but if manual update is required this method will initiate it.
51639      */
51640     autoSize : function(){
51641         if(this.rendered){
51642             this.view.layout();
51643             if(this.view.adjustForScroll){
51644                 this.view.adjustForScroll();
51645             }
51646         }
51647     },
51648
51649     /**
51650      * Returns the grid's underlying element.
51651      * @return {Element} The element
51652      */
51653     getGridEl : function(){
51654         return this.container;
51655     },
51656
51657     // private for compatibility, overridden by editor grid
51658     stopEditing : function(){},
51659
51660     /**
51661      * Returns the grid's SelectionModel.
51662      * @return {SelectionModel}
51663      */
51664     getSelectionModel : function(){
51665         if(!this.selModel){
51666             this.selModel = new Roo.grid.RowSelectionModel();
51667         }
51668         return this.selModel;
51669     },
51670
51671     /**
51672      * Returns the grid's DataSource.
51673      * @return {DataSource}
51674      */
51675     getDataSource : function(){
51676         return this.dataSource;
51677     },
51678
51679     /**
51680      * Returns the grid's ColumnModel.
51681      * @return {ColumnModel}
51682      */
51683     getColumnModel : function(){
51684         return this.colModel;
51685     },
51686
51687     /**
51688      * Returns the grid's GridView object.
51689      * @return {GridView}
51690      */
51691     getView : function(){
51692         if(!this.view){
51693             this.view = new Roo.grid.GridView(this.viewConfig);
51694         }
51695         return this.view;
51696     },
51697     /**
51698      * Called to get grid's drag proxy text, by default returns this.ddText.
51699      * @return {String}
51700      */
51701     getDragDropText : function(){
51702         var count = this.selModel.getCount();
51703         return String.format(this.ddText, count, count == 1 ? '' : 's');
51704     }
51705 });
51706 /**
51707  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
51708  * %0 is replaced with the number of selected rows.
51709  * @type String
51710  */
51711 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51712  * Based on:
51713  * Ext JS Library 1.1.1
51714  * Copyright(c) 2006-2007, Ext JS, LLC.
51715  *
51716  * Originally Released Under LGPL - original licence link has changed is not relivant.
51717  *
51718  * Fork - LGPL
51719  * <script type="text/javascript">
51720  */
51721  
51722 Roo.grid.AbstractGridView = function(){
51723         this.grid = null;
51724         
51725         this.events = {
51726             "beforerowremoved" : true,
51727             "beforerowsinserted" : true,
51728             "beforerefresh" : true,
51729             "rowremoved" : true,
51730             "rowsinserted" : true,
51731             "rowupdated" : true,
51732             "refresh" : true
51733         };
51734     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51735 };
51736
51737 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51738     rowClass : "x-grid-row",
51739     cellClass : "x-grid-cell",
51740     tdClass : "x-grid-td",
51741     hdClass : "x-grid-hd",
51742     splitClass : "x-grid-hd-split",
51743     
51744         init: function(grid){
51745         this.grid = grid;
51746                 var cid = this.grid.getGridEl().id;
51747         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51748         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51749         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51750         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51751         },
51752         
51753         getColumnRenderers : function(){
51754         var renderers = [];
51755         var cm = this.grid.colModel;
51756         var colCount = cm.getColumnCount();
51757         for(var i = 0; i < colCount; i++){
51758             renderers[i] = cm.getRenderer(i);
51759         }
51760         return renderers;
51761     },
51762     
51763     getColumnIds : function(){
51764         var ids = [];
51765         var cm = this.grid.colModel;
51766         var colCount = cm.getColumnCount();
51767         for(var i = 0; i < colCount; i++){
51768             ids[i] = cm.getColumnId(i);
51769         }
51770         return ids;
51771     },
51772     
51773     getDataIndexes : function(){
51774         if(!this.indexMap){
51775             this.indexMap = this.buildIndexMap();
51776         }
51777         return this.indexMap.colToData;
51778     },
51779     
51780     getColumnIndexByDataIndex : function(dataIndex){
51781         if(!this.indexMap){
51782             this.indexMap = this.buildIndexMap();
51783         }
51784         return this.indexMap.dataToCol[dataIndex];
51785     },
51786     
51787     /**
51788      * Set a css style for a column dynamically. 
51789      * @param {Number} colIndex The index of the column
51790      * @param {String} name The css property name
51791      * @param {String} value The css value
51792      */
51793     setCSSStyle : function(colIndex, name, value){
51794         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51795         Roo.util.CSS.updateRule(selector, name, value);
51796     },
51797     
51798     generateRules : function(cm){
51799         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51800         Roo.util.CSS.removeStyleSheet(rulesId);
51801         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51802             var cid = cm.getColumnId(i);
51803             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51804                          this.tdSelector, cid, " {\n}\n",
51805                          this.hdSelector, cid, " {\n}\n",
51806                          this.splitSelector, cid, " {\n}\n");
51807         }
51808         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51809     }
51810 });/*
51811  * Based on:
51812  * Ext JS Library 1.1.1
51813  * Copyright(c) 2006-2007, Ext JS, LLC.
51814  *
51815  * Originally Released Under LGPL - original licence link has changed is not relivant.
51816  *
51817  * Fork - LGPL
51818  * <script type="text/javascript">
51819  */
51820
51821 // private
51822 // This is a support class used internally by the Grid components
51823 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51824     this.grid = grid;
51825     this.view = grid.getView();
51826     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51827     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51828     if(hd2){
51829         this.setHandleElId(Roo.id(hd));
51830         this.setOuterHandleElId(Roo.id(hd2));
51831     }
51832     this.scroll = false;
51833 };
51834 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51835     maxDragWidth: 120,
51836     getDragData : function(e){
51837         var t = Roo.lib.Event.getTarget(e);
51838         var h = this.view.findHeaderCell(t);
51839         if(h){
51840             return {ddel: h.firstChild, header:h};
51841         }
51842         return false;
51843     },
51844
51845     onInitDrag : function(e){
51846         this.view.headersDisabled = true;
51847         var clone = this.dragData.ddel.cloneNode(true);
51848         clone.id = Roo.id();
51849         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51850         this.proxy.update(clone);
51851         return true;
51852     },
51853
51854     afterValidDrop : function(){
51855         var v = this.view;
51856         setTimeout(function(){
51857             v.headersDisabled = false;
51858         }, 50);
51859     },
51860
51861     afterInvalidDrop : function(){
51862         var v = this.view;
51863         setTimeout(function(){
51864             v.headersDisabled = false;
51865         }, 50);
51866     }
51867 });
51868 /*
51869  * Based on:
51870  * Ext JS Library 1.1.1
51871  * Copyright(c) 2006-2007, Ext JS, LLC.
51872  *
51873  * Originally Released Under LGPL - original licence link has changed is not relivant.
51874  *
51875  * Fork - LGPL
51876  * <script type="text/javascript">
51877  */
51878 // private
51879 // This is a support class used internally by the Grid components
51880 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51881     this.grid = grid;
51882     this.view = grid.getView();
51883     // split the proxies so they don't interfere with mouse events
51884     this.proxyTop = Roo.DomHelper.append(document.body, {
51885         cls:"col-move-top", html:"&#160;"
51886     }, true);
51887     this.proxyBottom = Roo.DomHelper.append(document.body, {
51888         cls:"col-move-bottom", html:"&#160;"
51889     }, true);
51890     this.proxyTop.hide = this.proxyBottom.hide = function(){
51891         this.setLeftTop(-100,-100);
51892         this.setStyle("visibility", "hidden");
51893     };
51894     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51895     // temporarily disabled
51896     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51897     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51898 };
51899 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51900     proxyOffsets : [-4, -9],
51901     fly: Roo.Element.fly,
51902
51903     getTargetFromEvent : function(e){
51904         var t = Roo.lib.Event.getTarget(e);
51905         var cindex = this.view.findCellIndex(t);
51906         if(cindex !== false){
51907             return this.view.getHeaderCell(cindex);
51908         }
51909         return null;
51910     },
51911
51912     nextVisible : function(h){
51913         var v = this.view, cm = this.grid.colModel;
51914         h = h.nextSibling;
51915         while(h){
51916             if(!cm.isHidden(v.getCellIndex(h))){
51917                 return h;
51918             }
51919             h = h.nextSibling;
51920         }
51921         return null;
51922     },
51923
51924     prevVisible : function(h){
51925         var v = this.view, cm = this.grid.colModel;
51926         h = h.prevSibling;
51927         while(h){
51928             if(!cm.isHidden(v.getCellIndex(h))){
51929                 return h;
51930             }
51931             h = h.prevSibling;
51932         }
51933         return null;
51934     },
51935
51936     positionIndicator : function(h, n, e){
51937         var x = Roo.lib.Event.getPageX(e);
51938         var r = Roo.lib.Dom.getRegion(n.firstChild);
51939         var px, pt, py = r.top + this.proxyOffsets[1];
51940         if((r.right - x) <= (r.right-r.left)/2){
51941             px = r.right+this.view.borderWidth;
51942             pt = "after";
51943         }else{
51944             px = r.left;
51945             pt = "before";
51946         }
51947         var oldIndex = this.view.getCellIndex(h);
51948         var newIndex = this.view.getCellIndex(n);
51949
51950         if(this.grid.colModel.isFixed(newIndex)){
51951             return false;
51952         }
51953
51954         var locked = this.grid.colModel.isLocked(newIndex);
51955
51956         if(pt == "after"){
51957             newIndex++;
51958         }
51959         if(oldIndex < newIndex){
51960             newIndex--;
51961         }
51962         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51963             return false;
51964         }
51965         px +=  this.proxyOffsets[0];
51966         this.proxyTop.setLeftTop(px, py);
51967         this.proxyTop.show();
51968         if(!this.bottomOffset){
51969             this.bottomOffset = this.view.mainHd.getHeight();
51970         }
51971         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51972         this.proxyBottom.show();
51973         return pt;
51974     },
51975
51976     onNodeEnter : function(n, dd, e, data){
51977         if(data.header != n){
51978             this.positionIndicator(data.header, n, e);
51979         }
51980     },
51981
51982     onNodeOver : function(n, dd, e, data){
51983         var result = false;
51984         if(data.header != n){
51985             result = this.positionIndicator(data.header, n, e);
51986         }
51987         if(!result){
51988             this.proxyTop.hide();
51989             this.proxyBottom.hide();
51990         }
51991         return result ? this.dropAllowed : this.dropNotAllowed;
51992     },
51993
51994     onNodeOut : function(n, dd, e, data){
51995         this.proxyTop.hide();
51996         this.proxyBottom.hide();
51997     },
51998
51999     onNodeDrop : function(n, dd, e, data){
52000         var h = data.header;
52001         if(h != n){
52002             var cm = this.grid.colModel;
52003             var x = Roo.lib.Event.getPageX(e);
52004             var r = Roo.lib.Dom.getRegion(n.firstChild);
52005             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52006             var oldIndex = this.view.getCellIndex(h);
52007             var newIndex = this.view.getCellIndex(n);
52008             var locked = cm.isLocked(newIndex);
52009             if(pt == "after"){
52010                 newIndex++;
52011             }
52012             if(oldIndex < newIndex){
52013                 newIndex--;
52014             }
52015             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52016                 return false;
52017             }
52018             cm.setLocked(oldIndex, locked, true);
52019             cm.moveColumn(oldIndex, newIndex);
52020             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52021             return true;
52022         }
52023         return false;
52024     }
52025 });
52026 /*
52027  * Based on:
52028  * Ext JS Library 1.1.1
52029  * Copyright(c) 2006-2007, Ext JS, LLC.
52030  *
52031  * Originally Released Under LGPL - original licence link has changed is not relivant.
52032  *
52033  * Fork - LGPL
52034  * <script type="text/javascript">
52035  */
52036   
52037 /**
52038  * @class Roo.grid.GridView
52039  * @extends Roo.util.Observable
52040  *
52041  * @constructor
52042  * @param {Object} config
52043  */
52044 Roo.grid.GridView = function(config){
52045     Roo.grid.GridView.superclass.constructor.call(this);
52046     this.el = null;
52047
52048     Roo.apply(this, config);
52049 };
52050
52051 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52052
52053     unselectable :  'unselectable="on"',
52054     unselectableCls :  'x-unselectable',
52055     
52056     
52057     rowClass : "x-grid-row",
52058
52059     cellClass : "x-grid-col",
52060
52061     tdClass : "x-grid-td",
52062
52063     hdClass : "x-grid-hd",
52064
52065     splitClass : "x-grid-split",
52066
52067     sortClasses : ["sort-asc", "sort-desc"],
52068
52069     enableMoveAnim : false,
52070
52071     hlColor: "C3DAF9",
52072
52073     dh : Roo.DomHelper,
52074
52075     fly : Roo.Element.fly,
52076
52077     css : Roo.util.CSS,
52078
52079     borderWidth: 1,
52080
52081     splitOffset: 3,
52082
52083     scrollIncrement : 22,
52084
52085     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52086
52087     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52088
52089     bind : function(ds, cm){
52090         if(this.ds){
52091             this.ds.un("load", this.onLoad, this);
52092             this.ds.un("datachanged", this.onDataChange, this);
52093             this.ds.un("add", this.onAdd, this);
52094             this.ds.un("remove", this.onRemove, this);
52095             this.ds.un("update", this.onUpdate, this);
52096             this.ds.un("clear", this.onClear, this);
52097         }
52098         if(ds){
52099             ds.on("load", this.onLoad, this);
52100             ds.on("datachanged", this.onDataChange, this);
52101             ds.on("add", this.onAdd, this);
52102             ds.on("remove", this.onRemove, this);
52103             ds.on("update", this.onUpdate, this);
52104             ds.on("clear", this.onClear, this);
52105         }
52106         this.ds = ds;
52107
52108         if(this.cm){
52109             this.cm.un("widthchange", this.onColWidthChange, this);
52110             this.cm.un("headerchange", this.onHeaderChange, this);
52111             this.cm.un("hiddenchange", this.onHiddenChange, this);
52112             this.cm.un("columnmoved", this.onColumnMove, this);
52113             this.cm.un("columnlockchange", this.onColumnLock, this);
52114         }
52115         if(cm){
52116             this.generateRules(cm);
52117             cm.on("widthchange", this.onColWidthChange, this);
52118             cm.on("headerchange", this.onHeaderChange, this);
52119             cm.on("hiddenchange", this.onHiddenChange, this);
52120             cm.on("columnmoved", this.onColumnMove, this);
52121             cm.on("columnlockchange", this.onColumnLock, this);
52122         }
52123         this.cm = cm;
52124     },
52125
52126     init: function(grid){
52127         Roo.grid.GridView.superclass.init.call(this, grid);
52128
52129         this.bind(grid.dataSource, grid.colModel);
52130
52131         grid.on("headerclick", this.handleHeaderClick, this);
52132
52133         if(grid.trackMouseOver){
52134             grid.on("mouseover", this.onRowOver, this);
52135             grid.on("mouseout", this.onRowOut, this);
52136         }
52137         grid.cancelTextSelection = function(){};
52138         this.gridId = grid.id;
52139
52140         var tpls = this.templates || {};
52141
52142         if(!tpls.master){
52143             tpls.master = new Roo.Template(
52144                '<div class="x-grid" hidefocus="true">',
52145                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52146                   '<div class="x-grid-topbar"></div>',
52147                   '<div class="x-grid-scroller"><div></div></div>',
52148                   '<div class="x-grid-locked">',
52149                       '<div class="x-grid-header">{lockedHeader}</div>',
52150                       '<div class="x-grid-body">{lockedBody}</div>',
52151                   "</div>",
52152                   '<div class="x-grid-viewport">',
52153                       '<div class="x-grid-header">{header}</div>',
52154                       '<div class="x-grid-body">{body}</div>',
52155                   "</div>",
52156                   '<div class="x-grid-bottombar"></div>',
52157                  
52158                   '<div class="x-grid-resize-proxy">&#160;</div>',
52159                "</div>"
52160             );
52161             tpls.master.disableformats = true;
52162         }
52163
52164         if(!tpls.header){
52165             tpls.header = new Roo.Template(
52166                '<table border="0" cellspacing="0" cellpadding="0">',
52167                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52168                "</table>{splits}"
52169             );
52170             tpls.header.disableformats = true;
52171         }
52172         tpls.header.compile();
52173
52174         if(!tpls.hcell){
52175             tpls.hcell = new Roo.Template(
52176                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52177                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52178                 "</div></td>"
52179              );
52180              tpls.hcell.disableFormats = true;
52181         }
52182         tpls.hcell.compile();
52183
52184         if(!tpls.hsplit){
52185             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52186                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52187             tpls.hsplit.disableFormats = true;
52188         }
52189         tpls.hsplit.compile();
52190
52191         if(!tpls.body){
52192             tpls.body = new Roo.Template(
52193                '<table border="0" cellspacing="0" cellpadding="0">',
52194                "<tbody>{rows}</tbody>",
52195                "</table>"
52196             );
52197             tpls.body.disableFormats = true;
52198         }
52199         tpls.body.compile();
52200
52201         if(!tpls.row){
52202             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52203             tpls.row.disableFormats = true;
52204         }
52205         tpls.row.compile();
52206
52207         if(!tpls.cell){
52208             tpls.cell = new Roo.Template(
52209                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52210                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52211                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52212                 "</td>"
52213             );
52214             tpls.cell.disableFormats = true;
52215         }
52216         tpls.cell.compile();
52217
52218         this.templates = tpls;
52219     },
52220
52221     // remap these for backwards compat
52222     onColWidthChange : function(){
52223         this.updateColumns.apply(this, arguments);
52224     },
52225     onHeaderChange : function(){
52226         this.updateHeaders.apply(this, arguments);
52227     }, 
52228     onHiddenChange : function(){
52229         this.handleHiddenChange.apply(this, arguments);
52230     },
52231     onColumnMove : function(){
52232         this.handleColumnMove.apply(this, arguments);
52233     },
52234     onColumnLock : function(){
52235         this.handleLockChange.apply(this, arguments);
52236     },
52237
52238     onDataChange : function(){
52239         this.refresh();
52240         this.updateHeaderSortState();
52241     },
52242
52243     onClear : function(){
52244         this.refresh();
52245     },
52246
52247     onUpdate : function(ds, record){
52248         this.refreshRow(record);
52249     },
52250
52251     refreshRow : function(record){
52252         var ds = this.ds, index;
52253         if(typeof record == 'number'){
52254             index = record;
52255             record = ds.getAt(index);
52256         }else{
52257             index = ds.indexOf(record);
52258         }
52259         this.insertRows(ds, index, index, true);
52260         this.onRemove(ds, record, index+1, true);
52261         this.syncRowHeights(index, index);
52262         this.layout();
52263         this.fireEvent("rowupdated", this, index, record);
52264     },
52265
52266     onAdd : function(ds, records, index){
52267         this.insertRows(ds, index, index + (records.length-1));
52268     },
52269
52270     onRemove : function(ds, record, index, isUpdate){
52271         if(isUpdate !== true){
52272             this.fireEvent("beforerowremoved", this, index, record);
52273         }
52274         var bt = this.getBodyTable(), lt = this.getLockedTable();
52275         if(bt.rows[index]){
52276             bt.firstChild.removeChild(bt.rows[index]);
52277         }
52278         if(lt.rows[index]){
52279             lt.firstChild.removeChild(lt.rows[index]);
52280         }
52281         if(isUpdate !== true){
52282             this.stripeRows(index);
52283             this.syncRowHeights(index, index);
52284             this.layout();
52285             this.fireEvent("rowremoved", this, index, record);
52286         }
52287     },
52288
52289     onLoad : function(){
52290         this.scrollToTop();
52291     },
52292
52293     /**
52294      * Scrolls the grid to the top
52295      */
52296     scrollToTop : function(){
52297         if(this.scroller){
52298             this.scroller.dom.scrollTop = 0;
52299             this.syncScroll();
52300         }
52301     },
52302
52303     /**
52304      * Gets a panel in the header of the grid that can be used for toolbars etc.
52305      * After modifying the contents of this panel a call to grid.autoSize() may be
52306      * required to register any changes in size.
52307      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52308      * @return Roo.Element
52309      */
52310     getHeaderPanel : function(doShow){
52311         if(doShow){
52312             this.headerPanel.show();
52313         }
52314         return this.headerPanel;
52315     },
52316
52317     /**
52318      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52319      * After modifying the contents of this panel a call to grid.autoSize() may be
52320      * required to register any changes in size.
52321      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52322      * @return Roo.Element
52323      */
52324     getFooterPanel : function(doShow){
52325         if(doShow){
52326             this.footerPanel.show();
52327         }
52328         return this.footerPanel;
52329     },
52330
52331     initElements : function(){
52332         var E = Roo.Element;
52333         var el = this.grid.getGridEl().dom.firstChild;
52334         var cs = el.childNodes;
52335
52336         this.el = new E(el);
52337         
52338          this.focusEl = new E(el.firstChild);
52339         this.focusEl.swallowEvent("click", true);
52340         
52341         this.headerPanel = new E(cs[1]);
52342         this.headerPanel.enableDisplayMode("block");
52343
52344         this.scroller = new E(cs[2]);
52345         this.scrollSizer = new E(this.scroller.dom.firstChild);
52346
52347         this.lockedWrap = new E(cs[3]);
52348         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52349         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52350
52351         this.mainWrap = new E(cs[4]);
52352         this.mainHd = new E(this.mainWrap.dom.firstChild);
52353         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52354
52355         this.footerPanel = new E(cs[5]);
52356         this.footerPanel.enableDisplayMode("block");
52357
52358         this.resizeProxy = new E(cs[6]);
52359
52360         this.headerSelector = String.format(
52361            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52362            this.lockedHd.id, this.mainHd.id
52363         );
52364
52365         this.splitterSelector = String.format(
52366            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52367            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52368         );
52369     },
52370     idToCssName : function(s)
52371     {
52372         return s.replace(/[^a-z0-9]+/ig, '-');
52373     },
52374
52375     getHeaderCell : function(index){
52376         return Roo.DomQuery.select(this.headerSelector)[index];
52377     },
52378
52379     getHeaderCellMeasure : function(index){
52380         return this.getHeaderCell(index).firstChild;
52381     },
52382
52383     getHeaderCellText : function(index){
52384         return this.getHeaderCell(index).firstChild.firstChild;
52385     },
52386
52387     getLockedTable : function(){
52388         return this.lockedBody.dom.firstChild;
52389     },
52390
52391     getBodyTable : function(){
52392         return this.mainBody.dom.firstChild;
52393     },
52394
52395     getLockedRow : function(index){
52396         return this.getLockedTable().rows[index];
52397     },
52398
52399     getRow : function(index){
52400         return this.getBodyTable().rows[index];
52401     },
52402
52403     getRowComposite : function(index){
52404         if(!this.rowEl){
52405             this.rowEl = new Roo.CompositeElementLite();
52406         }
52407         var els = [], lrow, mrow;
52408         if(lrow = this.getLockedRow(index)){
52409             els.push(lrow);
52410         }
52411         if(mrow = this.getRow(index)){
52412             els.push(mrow);
52413         }
52414         this.rowEl.elements = els;
52415         return this.rowEl;
52416     },
52417     /**
52418      * Gets the 'td' of the cell
52419      * 
52420      * @param {Integer} rowIndex row to select
52421      * @param {Integer} colIndex column to select
52422      * 
52423      * @return {Object} 
52424      */
52425     getCell : function(rowIndex, colIndex){
52426         var locked = this.cm.getLockedCount();
52427         var source;
52428         if(colIndex < locked){
52429             source = this.lockedBody.dom.firstChild;
52430         }else{
52431             source = this.mainBody.dom.firstChild;
52432             colIndex -= locked;
52433         }
52434         return source.rows[rowIndex].childNodes[colIndex];
52435     },
52436
52437     getCellText : function(rowIndex, colIndex){
52438         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52439     },
52440
52441     getCellBox : function(cell){
52442         var b = this.fly(cell).getBox();
52443         if(Roo.isOpera){ // opera fails to report the Y
52444             b.y = cell.offsetTop + this.mainBody.getY();
52445         }
52446         return b;
52447     },
52448
52449     getCellIndex : function(cell){
52450         var id = String(cell.className).match(this.cellRE);
52451         if(id){
52452             return parseInt(id[1], 10);
52453         }
52454         return 0;
52455     },
52456
52457     findHeaderIndex : function(n){
52458         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52459         return r ? this.getCellIndex(r) : false;
52460     },
52461
52462     findHeaderCell : function(n){
52463         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52464         return r ? r : false;
52465     },
52466
52467     findRowIndex : function(n){
52468         if(!n){
52469             return false;
52470         }
52471         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52472         return r ? r.rowIndex : false;
52473     },
52474
52475     findCellIndex : function(node){
52476         var stop = this.el.dom;
52477         while(node && node != stop){
52478             if(this.findRE.test(node.className)){
52479                 return this.getCellIndex(node);
52480             }
52481             node = node.parentNode;
52482         }
52483         return false;
52484     },
52485
52486     getColumnId : function(index){
52487         return this.cm.getColumnId(index);
52488     },
52489
52490     getSplitters : function()
52491     {
52492         if(this.splitterSelector){
52493            return Roo.DomQuery.select(this.splitterSelector);
52494         }else{
52495             return null;
52496       }
52497     },
52498
52499     getSplitter : function(index){
52500         return this.getSplitters()[index];
52501     },
52502
52503     onRowOver : function(e, t){
52504         var row;
52505         if((row = this.findRowIndex(t)) !== false){
52506             this.getRowComposite(row).addClass("x-grid-row-over");
52507         }
52508     },
52509
52510     onRowOut : function(e, t){
52511         var row;
52512         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52513             this.getRowComposite(row).removeClass("x-grid-row-over");
52514         }
52515     },
52516
52517     renderHeaders : function(){
52518         var cm = this.cm;
52519         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52520         var cb = [], lb = [], sb = [], lsb = [], p = {};
52521         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52522             p.cellId = "x-grid-hd-0-" + i;
52523             p.splitId = "x-grid-csplit-0-" + i;
52524             p.id = cm.getColumnId(i);
52525             p.title = cm.getColumnTooltip(i) || "";
52526             p.value = cm.getColumnHeader(i) || "";
52527             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52528             if(!cm.isLocked(i)){
52529                 cb[cb.length] = ct.apply(p);
52530                 sb[sb.length] = st.apply(p);
52531             }else{
52532                 lb[lb.length] = ct.apply(p);
52533                 lsb[lsb.length] = st.apply(p);
52534             }
52535         }
52536         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52537                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52538     },
52539
52540     updateHeaders : function(){
52541         var html = this.renderHeaders();
52542         this.lockedHd.update(html[0]);
52543         this.mainHd.update(html[1]);
52544     },
52545
52546     /**
52547      * Focuses the specified row.
52548      * @param {Number} row The row index
52549      */
52550     focusRow : function(row)
52551     {
52552         //Roo.log('GridView.focusRow');
52553         var x = this.scroller.dom.scrollLeft;
52554         this.focusCell(row, 0, false);
52555         this.scroller.dom.scrollLeft = x;
52556     },
52557
52558     /**
52559      * Focuses the specified cell.
52560      * @param {Number} row The row index
52561      * @param {Number} col The column index
52562      * @param {Boolean} hscroll false to disable horizontal scrolling
52563      */
52564     focusCell : function(row, col, hscroll)
52565     {
52566         //Roo.log('GridView.focusCell');
52567         var el = this.ensureVisible(row, col, hscroll);
52568         this.focusEl.alignTo(el, "tl-tl");
52569         if(Roo.isGecko){
52570             this.focusEl.focus();
52571         }else{
52572             this.focusEl.focus.defer(1, this.focusEl);
52573         }
52574     },
52575
52576     /**
52577      * Scrolls the specified cell into view
52578      * @param {Number} row The row index
52579      * @param {Number} col The column index
52580      * @param {Boolean} hscroll false to disable horizontal scrolling
52581      */
52582     ensureVisible : function(row, col, hscroll)
52583     {
52584         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52585         //return null; //disable for testing.
52586         if(typeof row != "number"){
52587             row = row.rowIndex;
52588         }
52589         if(row < 0 && row >= this.ds.getCount()){
52590             return  null;
52591         }
52592         col = (col !== undefined ? col : 0);
52593         var cm = this.grid.colModel;
52594         while(cm.isHidden(col)){
52595             col++;
52596         }
52597
52598         var el = this.getCell(row, col);
52599         if(!el){
52600             return null;
52601         }
52602         var c = this.scroller.dom;
52603
52604         var ctop = parseInt(el.offsetTop, 10);
52605         var cleft = parseInt(el.offsetLeft, 10);
52606         var cbot = ctop + el.offsetHeight;
52607         var cright = cleft + el.offsetWidth;
52608         
52609         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52610         var stop = parseInt(c.scrollTop, 10);
52611         var sleft = parseInt(c.scrollLeft, 10);
52612         var sbot = stop + ch;
52613         var sright = sleft + c.clientWidth;
52614         /*
52615         Roo.log('GridView.ensureVisible:' +
52616                 ' ctop:' + ctop +
52617                 ' c.clientHeight:' + c.clientHeight +
52618                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52619                 ' stop:' + stop +
52620                 ' cbot:' + cbot +
52621                 ' sbot:' + sbot +
52622                 ' ch:' + ch  
52623                 );
52624         */
52625         if(ctop < stop){
52626              c.scrollTop = ctop;
52627             //Roo.log("set scrolltop to ctop DISABLE?");
52628         }else if(cbot > sbot){
52629             //Roo.log("set scrolltop to cbot-ch");
52630             c.scrollTop = cbot-ch;
52631         }
52632         
52633         if(hscroll !== false){
52634             if(cleft < sleft){
52635                 c.scrollLeft = cleft;
52636             }else if(cright > sright){
52637                 c.scrollLeft = cright-c.clientWidth;
52638             }
52639         }
52640          
52641         return el;
52642     },
52643
52644     updateColumns : function(){
52645         this.grid.stopEditing();
52646         var cm = this.grid.colModel, colIds = this.getColumnIds();
52647         //var totalWidth = cm.getTotalWidth();
52648         var pos = 0;
52649         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52650             //if(cm.isHidden(i)) continue;
52651             var w = cm.getColumnWidth(i);
52652             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52653             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52654         }
52655         this.updateSplitters();
52656     },
52657
52658     generateRules : function(cm){
52659         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
52660         Roo.util.CSS.removeStyleSheet(rulesId);
52661         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52662             var cid = cm.getColumnId(i);
52663             var align = '';
52664             if(cm.config[i].align){
52665                 align = 'text-align:'+cm.config[i].align+';';
52666             }
52667             var hidden = '';
52668             if(cm.isHidden(i)){
52669                 hidden = 'display:none;';
52670             }
52671             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
52672             ruleBuf.push(
52673                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
52674                     this.hdSelector, cid, " {\n", align, width, "}\n",
52675                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
52676                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
52677         }
52678         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52679     },
52680
52681     updateSplitters : function(){
52682         var cm = this.cm, s = this.getSplitters();
52683         if(s){ // splitters not created yet
52684             var pos = 0, locked = true;
52685             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52686                 if(cm.isHidden(i)) continue;
52687                 var w = cm.getColumnWidth(i); // make sure it's a number
52688                 if(!cm.isLocked(i) && locked){
52689                     pos = 0;
52690                     locked = false;
52691                 }
52692                 pos += w;
52693                 s[i].style.left = (pos-this.splitOffset) + "px";
52694             }
52695         }
52696     },
52697
52698     handleHiddenChange : function(colModel, colIndex, hidden){
52699         if(hidden){
52700             this.hideColumn(colIndex);
52701         }else{
52702             this.unhideColumn(colIndex);
52703         }
52704     },
52705
52706     hideColumn : function(colIndex){
52707         var cid = this.getColumnId(colIndex);
52708         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
52709         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52710         if(Roo.isSafari){
52711             this.updateHeaders();
52712         }
52713         this.updateSplitters();
52714         this.layout();
52715     },
52716
52717     unhideColumn : function(colIndex){
52718         var cid = this.getColumnId(colIndex);
52719         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52720         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52721
52722         if(Roo.isSafari){
52723             this.updateHeaders();
52724         }
52725         this.updateSplitters();
52726         this.layout();
52727     },
52728
52729     insertRows : function(dm, firstRow, lastRow, isUpdate){
52730         if(firstRow == 0 && lastRow == dm.getCount()-1){
52731             this.refresh();
52732         }else{
52733             if(!isUpdate){
52734                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52735             }
52736             var s = this.getScrollState();
52737             var markup = this.renderRows(firstRow, lastRow);
52738             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52739             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52740             this.restoreScroll(s);
52741             if(!isUpdate){
52742                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52743                 this.syncRowHeights(firstRow, lastRow);
52744                 this.stripeRows(firstRow);
52745                 this.layout();
52746             }
52747         }
52748     },
52749
52750     bufferRows : function(markup, target, index){
52751         var before = null, trows = target.rows, tbody = target.tBodies[0];
52752         if(index < trows.length){
52753             before = trows[index];
52754         }
52755         var b = document.createElement("div");
52756         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52757         var rows = b.firstChild.rows;
52758         for(var i = 0, len = rows.length; i < len; i++){
52759             if(before){
52760                 tbody.insertBefore(rows[0], before);
52761             }else{
52762                 tbody.appendChild(rows[0]);
52763             }
52764         }
52765         b.innerHTML = "";
52766         b = null;
52767     },
52768
52769     deleteRows : function(dm, firstRow, lastRow){
52770         if(dm.getRowCount()<1){
52771             this.fireEvent("beforerefresh", this);
52772             this.mainBody.update("");
52773             this.lockedBody.update("");
52774             this.fireEvent("refresh", this);
52775         }else{
52776             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52777             var bt = this.getBodyTable();
52778             var tbody = bt.firstChild;
52779             var rows = bt.rows;
52780             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52781                 tbody.removeChild(rows[firstRow]);
52782             }
52783             this.stripeRows(firstRow);
52784             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52785         }
52786     },
52787
52788     updateRows : function(dataSource, firstRow, lastRow){
52789         var s = this.getScrollState();
52790         this.refresh();
52791         this.restoreScroll(s);
52792     },
52793
52794     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52795         if(!noRefresh){
52796            this.refresh();
52797         }
52798         this.updateHeaderSortState();
52799     },
52800
52801     getScrollState : function(){
52802         
52803         var sb = this.scroller.dom;
52804         return {left: sb.scrollLeft, top: sb.scrollTop};
52805     },
52806
52807     stripeRows : function(startRow){
52808         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52809             return;
52810         }
52811         startRow = startRow || 0;
52812         var rows = this.getBodyTable().rows;
52813         var lrows = this.getLockedTable().rows;
52814         var cls = ' x-grid-row-alt ';
52815         for(var i = startRow, len = rows.length; i < len; i++){
52816             var row = rows[i], lrow = lrows[i];
52817             var isAlt = ((i+1) % 2 == 0);
52818             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52819             if(isAlt == hasAlt){
52820                 continue;
52821             }
52822             if(isAlt){
52823                 row.className += " x-grid-row-alt";
52824             }else{
52825                 row.className = row.className.replace("x-grid-row-alt", "");
52826             }
52827             if(lrow){
52828                 lrow.className = row.className;
52829             }
52830         }
52831     },
52832
52833     restoreScroll : function(state){
52834         //Roo.log('GridView.restoreScroll');
52835         var sb = this.scroller.dom;
52836         sb.scrollLeft = state.left;
52837         sb.scrollTop = state.top;
52838         this.syncScroll();
52839     },
52840
52841     syncScroll : function(){
52842         //Roo.log('GridView.syncScroll');
52843         var sb = this.scroller.dom;
52844         var sh = this.mainHd.dom;
52845         var bs = this.mainBody.dom;
52846         var lv = this.lockedBody.dom;
52847         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52848         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52849     },
52850
52851     handleScroll : function(e){
52852         this.syncScroll();
52853         var sb = this.scroller.dom;
52854         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52855         e.stopEvent();
52856     },
52857
52858     handleWheel : function(e){
52859         var d = e.getWheelDelta();
52860         this.scroller.dom.scrollTop -= d*22;
52861         // set this here to prevent jumpy scrolling on large tables
52862         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52863         e.stopEvent();
52864     },
52865
52866     renderRows : function(startRow, endRow){
52867         // pull in all the crap needed to render rows
52868         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52869         var colCount = cm.getColumnCount();
52870
52871         if(ds.getCount() < 1){
52872             return ["", ""];
52873         }
52874
52875         // build a map for all the columns
52876         var cs = [];
52877         for(var i = 0; i < colCount; i++){
52878             var name = cm.getDataIndex(i);
52879             cs[i] = {
52880                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52881                 renderer : cm.getRenderer(i),
52882                 id : cm.getColumnId(i),
52883                 locked : cm.isLocked(i)
52884             };
52885         }
52886
52887         startRow = startRow || 0;
52888         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52889
52890         // records to render
52891         var rs = ds.getRange(startRow, endRow);
52892
52893         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52894     },
52895
52896     // As much as I hate to duplicate code, this was branched because FireFox really hates
52897     // [].join("") on strings. The performance difference was substantial enough to
52898     // branch this function
52899     doRender : Roo.isGecko ?
52900             function(cs, rs, ds, startRow, colCount, stripe){
52901                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52902                 // buffers
52903                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52904                 
52905                 var hasListener = this.grid.hasListener('rowclass');
52906                 var rowcfg = {};
52907                 for(var j = 0, len = rs.length; j < len; j++){
52908                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52909                     for(var i = 0; i < colCount; i++){
52910                         c = cs[i];
52911                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52912                         p.id = c.id;
52913                         p.css = p.attr = "";
52914                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52915                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52916                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52917                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52918                         }
52919                         var markup = ct.apply(p);
52920                         if(!c.locked){
52921                             cb+= markup;
52922                         }else{
52923                             lcb+= markup;
52924                         }
52925                     }
52926                     var alt = [];
52927                     if(stripe && ((rowIndex+1) % 2 == 0)){
52928                         alt.push("x-grid-row-alt")
52929                     }
52930                     if(r.dirty){
52931                         alt.push(  " x-grid-dirty-row");
52932                     }
52933                     rp.cells = lcb;
52934                     if(this.getRowClass){
52935                         alt.push(this.getRowClass(r, rowIndex));
52936                     }
52937                     if (hasListener) {
52938                         rowcfg = {
52939                              
52940                             record: r,
52941                             rowIndex : rowIndex,
52942                             rowClass : ''
52943                         }
52944                         this.grid.fireEvent('rowclass', this, rowcfg);
52945                         alt.push(rowcfg.rowClass);
52946                     }
52947                     rp.alt = alt.join(" ");
52948                     lbuf+= rt.apply(rp);
52949                     rp.cells = cb;
52950                     buf+=  rt.apply(rp);
52951                 }
52952                 return [lbuf, buf];
52953             } :
52954             function(cs, rs, ds, startRow, colCount, stripe){
52955                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52956                 // buffers
52957                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52958                 var hasListener = this.grid.hasListener('rowclass');
52959  
52960                 var rowcfg = {};
52961                 for(var j = 0, len = rs.length; j < len; j++){
52962                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52963                     for(var i = 0; i < colCount; i++){
52964                         c = cs[i];
52965                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52966                         p.id = c.id;
52967                         p.css = p.attr = "";
52968                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52969                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52970                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52971                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52972                         }
52973                         
52974                         var markup = ct.apply(p);
52975                         if(!c.locked){
52976                             cb[cb.length] = markup;
52977                         }else{
52978                             lcb[lcb.length] = markup;
52979                         }
52980                     }
52981                     var alt = [];
52982                     if(stripe && ((rowIndex+1) % 2 == 0)){
52983                         alt.push( "x-grid-row-alt");
52984                     }
52985                     if(r.dirty){
52986                         alt.push(" x-grid-dirty-row");
52987                     }
52988                     rp.cells = lcb;
52989                     if(this.getRowClass){
52990                         alt.push( this.getRowClass(r, rowIndex));
52991                     }
52992                     if (hasListener) {
52993                         rowcfg = {
52994                              
52995                             record: r,
52996                             rowIndex : rowIndex,
52997                             rowClass : ''
52998                         }
52999                         this.grid.fireEvent('rowclass', this, rowcfg);
53000                         alt.push(rowcfg.rowClass);
53001                     }
53002                     rp.alt = alt.join(" ");
53003                     rp.cells = lcb.join("");
53004                     lbuf[lbuf.length] = rt.apply(rp);
53005                     rp.cells = cb.join("");
53006                     buf[buf.length] =  rt.apply(rp);
53007                 }
53008                 return [lbuf.join(""), buf.join("")];
53009             },
53010
53011     renderBody : function(){
53012         var markup = this.renderRows();
53013         var bt = this.templates.body;
53014         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53015     },
53016
53017     /**
53018      * Refreshes the grid
53019      * @param {Boolean} headersToo
53020      */
53021     refresh : function(headersToo){
53022         this.fireEvent("beforerefresh", this);
53023         this.grid.stopEditing();
53024         var result = this.renderBody();
53025         this.lockedBody.update(result[0]);
53026         this.mainBody.update(result[1]);
53027         if(headersToo === true){
53028             this.updateHeaders();
53029             this.updateColumns();
53030             this.updateSplitters();
53031             this.updateHeaderSortState();
53032         }
53033         this.syncRowHeights();
53034         this.layout();
53035         this.fireEvent("refresh", this);
53036     },
53037
53038     handleColumnMove : function(cm, oldIndex, newIndex){
53039         this.indexMap = null;
53040         var s = this.getScrollState();
53041         this.refresh(true);
53042         this.restoreScroll(s);
53043         this.afterMove(newIndex);
53044     },
53045
53046     afterMove : function(colIndex){
53047         if(this.enableMoveAnim && Roo.enableFx){
53048             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53049         }
53050         // if multisort - fix sortOrder, and reload..
53051         if (this.grid.dataSource.multiSort) {
53052             // the we can call sort again..
53053             var dm = this.grid.dataSource;
53054             var cm = this.grid.colModel;
53055             var so = [];
53056             for(var i = 0; i < cm.config.length; i++ ) {
53057                 
53058                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53059                     continue; // dont' bother, it's not in sort list or being set.
53060                 }
53061                 
53062                 so.push(cm.config[i].dataIndex);
53063             };
53064             dm.sortOrder = so;
53065             dm.load(dm.lastOptions);
53066             
53067             
53068         }
53069         
53070     },
53071
53072     updateCell : function(dm, rowIndex, dataIndex){
53073         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53074         if(typeof colIndex == "undefined"){ // not present in grid
53075             return;
53076         }
53077         var cm = this.grid.colModel;
53078         var cell = this.getCell(rowIndex, colIndex);
53079         var cellText = this.getCellText(rowIndex, colIndex);
53080
53081         var p = {
53082             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53083             id : cm.getColumnId(colIndex),
53084             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53085         };
53086         var renderer = cm.getRenderer(colIndex);
53087         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53088         if(typeof val == "undefined" || val === "") val = "&#160;";
53089         cellText.innerHTML = val;
53090         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53091         this.syncRowHeights(rowIndex, rowIndex);
53092     },
53093
53094     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53095         var maxWidth = 0;
53096         if(this.grid.autoSizeHeaders){
53097             var h = this.getHeaderCellMeasure(colIndex);
53098             maxWidth = Math.max(maxWidth, h.scrollWidth);
53099         }
53100         var tb, index;
53101         if(this.cm.isLocked(colIndex)){
53102             tb = this.getLockedTable();
53103             index = colIndex;
53104         }else{
53105             tb = this.getBodyTable();
53106             index = colIndex - this.cm.getLockedCount();
53107         }
53108         if(tb && tb.rows){
53109             var rows = tb.rows;
53110             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53111             for(var i = 0; i < stopIndex; i++){
53112                 var cell = rows[i].childNodes[index].firstChild;
53113                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53114             }
53115         }
53116         return maxWidth + /*margin for error in IE*/ 5;
53117     },
53118     /**
53119      * Autofit a column to its content.
53120      * @param {Number} colIndex
53121      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53122      */
53123      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53124          if(this.cm.isHidden(colIndex)){
53125              return; // can't calc a hidden column
53126          }
53127         if(forceMinSize){
53128             var cid = this.cm.getColumnId(colIndex);
53129             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53130            if(this.grid.autoSizeHeaders){
53131                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53132            }
53133         }
53134         var newWidth = this.calcColumnWidth(colIndex);
53135         this.cm.setColumnWidth(colIndex,
53136             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53137         if(!suppressEvent){
53138             this.grid.fireEvent("columnresize", colIndex, newWidth);
53139         }
53140     },
53141
53142     /**
53143      * Autofits all columns to their content and then expands to fit any extra space in the grid
53144      */
53145      autoSizeColumns : function(){
53146         var cm = this.grid.colModel;
53147         var colCount = cm.getColumnCount();
53148         for(var i = 0; i < colCount; i++){
53149             this.autoSizeColumn(i, true, true);
53150         }
53151         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53152             this.fitColumns();
53153         }else{
53154             this.updateColumns();
53155             this.layout();
53156         }
53157     },
53158
53159     /**
53160      * Autofits all columns to the grid's width proportionate with their current size
53161      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53162      */
53163     fitColumns : function(reserveScrollSpace){
53164         var cm = this.grid.colModel;
53165         var colCount = cm.getColumnCount();
53166         var cols = [];
53167         var width = 0;
53168         var i, w;
53169         for (i = 0; i < colCount; i++){
53170             if(!cm.isHidden(i) && !cm.isFixed(i)){
53171                 w = cm.getColumnWidth(i);
53172                 cols.push(i);
53173                 cols.push(w);
53174                 width += w;
53175             }
53176         }
53177         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53178         if(reserveScrollSpace){
53179             avail -= 17;
53180         }
53181         var frac = (avail - cm.getTotalWidth())/width;
53182         while (cols.length){
53183             w = cols.pop();
53184             i = cols.pop();
53185             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53186         }
53187         this.updateColumns();
53188         this.layout();
53189     },
53190
53191     onRowSelect : function(rowIndex){
53192         var row = this.getRowComposite(rowIndex);
53193         row.addClass("x-grid-row-selected");
53194     },
53195
53196     onRowDeselect : function(rowIndex){
53197         var row = this.getRowComposite(rowIndex);
53198         row.removeClass("x-grid-row-selected");
53199     },
53200
53201     onCellSelect : function(row, col){
53202         var cell = this.getCell(row, col);
53203         if(cell){
53204             Roo.fly(cell).addClass("x-grid-cell-selected");
53205         }
53206     },
53207
53208     onCellDeselect : function(row, col){
53209         var cell = this.getCell(row, col);
53210         if(cell){
53211             Roo.fly(cell).removeClass("x-grid-cell-selected");
53212         }
53213     },
53214
53215     updateHeaderSortState : function(){
53216         
53217         // sort state can be single { field: xxx, direction : yyy}
53218         // or   { xxx=>ASC , yyy : DESC ..... }
53219         
53220         var mstate = {};
53221         if (!this.ds.multiSort) { 
53222             var state = this.ds.getSortState();
53223             if(!state){
53224                 return;
53225             }
53226             mstate[state.field] = state.direction;
53227             // FIXME... - this is not used here.. but might be elsewhere..
53228             this.sortState = state;
53229             
53230         } else {
53231             mstate = this.ds.sortToggle;
53232         }
53233         //remove existing sort classes..
53234         
53235         var sc = this.sortClasses;
53236         var hds = this.el.select(this.headerSelector).removeClass(sc);
53237         
53238         for(var f in mstate) {
53239         
53240             var sortColumn = this.cm.findColumnIndex(f);
53241             
53242             if(sortColumn != -1){
53243                 var sortDir = mstate[f];        
53244                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53245             }
53246         }
53247         
53248          
53249         
53250     },
53251
53252
53253     handleHeaderClick : function(g, index){
53254         if(this.headersDisabled){
53255             return;
53256         }
53257         var dm = g.dataSource, cm = g.colModel;
53258         if(!cm.isSortable(index)){
53259             return;
53260         }
53261         g.stopEditing();
53262         
53263         if (dm.multiSort) {
53264             // update the sortOrder
53265             var so = [];
53266             for(var i = 0; i < cm.config.length; i++ ) {
53267                 
53268                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53269                     continue; // dont' bother, it's not in sort list or being set.
53270                 }
53271                 
53272                 so.push(cm.config[i].dataIndex);
53273             };
53274             dm.sortOrder = so;
53275         }
53276         
53277         
53278         dm.sort(cm.getDataIndex(index));
53279     },
53280
53281
53282     destroy : function(){
53283         if(this.colMenu){
53284             this.colMenu.removeAll();
53285             Roo.menu.MenuMgr.unregister(this.colMenu);
53286             this.colMenu.getEl().remove();
53287             delete this.colMenu;
53288         }
53289         if(this.hmenu){
53290             this.hmenu.removeAll();
53291             Roo.menu.MenuMgr.unregister(this.hmenu);
53292             this.hmenu.getEl().remove();
53293             delete this.hmenu;
53294         }
53295         if(this.grid.enableColumnMove){
53296             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53297             if(dds){
53298                 for(var dd in dds){
53299                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53300                         var elid = dds[dd].dragElId;
53301                         dds[dd].unreg();
53302                         Roo.get(elid).remove();
53303                     } else if(dds[dd].config.isTarget){
53304                         dds[dd].proxyTop.remove();
53305                         dds[dd].proxyBottom.remove();
53306                         dds[dd].unreg();
53307                     }
53308                     if(Roo.dd.DDM.locationCache[dd]){
53309                         delete Roo.dd.DDM.locationCache[dd];
53310                     }
53311                 }
53312                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53313             }
53314         }
53315         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53316         this.bind(null, null);
53317         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53318     },
53319
53320     handleLockChange : function(){
53321         this.refresh(true);
53322     },
53323
53324     onDenyColumnLock : function(){
53325
53326     },
53327
53328     onDenyColumnHide : function(){
53329
53330     },
53331
53332     handleHdMenuClick : function(item){
53333         var index = this.hdCtxIndex;
53334         var cm = this.cm, ds = this.ds;
53335         switch(item.id){
53336             case "asc":
53337                 ds.sort(cm.getDataIndex(index), "ASC");
53338                 break;
53339             case "desc":
53340                 ds.sort(cm.getDataIndex(index), "DESC");
53341                 break;
53342             case "lock":
53343                 var lc = cm.getLockedCount();
53344                 if(cm.getColumnCount(true) <= lc+1){
53345                     this.onDenyColumnLock();
53346                     return;
53347                 }
53348                 if(lc != index){
53349                     cm.setLocked(index, true, true);
53350                     cm.moveColumn(index, lc);
53351                     this.grid.fireEvent("columnmove", index, lc);
53352                 }else{
53353                     cm.setLocked(index, true);
53354                 }
53355             break;
53356             case "unlock":
53357                 var lc = cm.getLockedCount();
53358                 if((lc-1) != index){
53359                     cm.setLocked(index, false, true);
53360                     cm.moveColumn(index, lc-1);
53361                     this.grid.fireEvent("columnmove", index, lc-1);
53362                 }else{
53363                     cm.setLocked(index, false);
53364                 }
53365             break;
53366             default:
53367                 index = cm.getIndexById(item.id.substr(4));
53368                 if(index != -1){
53369                     if(item.checked && cm.getColumnCount(true) <= 1){
53370                         this.onDenyColumnHide();
53371                         return false;
53372                     }
53373                     cm.setHidden(index, item.checked);
53374                 }
53375         }
53376         return true;
53377     },
53378
53379     beforeColMenuShow : function(){
53380         var cm = this.cm,  colCount = cm.getColumnCount();
53381         this.colMenu.removeAll();
53382         for(var i = 0; i < colCount; i++){
53383             this.colMenu.add(new Roo.menu.CheckItem({
53384                 id: "col-"+cm.getColumnId(i),
53385                 text: cm.getColumnHeader(i),
53386                 checked: !cm.isHidden(i),
53387                 hideOnClick:false
53388             }));
53389         }
53390     },
53391
53392     handleHdCtx : function(g, index, e){
53393         e.stopEvent();
53394         var hd = this.getHeaderCell(index);
53395         this.hdCtxIndex = index;
53396         var ms = this.hmenu.items, cm = this.cm;
53397         ms.get("asc").setDisabled(!cm.isSortable(index));
53398         ms.get("desc").setDisabled(!cm.isSortable(index));
53399         if(this.grid.enableColLock !== false){
53400             ms.get("lock").setDisabled(cm.isLocked(index));
53401             ms.get("unlock").setDisabled(!cm.isLocked(index));
53402         }
53403         this.hmenu.show(hd, "tl-bl");
53404     },
53405
53406     handleHdOver : function(e){
53407         var hd = this.findHeaderCell(e.getTarget());
53408         if(hd && !this.headersDisabled){
53409             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53410                this.fly(hd).addClass("x-grid-hd-over");
53411             }
53412         }
53413     },
53414
53415     handleHdOut : function(e){
53416         var hd = this.findHeaderCell(e.getTarget());
53417         if(hd){
53418             this.fly(hd).removeClass("x-grid-hd-over");
53419         }
53420     },
53421
53422     handleSplitDblClick : function(e, t){
53423         var i = this.getCellIndex(t);
53424         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53425             this.autoSizeColumn(i, true);
53426             this.layout();
53427         }
53428     },
53429
53430     render : function(){
53431
53432         var cm = this.cm;
53433         var colCount = cm.getColumnCount();
53434
53435         if(this.grid.monitorWindowResize === true){
53436             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53437         }
53438         var header = this.renderHeaders();
53439         var body = this.templates.body.apply({rows:""});
53440         var html = this.templates.master.apply({
53441             lockedBody: body,
53442             body: body,
53443             lockedHeader: header[0],
53444             header: header[1]
53445         });
53446
53447         //this.updateColumns();
53448
53449         this.grid.getGridEl().dom.innerHTML = html;
53450
53451         this.initElements();
53452         
53453         // a kludge to fix the random scolling effect in webkit
53454         this.el.on("scroll", function() {
53455             this.el.dom.scrollTop=0; // hopefully not recursive..
53456         },this);
53457
53458         this.scroller.on("scroll", this.handleScroll, this);
53459         this.lockedBody.on("mousewheel", this.handleWheel, this);
53460         this.mainBody.on("mousewheel", this.handleWheel, this);
53461
53462         this.mainHd.on("mouseover", this.handleHdOver, this);
53463         this.mainHd.on("mouseout", this.handleHdOut, this);
53464         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53465                 {delegate: "."+this.splitClass});
53466
53467         this.lockedHd.on("mouseover", this.handleHdOver, this);
53468         this.lockedHd.on("mouseout", this.handleHdOut, this);
53469         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53470                 {delegate: "."+this.splitClass});
53471
53472         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53473             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53474         }
53475
53476         this.updateSplitters();
53477
53478         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53479             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53480             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53481         }
53482
53483         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53484             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53485             this.hmenu.add(
53486                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53487                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53488             );
53489             if(this.grid.enableColLock !== false){
53490                 this.hmenu.add('-',
53491                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53492                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53493                 );
53494             }
53495             if(this.grid.enableColumnHide !== false){
53496
53497                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53498                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53499                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53500
53501                 this.hmenu.add('-',
53502                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53503                 );
53504             }
53505             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53506
53507             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53508         }
53509
53510         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53511             this.dd = new Roo.grid.GridDragZone(this.grid, {
53512                 ddGroup : this.grid.ddGroup || 'GridDD'
53513             });
53514             
53515         }
53516
53517         /*
53518         for(var i = 0; i < colCount; i++){
53519             if(cm.isHidden(i)){
53520                 this.hideColumn(i);
53521             }
53522             if(cm.config[i].align){
53523                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53524                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53525             }
53526         }*/
53527         
53528         this.updateHeaderSortState();
53529
53530         this.beforeInitialResize();
53531         this.layout(true);
53532
53533         // two part rendering gives faster view to the user
53534         this.renderPhase2.defer(1, this);
53535     },
53536
53537     renderPhase2 : function(){
53538         // render the rows now
53539         this.refresh();
53540         if(this.grid.autoSizeColumns){
53541             this.autoSizeColumns();
53542         }
53543     },
53544
53545     beforeInitialResize : function(){
53546
53547     },
53548
53549     onColumnSplitterMoved : function(i, w){
53550         this.userResized = true;
53551         var cm = this.grid.colModel;
53552         cm.setColumnWidth(i, w, true);
53553         var cid = cm.getColumnId(i);
53554         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53555         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53556         this.updateSplitters();
53557         this.layout();
53558         this.grid.fireEvent("columnresize", i, w);
53559     },
53560
53561     syncRowHeights : function(startIndex, endIndex){
53562         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53563             startIndex = startIndex || 0;
53564             var mrows = this.getBodyTable().rows;
53565             var lrows = this.getLockedTable().rows;
53566             var len = mrows.length-1;
53567             endIndex = Math.min(endIndex || len, len);
53568             for(var i = startIndex; i <= endIndex; i++){
53569                 var m = mrows[i], l = lrows[i];
53570                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53571                 m.style.height = l.style.height = h + "px";
53572             }
53573         }
53574     },
53575
53576     layout : function(initialRender, is2ndPass){
53577         var g = this.grid;
53578         var auto = g.autoHeight;
53579         var scrollOffset = 16;
53580         var c = g.getGridEl(), cm = this.cm,
53581                 expandCol = g.autoExpandColumn,
53582                 gv = this;
53583         //c.beginMeasure();
53584
53585         if(!c.dom.offsetWidth){ // display:none?
53586             if(initialRender){
53587                 this.lockedWrap.show();
53588                 this.mainWrap.show();
53589             }
53590             return;
53591         }
53592
53593         var hasLock = this.cm.isLocked(0);
53594
53595         var tbh = this.headerPanel.getHeight();
53596         var bbh = this.footerPanel.getHeight();
53597
53598         if(auto){
53599             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53600             var newHeight = ch + c.getBorderWidth("tb");
53601             if(g.maxHeight){
53602                 newHeight = Math.min(g.maxHeight, newHeight);
53603             }
53604             c.setHeight(newHeight);
53605         }
53606
53607         if(g.autoWidth){
53608             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
53609         }
53610
53611         var s = this.scroller;
53612
53613         var csize = c.getSize(true);
53614
53615         this.el.setSize(csize.width, csize.height);
53616
53617         this.headerPanel.setWidth(csize.width);
53618         this.footerPanel.setWidth(csize.width);
53619
53620         var hdHeight = this.mainHd.getHeight();
53621         var vw = csize.width;
53622         var vh = csize.height - (tbh + bbh);
53623
53624         s.setSize(vw, vh);
53625
53626         var bt = this.getBodyTable();
53627         var ltWidth = hasLock ?
53628                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
53629
53630         var scrollHeight = bt.offsetHeight;
53631         var scrollWidth = ltWidth + bt.offsetWidth;
53632         var vscroll = false, hscroll = false;
53633
53634         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
53635
53636         var lw = this.lockedWrap, mw = this.mainWrap;
53637         var lb = this.lockedBody, mb = this.mainBody;
53638
53639         setTimeout(function(){
53640             var t = s.dom.offsetTop;
53641             var w = s.dom.clientWidth,
53642                 h = s.dom.clientHeight;
53643
53644             lw.setTop(t);
53645             lw.setSize(ltWidth, h);
53646
53647             mw.setLeftTop(ltWidth, t);
53648             mw.setSize(w-ltWidth, h);
53649
53650             lb.setHeight(h-hdHeight);
53651             mb.setHeight(h-hdHeight);
53652
53653             if(is2ndPass !== true && !gv.userResized && expandCol){
53654                 // high speed resize without full column calculation
53655                 
53656                 var ci = cm.getIndexById(expandCol);
53657                 if (ci < 0) {
53658                     ci = cm.findColumnIndex(expandCol);
53659                 }
53660                 ci = Math.max(0, ci); // make sure it's got at least the first col.
53661                 var expandId = cm.getColumnId(ci);
53662                 var  tw = cm.getTotalWidth(false);
53663                 var currentWidth = cm.getColumnWidth(ci);
53664                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
53665                 if(currentWidth != cw){
53666                     cm.setColumnWidth(ci, cw, true);
53667                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53668                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53669                     gv.updateSplitters();
53670                     gv.layout(false, true);
53671                 }
53672             }
53673
53674             if(initialRender){
53675                 lw.show();
53676                 mw.show();
53677             }
53678             //c.endMeasure();
53679         }, 10);
53680     },
53681
53682     onWindowResize : function(){
53683         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
53684             return;
53685         }
53686         this.layout();
53687     },
53688
53689     appendFooter : function(parentEl){
53690         return null;
53691     },
53692
53693     sortAscText : "Sort Ascending",
53694     sortDescText : "Sort Descending",
53695     lockText : "Lock Column",
53696     unlockText : "Unlock Column",
53697     columnsText : "Columns"
53698 });
53699
53700
53701 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
53702     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
53703     this.proxy.el.addClass('x-grid3-col-dd');
53704 };
53705
53706 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
53707     handleMouseDown : function(e){
53708
53709     },
53710
53711     callHandleMouseDown : function(e){
53712         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53713     }
53714 });
53715 /*
53716  * Based on:
53717  * Ext JS Library 1.1.1
53718  * Copyright(c) 2006-2007, Ext JS, LLC.
53719  *
53720  * Originally Released Under LGPL - original licence link has changed is not relivant.
53721  *
53722  * Fork - LGPL
53723  * <script type="text/javascript">
53724  */
53725  
53726 // private
53727 // This is a support class used internally by the Grid components
53728 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53729     this.grid = grid;
53730     this.view = grid.getView();
53731     this.proxy = this.view.resizeProxy;
53732     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53733         "gridSplitters" + this.grid.getGridEl().id, {
53734         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53735     });
53736     this.setHandleElId(Roo.id(hd));
53737     this.setOuterHandleElId(Roo.id(hd2));
53738     this.scroll = false;
53739 };
53740 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53741     fly: Roo.Element.fly,
53742
53743     b4StartDrag : function(x, y){
53744         this.view.headersDisabled = true;
53745         this.proxy.setHeight(this.view.mainWrap.getHeight());
53746         var w = this.cm.getColumnWidth(this.cellIndex);
53747         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53748         this.resetConstraints();
53749         this.setXConstraint(minw, 1000);
53750         this.setYConstraint(0, 0);
53751         this.minX = x - minw;
53752         this.maxX = x + 1000;
53753         this.startPos = x;
53754         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53755     },
53756
53757
53758     handleMouseDown : function(e){
53759         ev = Roo.EventObject.setEvent(e);
53760         var t = this.fly(ev.getTarget());
53761         if(t.hasClass("x-grid-split")){
53762             this.cellIndex = this.view.getCellIndex(t.dom);
53763             this.split = t.dom;
53764             this.cm = this.grid.colModel;
53765             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53766                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53767             }
53768         }
53769     },
53770
53771     endDrag : function(e){
53772         this.view.headersDisabled = false;
53773         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53774         var diff = endX - this.startPos;
53775         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53776     },
53777
53778     autoOffset : function(){
53779         this.setDelta(0,0);
53780     }
53781 });/*
53782  * Based on:
53783  * Ext JS Library 1.1.1
53784  * Copyright(c) 2006-2007, Ext JS, LLC.
53785  *
53786  * Originally Released Under LGPL - original licence link has changed is not relivant.
53787  *
53788  * Fork - LGPL
53789  * <script type="text/javascript">
53790  */
53791  
53792 // private
53793 // This is a support class used internally by the Grid components
53794 Roo.grid.GridDragZone = function(grid, config){
53795     this.view = grid.getView();
53796     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53797     if(this.view.lockedBody){
53798         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53799         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53800     }
53801     this.scroll = false;
53802     this.grid = grid;
53803     this.ddel = document.createElement('div');
53804     this.ddel.className = 'x-grid-dd-wrap';
53805 };
53806
53807 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53808     ddGroup : "GridDD",
53809
53810     getDragData : function(e){
53811         var t = Roo.lib.Event.getTarget(e);
53812         var rowIndex = this.view.findRowIndex(t);
53813         var sm = this.grid.selModel;
53814             
53815         //Roo.log(rowIndex);
53816         
53817         if (sm.getSelectedCell) {
53818             // cell selection..
53819             if (!sm.getSelectedCell()) {
53820                 return false;
53821             }
53822             if (rowIndex != sm.getSelectedCell()[0]) {
53823                 return false;
53824             }
53825         
53826         }
53827         
53828         if(rowIndex !== false){
53829             
53830             // if editorgrid.. 
53831             
53832             
53833             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53834                
53835             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53836               //  
53837             //}
53838             if (e.hasModifier()){
53839                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53840             }
53841             
53842             Roo.log("getDragData");
53843             
53844             return {
53845                 grid: this.grid,
53846                 ddel: this.ddel,
53847                 rowIndex: rowIndex,
53848                 selections:sm.getSelections ? sm.getSelections() : (
53849                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53850                 )
53851             };
53852         }
53853         return false;
53854     },
53855
53856     onInitDrag : function(e){
53857         var data = this.dragData;
53858         this.ddel.innerHTML = this.grid.getDragDropText();
53859         this.proxy.update(this.ddel);
53860         // fire start drag?
53861     },
53862
53863     afterRepair : function(){
53864         this.dragging = false;
53865     },
53866
53867     getRepairXY : function(e, data){
53868         return false;
53869     },
53870
53871     onEndDrag : function(data, e){
53872         // fire end drag?
53873     },
53874
53875     onValidDrop : function(dd, e, id){
53876         // fire drag drop?
53877         this.hideProxy();
53878     },
53879
53880     beforeInvalidDrop : function(e, id){
53881
53882     }
53883 });/*
53884  * Based on:
53885  * Ext JS Library 1.1.1
53886  * Copyright(c) 2006-2007, Ext JS, LLC.
53887  *
53888  * Originally Released Under LGPL - original licence link has changed is not relivant.
53889  *
53890  * Fork - LGPL
53891  * <script type="text/javascript">
53892  */
53893  
53894
53895 /**
53896  * @class Roo.grid.ColumnModel
53897  * @extends Roo.util.Observable
53898  * This is the default implementation of a ColumnModel used by the Grid. It defines
53899  * the columns in the grid.
53900  * <br>Usage:<br>
53901  <pre><code>
53902  var colModel = new Roo.grid.ColumnModel([
53903         {header: "Ticker", width: 60, sortable: true, locked: true},
53904         {header: "Company Name", width: 150, sortable: true},
53905         {header: "Market Cap.", width: 100, sortable: true},
53906         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53907         {header: "Employees", width: 100, sortable: true, resizable: false}
53908  ]);
53909  </code></pre>
53910  * <p>
53911  
53912  * The config options listed for this class are options which may appear in each
53913  * individual column definition.
53914  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53915  * @constructor
53916  * @param {Object} config An Array of column config objects. See this class's
53917  * config objects for details.
53918 */
53919 Roo.grid.ColumnModel = function(config){
53920         /**
53921      * The config passed into the constructor
53922      */
53923     this.config = config;
53924     this.lookup = {};
53925
53926     // if no id, create one
53927     // if the column does not have a dataIndex mapping,
53928     // map it to the order it is in the config
53929     for(var i = 0, len = config.length; i < len; i++){
53930         var c = config[i];
53931         if(typeof c.dataIndex == "undefined"){
53932             c.dataIndex = i;
53933         }
53934         if(typeof c.renderer == "string"){
53935             c.renderer = Roo.util.Format[c.renderer];
53936         }
53937         if(typeof c.id == "undefined"){
53938             c.id = Roo.id();
53939         }
53940         if(c.editor && c.editor.xtype){
53941             c.editor  = Roo.factory(c.editor, Roo.grid);
53942         }
53943         if(c.editor && c.editor.isFormField){
53944             c.editor = new Roo.grid.GridEditor(c.editor);
53945         }
53946         this.lookup[c.id] = c;
53947     }
53948
53949     /**
53950      * The width of columns which have no width specified (defaults to 100)
53951      * @type Number
53952      */
53953     this.defaultWidth = 100;
53954
53955     /**
53956      * Default sortable of columns which have no sortable specified (defaults to false)
53957      * @type Boolean
53958      */
53959     this.defaultSortable = false;
53960
53961     this.addEvents({
53962         /**
53963              * @event widthchange
53964              * Fires when the width of a column changes.
53965              * @param {ColumnModel} this
53966              * @param {Number} columnIndex The column index
53967              * @param {Number} newWidth The new width
53968              */
53969             "widthchange": true,
53970         /**
53971              * @event headerchange
53972              * Fires when the text of a header changes.
53973              * @param {ColumnModel} this
53974              * @param {Number} columnIndex The column index
53975              * @param {Number} newText The new header text
53976              */
53977             "headerchange": true,
53978         /**
53979              * @event hiddenchange
53980              * Fires when a column is hidden or "unhidden".
53981              * @param {ColumnModel} this
53982              * @param {Number} columnIndex The column index
53983              * @param {Boolean} hidden true if hidden, false otherwise
53984              */
53985             "hiddenchange": true,
53986             /**
53987          * @event columnmoved
53988          * Fires when a column is moved.
53989          * @param {ColumnModel} this
53990          * @param {Number} oldIndex
53991          * @param {Number} newIndex
53992          */
53993         "columnmoved" : true,
53994         /**
53995          * @event columlockchange
53996          * Fires when a column's locked state is changed
53997          * @param {ColumnModel} this
53998          * @param {Number} colIndex
53999          * @param {Boolean} locked true if locked
54000          */
54001         "columnlockchange" : true
54002     });
54003     Roo.grid.ColumnModel.superclass.constructor.call(this);
54004 };
54005 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54006     /**
54007      * @cfg {String} header The header text to display in the Grid view.
54008      */
54009     /**
54010      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54011      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54012      * specified, the column's index is used as an index into the Record's data Array.
54013      */
54014     /**
54015      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54016      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54017      */
54018     /**
54019      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54020      * Defaults to the value of the {@link #defaultSortable} property.
54021      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54022      */
54023     /**
54024      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54025      */
54026     /**
54027      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54028      */
54029     /**
54030      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54031      */
54032     /**
54033      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54034      */
54035     /**
54036      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54037      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54038      * default renderer uses the raw data value.
54039      */
54040        /**
54041      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54042      */
54043     /**
54044      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54045      */
54046
54047     /**
54048      * Returns the id of the column at the specified index.
54049      * @param {Number} index The column index
54050      * @return {String} the id
54051      */
54052     getColumnId : function(index){
54053         return this.config[index].id;
54054     },
54055
54056     /**
54057      * Returns the column for a specified id.
54058      * @param {String} id The column id
54059      * @return {Object} the column
54060      */
54061     getColumnById : function(id){
54062         return this.lookup[id];
54063     },
54064
54065     
54066     /**
54067      * Returns the column for a specified dataIndex.
54068      * @param {String} dataIndex The column dataIndex
54069      * @return {Object|Boolean} the column or false if not found
54070      */
54071     getColumnByDataIndex: function(dataIndex){
54072         var index = this.findColumnIndex(dataIndex);
54073         return index > -1 ? this.config[index] : false;
54074     },
54075     
54076     /**
54077      * Returns the index for a specified column id.
54078      * @param {String} id The column id
54079      * @return {Number} the index, or -1 if not found
54080      */
54081     getIndexById : function(id){
54082         for(var i = 0, len = this.config.length; i < len; i++){
54083             if(this.config[i].id == id){
54084                 return i;
54085             }
54086         }
54087         return -1;
54088     },
54089     
54090     /**
54091      * Returns the index for a specified column dataIndex.
54092      * @param {String} dataIndex The column dataIndex
54093      * @return {Number} the index, or -1 if not found
54094      */
54095     
54096     findColumnIndex : function(dataIndex){
54097         for(var i = 0, len = this.config.length; i < len; i++){
54098             if(this.config[i].dataIndex == dataIndex){
54099                 return i;
54100             }
54101         }
54102         return -1;
54103     },
54104     
54105     
54106     moveColumn : function(oldIndex, newIndex){
54107         var c = this.config[oldIndex];
54108         this.config.splice(oldIndex, 1);
54109         this.config.splice(newIndex, 0, c);
54110         this.dataMap = null;
54111         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54112     },
54113
54114     isLocked : function(colIndex){
54115         return this.config[colIndex].locked === true;
54116     },
54117
54118     setLocked : function(colIndex, value, suppressEvent){
54119         if(this.isLocked(colIndex) == value){
54120             return;
54121         }
54122         this.config[colIndex].locked = value;
54123         if(!suppressEvent){
54124             this.fireEvent("columnlockchange", this, colIndex, value);
54125         }
54126     },
54127
54128     getTotalLockedWidth : function(){
54129         var totalWidth = 0;
54130         for(var i = 0; i < this.config.length; i++){
54131             if(this.isLocked(i) && !this.isHidden(i)){
54132                 this.totalWidth += this.getColumnWidth(i);
54133             }
54134         }
54135         return totalWidth;
54136     },
54137
54138     getLockedCount : function(){
54139         for(var i = 0, len = this.config.length; i < len; i++){
54140             if(!this.isLocked(i)){
54141                 return i;
54142             }
54143         }
54144     },
54145
54146     /**
54147      * Returns the number of columns.
54148      * @return {Number}
54149      */
54150     getColumnCount : function(visibleOnly){
54151         if(visibleOnly === true){
54152             var c = 0;
54153             for(var i = 0, len = this.config.length; i < len; i++){
54154                 if(!this.isHidden(i)){
54155                     c++;
54156                 }
54157             }
54158             return c;
54159         }
54160         return this.config.length;
54161     },
54162
54163     /**
54164      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54165      * @param {Function} fn
54166      * @param {Object} scope (optional)
54167      * @return {Array} result
54168      */
54169     getColumnsBy : function(fn, scope){
54170         var r = [];
54171         for(var i = 0, len = this.config.length; i < len; i++){
54172             var c = this.config[i];
54173             if(fn.call(scope||this, c, i) === true){
54174                 r[r.length] = c;
54175             }
54176         }
54177         return r;
54178     },
54179
54180     /**
54181      * Returns true if the specified column is sortable.
54182      * @param {Number} col The column index
54183      * @return {Boolean}
54184      */
54185     isSortable : function(col){
54186         if(typeof this.config[col].sortable == "undefined"){
54187             return this.defaultSortable;
54188         }
54189         return this.config[col].sortable;
54190     },
54191
54192     /**
54193      * Returns the rendering (formatting) function defined for the column.
54194      * @param {Number} col The column index.
54195      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54196      */
54197     getRenderer : function(col){
54198         if(!this.config[col].renderer){
54199             return Roo.grid.ColumnModel.defaultRenderer;
54200         }
54201         return this.config[col].renderer;
54202     },
54203
54204     /**
54205      * Sets the rendering (formatting) function for a column.
54206      * @param {Number} col The column index
54207      * @param {Function} fn The function to use to process the cell's raw data
54208      * to return HTML markup for the grid view. The render function is called with
54209      * the following parameters:<ul>
54210      * <li>Data value.</li>
54211      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54212      * <li>css A CSS style string to apply to the table cell.</li>
54213      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54214      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54215      * <li>Row index</li>
54216      * <li>Column index</li>
54217      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54218      */
54219     setRenderer : function(col, fn){
54220         this.config[col].renderer = fn;
54221     },
54222
54223     /**
54224      * Returns the width for the specified column.
54225      * @param {Number} col The column index
54226      * @return {Number}
54227      */
54228     getColumnWidth : function(col){
54229         return this.config[col].width * 1 || this.defaultWidth;
54230     },
54231
54232     /**
54233      * Sets the width for a column.
54234      * @param {Number} col The column index
54235      * @param {Number} width The new width
54236      */
54237     setColumnWidth : function(col, width, suppressEvent){
54238         this.config[col].width = width;
54239         this.totalWidth = null;
54240         if(!suppressEvent){
54241              this.fireEvent("widthchange", this, col, width);
54242         }
54243     },
54244
54245     /**
54246      * Returns the total width of all columns.
54247      * @param {Boolean} includeHidden True to include hidden column widths
54248      * @return {Number}
54249      */
54250     getTotalWidth : function(includeHidden){
54251         if(!this.totalWidth){
54252             this.totalWidth = 0;
54253             for(var i = 0, len = this.config.length; i < len; i++){
54254                 if(includeHidden || !this.isHidden(i)){
54255                     this.totalWidth += this.getColumnWidth(i);
54256                 }
54257             }
54258         }
54259         return this.totalWidth;
54260     },
54261
54262     /**
54263      * Returns the header for the specified column.
54264      * @param {Number} col The column index
54265      * @return {String}
54266      */
54267     getColumnHeader : function(col){
54268         return this.config[col].header;
54269     },
54270
54271     /**
54272      * Sets the header for a column.
54273      * @param {Number} col The column index
54274      * @param {String} header The new header
54275      */
54276     setColumnHeader : function(col, header){
54277         this.config[col].header = header;
54278         this.fireEvent("headerchange", this, col, header);
54279     },
54280
54281     /**
54282      * Returns the tooltip for the specified column.
54283      * @param {Number} col The column index
54284      * @return {String}
54285      */
54286     getColumnTooltip : function(col){
54287             return this.config[col].tooltip;
54288     },
54289     /**
54290      * Sets the tooltip for a column.
54291      * @param {Number} col The column index
54292      * @param {String} tooltip The new tooltip
54293      */
54294     setColumnTooltip : function(col, tooltip){
54295             this.config[col].tooltip = tooltip;
54296     },
54297
54298     /**
54299      * Returns the dataIndex for the specified column.
54300      * @param {Number} col The column index
54301      * @return {Number}
54302      */
54303     getDataIndex : function(col){
54304         return this.config[col].dataIndex;
54305     },
54306
54307     /**
54308      * Sets the dataIndex for a column.
54309      * @param {Number} col The column index
54310      * @param {Number} dataIndex The new dataIndex
54311      */
54312     setDataIndex : function(col, dataIndex){
54313         this.config[col].dataIndex = dataIndex;
54314     },
54315
54316     
54317     
54318     /**
54319      * Returns true if the cell is editable.
54320      * @param {Number} colIndex The column index
54321      * @param {Number} rowIndex The row index
54322      * @return {Boolean}
54323      */
54324     isCellEditable : function(colIndex, rowIndex){
54325         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54326     },
54327
54328     /**
54329      * Returns the editor defined for the cell/column.
54330      * return false or null to disable editing.
54331      * @param {Number} colIndex The column index
54332      * @param {Number} rowIndex The row index
54333      * @return {Object}
54334      */
54335     getCellEditor : function(colIndex, rowIndex){
54336         return this.config[colIndex].editor;
54337     },
54338
54339     /**
54340      * Sets if a column is editable.
54341      * @param {Number} col The column index
54342      * @param {Boolean} editable True if the column is editable
54343      */
54344     setEditable : function(col, editable){
54345         this.config[col].editable = editable;
54346     },
54347
54348
54349     /**
54350      * Returns true if the column is hidden.
54351      * @param {Number} colIndex The column index
54352      * @return {Boolean}
54353      */
54354     isHidden : function(colIndex){
54355         return this.config[colIndex].hidden;
54356     },
54357
54358
54359     /**
54360      * Returns true if the column width cannot be changed
54361      */
54362     isFixed : function(colIndex){
54363         return this.config[colIndex].fixed;
54364     },
54365
54366     /**
54367      * Returns true if the column can be resized
54368      * @return {Boolean}
54369      */
54370     isResizable : function(colIndex){
54371         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54372     },
54373     /**
54374      * Sets if a column is hidden.
54375      * @param {Number} colIndex The column index
54376      * @param {Boolean} hidden True if the column is hidden
54377      */
54378     setHidden : function(colIndex, hidden){
54379         this.config[colIndex].hidden = hidden;
54380         this.totalWidth = null;
54381         this.fireEvent("hiddenchange", this, colIndex, hidden);
54382     },
54383
54384     /**
54385      * Sets the editor for a column.
54386      * @param {Number} col The column index
54387      * @param {Object} editor The editor object
54388      */
54389     setEditor : function(col, editor){
54390         this.config[col].editor = editor;
54391     }
54392 });
54393
54394 Roo.grid.ColumnModel.defaultRenderer = function(value){
54395         if(typeof value == "string" && value.length < 1){
54396             return "&#160;";
54397         }
54398         return value;
54399 };
54400
54401 // Alias for backwards compatibility
54402 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54403 /*
54404  * Based on:
54405  * Ext JS Library 1.1.1
54406  * Copyright(c) 2006-2007, Ext JS, LLC.
54407  *
54408  * Originally Released Under LGPL - original licence link has changed is not relivant.
54409  *
54410  * Fork - LGPL
54411  * <script type="text/javascript">
54412  */
54413
54414 /**
54415  * @class Roo.grid.AbstractSelectionModel
54416  * @extends Roo.util.Observable
54417  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54418  * implemented by descendant classes.  This class should not be directly instantiated.
54419  * @constructor
54420  */
54421 Roo.grid.AbstractSelectionModel = function(){
54422     this.locked = false;
54423     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54424 };
54425
54426 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54427     /** @ignore Called by the grid automatically. Do not call directly. */
54428     init : function(grid){
54429         this.grid = grid;
54430         this.initEvents();
54431     },
54432
54433     /**
54434      * Locks the selections.
54435      */
54436     lock : function(){
54437         this.locked = true;
54438     },
54439
54440     /**
54441      * Unlocks the selections.
54442      */
54443     unlock : function(){
54444         this.locked = false;
54445     },
54446
54447     /**
54448      * Returns true if the selections are locked.
54449      * @return {Boolean}
54450      */
54451     isLocked : function(){
54452         return this.locked;
54453     }
54454 });/*
54455  * Based on:
54456  * Ext JS Library 1.1.1
54457  * Copyright(c) 2006-2007, Ext JS, LLC.
54458  *
54459  * Originally Released Under LGPL - original licence link has changed is not relivant.
54460  *
54461  * Fork - LGPL
54462  * <script type="text/javascript">
54463  */
54464 /**
54465  * @extends Roo.grid.AbstractSelectionModel
54466  * @class Roo.grid.RowSelectionModel
54467  * The default SelectionModel used by {@link Roo.grid.Grid}.
54468  * It supports multiple selections and keyboard selection/navigation. 
54469  * @constructor
54470  * @param {Object} config
54471  */
54472 Roo.grid.RowSelectionModel = function(config){
54473     Roo.apply(this, config);
54474     this.selections = new Roo.util.MixedCollection(false, function(o){
54475         return o.id;
54476     });
54477
54478     this.last = false;
54479     this.lastActive = false;
54480
54481     this.addEvents({
54482         /**
54483              * @event selectionchange
54484              * Fires when the selection changes
54485              * @param {SelectionModel} this
54486              */
54487             "selectionchange" : true,
54488         /**
54489              * @event afterselectionchange
54490              * Fires after the selection changes (eg. by key press or clicking)
54491              * @param {SelectionModel} this
54492              */
54493             "afterselectionchange" : true,
54494         /**
54495              * @event beforerowselect
54496              * Fires when a row is selected being selected, return false to cancel.
54497              * @param {SelectionModel} this
54498              * @param {Number} rowIndex The selected index
54499              * @param {Boolean} keepExisting False if other selections will be cleared
54500              */
54501             "beforerowselect" : true,
54502         /**
54503              * @event rowselect
54504              * Fires when a row is selected.
54505              * @param {SelectionModel} this
54506              * @param {Number} rowIndex The selected index
54507              * @param {Roo.data.Record} r The record
54508              */
54509             "rowselect" : true,
54510         /**
54511              * @event rowdeselect
54512              * Fires when a row is deselected.
54513              * @param {SelectionModel} this
54514              * @param {Number} rowIndex The selected index
54515              */
54516         "rowdeselect" : true
54517     });
54518     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54519     this.locked = false;
54520 };
54521
54522 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54523     /**
54524      * @cfg {Boolean} singleSelect
54525      * True to allow selection of only one row at a time (defaults to false)
54526      */
54527     singleSelect : false,
54528
54529     // private
54530     initEvents : function(){
54531
54532         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54533             this.grid.on("mousedown", this.handleMouseDown, this);
54534         }else{ // allow click to work like normal
54535             this.grid.on("rowclick", this.handleDragableRowClick, this);
54536         }
54537
54538         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54539             "up" : function(e){
54540                 if(!e.shiftKey){
54541                     this.selectPrevious(e.shiftKey);
54542                 }else if(this.last !== false && this.lastActive !== false){
54543                     var last = this.last;
54544                     this.selectRange(this.last,  this.lastActive-1);
54545                     this.grid.getView().focusRow(this.lastActive);
54546                     if(last !== false){
54547                         this.last = last;
54548                     }
54549                 }else{
54550                     this.selectFirstRow();
54551                 }
54552                 this.fireEvent("afterselectionchange", this);
54553             },
54554             "down" : function(e){
54555                 if(!e.shiftKey){
54556                     this.selectNext(e.shiftKey);
54557                 }else if(this.last !== false && this.lastActive !== false){
54558                     var last = this.last;
54559                     this.selectRange(this.last,  this.lastActive+1);
54560                     this.grid.getView().focusRow(this.lastActive);
54561                     if(last !== false){
54562                         this.last = last;
54563                     }
54564                 }else{
54565                     this.selectFirstRow();
54566                 }
54567                 this.fireEvent("afterselectionchange", this);
54568             },
54569             scope: this
54570         });
54571
54572         var view = this.grid.view;
54573         view.on("refresh", this.onRefresh, this);
54574         view.on("rowupdated", this.onRowUpdated, this);
54575         view.on("rowremoved", this.onRemove, this);
54576     },
54577
54578     // private
54579     onRefresh : function(){
54580         var ds = this.grid.dataSource, i, v = this.grid.view;
54581         var s = this.selections;
54582         s.each(function(r){
54583             if((i = ds.indexOfId(r.id)) != -1){
54584                 v.onRowSelect(i);
54585             }else{
54586                 s.remove(r);
54587             }
54588         });
54589     },
54590
54591     // private
54592     onRemove : function(v, index, r){
54593         this.selections.remove(r);
54594     },
54595
54596     // private
54597     onRowUpdated : function(v, index, r){
54598         if(this.isSelected(r)){
54599             v.onRowSelect(index);
54600         }
54601     },
54602
54603     /**
54604      * Select records.
54605      * @param {Array} records The records to select
54606      * @param {Boolean} keepExisting (optional) True to keep existing selections
54607      */
54608     selectRecords : function(records, keepExisting){
54609         if(!keepExisting){
54610             this.clearSelections();
54611         }
54612         var ds = this.grid.dataSource;
54613         for(var i = 0, len = records.length; i < len; i++){
54614             this.selectRow(ds.indexOf(records[i]), true);
54615         }
54616     },
54617
54618     /**
54619      * Gets the number of selected rows.
54620      * @return {Number}
54621      */
54622     getCount : function(){
54623         return this.selections.length;
54624     },
54625
54626     /**
54627      * Selects the first row in the grid.
54628      */
54629     selectFirstRow : function(){
54630         this.selectRow(0);
54631     },
54632
54633     /**
54634      * Select the last row.
54635      * @param {Boolean} keepExisting (optional) True to keep existing selections
54636      */
54637     selectLastRow : function(keepExisting){
54638         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
54639     },
54640
54641     /**
54642      * Selects the row immediately following the last selected row.
54643      * @param {Boolean} keepExisting (optional) True to keep existing selections
54644      */
54645     selectNext : function(keepExisting){
54646         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
54647             this.selectRow(this.last+1, keepExisting);
54648             this.grid.getView().focusRow(this.last);
54649         }
54650     },
54651
54652     /**
54653      * Selects the row that precedes the last selected row.
54654      * @param {Boolean} keepExisting (optional) True to keep existing selections
54655      */
54656     selectPrevious : function(keepExisting){
54657         if(this.last){
54658             this.selectRow(this.last-1, keepExisting);
54659             this.grid.getView().focusRow(this.last);
54660         }
54661     },
54662
54663     /**
54664      * Returns the selected records
54665      * @return {Array} Array of selected records
54666      */
54667     getSelections : function(){
54668         return [].concat(this.selections.items);
54669     },
54670
54671     /**
54672      * Returns the first selected record.
54673      * @return {Record}
54674      */
54675     getSelected : function(){
54676         return this.selections.itemAt(0);
54677     },
54678
54679
54680     /**
54681      * Clears all selections.
54682      */
54683     clearSelections : function(fast){
54684         if(this.locked) return;
54685         if(fast !== true){
54686             var ds = this.grid.dataSource;
54687             var s = this.selections;
54688             s.each(function(r){
54689                 this.deselectRow(ds.indexOfId(r.id));
54690             }, this);
54691             s.clear();
54692         }else{
54693             this.selections.clear();
54694         }
54695         this.last = false;
54696     },
54697
54698
54699     /**
54700      * Selects all rows.
54701      */
54702     selectAll : function(){
54703         if(this.locked) return;
54704         this.selections.clear();
54705         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
54706             this.selectRow(i, true);
54707         }
54708     },
54709
54710     /**
54711      * Returns True if there is a selection.
54712      * @return {Boolean}
54713      */
54714     hasSelection : function(){
54715         return this.selections.length > 0;
54716     },
54717
54718     /**
54719      * Returns True if the specified row is selected.
54720      * @param {Number/Record} record The record or index of the record to check
54721      * @return {Boolean}
54722      */
54723     isSelected : function(index){
54724         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54725         return (r && this.selections.key(r.id) ? true : false);
54726     },
54727
54728     /**
54729      * Returns True if the specified record id is selected.
54730      * @param {String} id The id of record to check
54731      * @return {Boolean}
54732      */
54733     isIdSelected : function(id){
54734         return (this.selections.key(id) ? true : false);
54735     },
54736
54737     // private
54738     handleMouseDown : function(e, t){
54739         var view = this.grid.getView(), rowIndex;
54740         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54741             return;
54742         };
54743         if(e.shiftKey && this.last !== false){
54744             var last = this.last;
54745             this.selectRange(last, rowIndex, e.ctrlKey);
54746             this.last = last; // reset the last
54747             view.focusRow(rowIndex);
54748         }else{
54749             var isSelected = this.isSelected(rowIndex);
54750             if(e.button !== 0 && isSelected){
54751                 view.focusRow(rowIndex);
54752             }else if(e.ctrlKey && isSelected){
54753                 this.deselectRow(rowIndex);
54754             }else if(!isSelected){
54755                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54756                 view.focusRow(rowIndex);
54757             }
54758         }
54759         this.fireEvent("afterselectionchange", this);
54760     },
54761     // private
54762     handleDragableRowClick :  function(grid, rowIndex, e) 
54763     {
54764         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54765             this.selectRow(rowIndex, false);
54766             grid.view.focusRow(rowIndex);
54767              this.fireEvent("afterselectionchange", this);
54768         }
54769     },
54770     
54771     /**
54772      * Selects multiple rows.
54773      * @param {Array} rows Array of the indexes of the row to select
54774      * @param {Boolean} keepExisting (optional) True to keep existing selections
54775      */
54776     selectRows : function(rows, keepExisting){
54777         if(!keepExisting){
54778             this.clearSelections();
54779         }
54780         for(var i = 0, len = rows.length; i < len; i++){
54781             this.selectRow(rows[i], true);
54782         }
54783     },
54784
54785     /**
54786      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54787      * @param {Number} startRow The index of the first row in the range
54788      * @param {Number} endRow The index of the last row in the range
54789      * @param {Boolean} keepExisting (optional) True to retain existing selections
54790      */
54791     selectRange : function(startRow, endRow, keepExisting){
54792         if(this.locked) return;
54793         if(!keepExisting){
54794             this.clearSelections();
54795         }
54796         if(startRow <= endRow){
54797             for(var i = startRow; i <= endRow; i++){
54798                 this.selectRow(i, true);
54799             }
54800         }else{
54801             for(var i = startRow; i >= endRow; i--){
54802                 this.selectRow(i, true);
54803             }
54804         }
54805     },
54806
54807     /**
54808      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54809      * @param {Number} startRow The index of the first row in the range
54810      * @param {Number} endRow The index of the last row in the range
54811      */
54812     deselectRange : function(startRow, endRow, preventViewNotify){
54813         if(this.locked) return;
54814         for(var i = startRow; i <= endRow; i++){
54815             this.deselectRow(i, preventViewNotify);
54816         }
54817     },
54818
54819     /**
54820      * Selects a row.
54821      * @param {Number} row The index of the row to select
54822      * @param {Boolean} keepExisting (optional) True to keep existing selections
54823      */
54824     selectRow : function(index, keepExisting, preventViewNotify){
54825         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54826         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54827             if(!keepExisting || this.singleSelect){
54828                 this.clearSelections();
54829             }
54830             var r = this.grid.dataSource.getAt(index);
54831             this.selections.add(r);
54832             this.last = this.lastActive = index;
54833             if(!preventViewNotify){
54834                 this.grid.getView().onRowSelect(index);
54835             }
54836             this.fireEvent("rowselect", this, index, r);
54837             this.fireEvent("selectionchange", this);
54838         }
54839     },
54840
54841     /**
54842      * Deselects a row.
54843      * @param {Number} row The index of the row to deselect
54844      */
54845     deselectRow : function(index, preventViewNotify){
54846         if(this.locked) return;
54847         if(this.last == index){
54848             this.last = false;
54849         }
54850         if(this.lastActive == index){
54851             this.lastActive = false;
54852         }
54853         var r = this.grid.dataSource.getAt(index);
54854         this.selections.remove(r);
54855         if(!preventViewNotify){
54856             this.grid.getView().onRowDeselect(index);
54857         }
54858         this.fireEvent("rowdeselect", this, index);
54859         this.fireEvent("selectionchange", this);
54860     },
54861
54862     // private
54863     restoreLast : function(){
54864         if(this._last){
54865             this.last = this._last;
54866         }
54867     },
54868
54869     // private
54870     acceptsNav : function(row, col, cm){
54871         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54872     },
54873
54874     // private
54875     onEditorKey : function(field, e){
54876         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54877         if(k == e.TAB){
54878             e.stopEvent();
54879             ed.completeEdit();
54880             if(e.shiftKey){
54881                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54882             }else{
54883                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54884             }
54885         }else if(k == e.ENTER && !e.ctrlKey){
54886             e.stopEvent();
54887             ed.completeEdit();
54888             if(e.shiftKey){
54889                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54890             }else{
54891                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54892             }
54893         }else if(k == e.ESC){
54894             ed.cancelEdit();
54895         }
54896         if(newCell){
54897             g.startEditing(newCell[0], newCell[1]);
54898         }
54899     }
54900 });/*
54901  * Based on:
54902  * Ext JS Library 1.1.1
54903  * Copyright(c) 2006-2007, Ext JS, LLC.
54904  *
54905  * Originally Released Under LGPL - original licence link has changed is not relivant.
54906  *
54907  * Fork - LGPL
54908  * <script type="text/javascript">
54909  */
54910 /**
54911  * @class Roo.grid.CellSelectionModel
54912  * @extends Roo.grid.AbstractSelectionModel
54913  * This class provides the basic implementation for cell selection in a grid.
54914  * @constructor
54915  * @param {Object} config The object containing the configuration of this model.
54916  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54917  */
54918 Roo.grid.CellSelectionModel = function(config){
54919     Roo.apply(this, config);
54920
54921     this.selection = null;
54922
54923     this.addEvents({
54924         /**
54925              * @event beforerowselect
54926              * Fires before a cell is selected.
54927              * @param {SelectionModel} this
54928              * @param {Number} rowIndex The selected row index
54929              * @param {Number} colIndex The selected cell index
54930              */
54931             "beforecellselect" : true,
54932         /**
54933              * @event cellselect
54934              * Fires when a cell is selected.
54935              * @param {SelectionModel} this
54936              * @param {Number} rowIndex The selected row index
54937              * @param {Number} colIndex The selected cell index
54938              */
54939             "cellselect" : true,
54940         /**
54941              * @event selectionchange
54942              * Fires when the active selection changes.
54943              * @param {SelectionModel} this
54944              * @param {Object} selection null for no selection or an object (o) with two properties
54945                 <ul>
54946                 <li>o.record: the record object for the row the selection is in</li>
54947                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54948                 </ul>
54949              */
54950             "selectionchange" : true,
54951         /**
54952              * @event tabend
54953              * Fires when the tab (or enter) was pressed on the last editable cell
54954              * You can use this to trigger add new row.
54955              * @param {SelectionModel} this
54956              */
54957             "tabend" : true,
54958          /**
54959              * @event beforeeditnext
54960              * Fires before the next editable sell is made active
54961              * You can use this to skip to another cell or fire the tabend
54962              *    if you set cell to false
54963              * @param {Object} eventdata object : { cell : [ row, col ] } 
54964              */
54965             "beforeeditnext" : true
54966     });
54967     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54968 };
54969
54970 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54971     
54972     enter_is_tab: false,
54973
54974     /** @ignore */
54975     initEvents : function(){
54976         this.grid.on("mousedown", this.handleMouseDown, this);
54977         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54978         var view = this.grid.view;
54979         view.on("refresh", this.onViewChange, this);
54980         view.on("rowupdated", this.onRowUpdated, this);
54981         view.on("beforerowremoved", this.clearSelections, this);
54982         view.on("beforerowsinserted", this.clearSelections, this);
54983         if(this.grid.isEditor){
54984             this.grid.on("beforeedit", this.beforeEdit,  this);
54985         }
54986     },
54987
54988         //private
54989     beforeEdit : function(e){
54990         this.select(e.row, e.column, false, true, e.record);
54991     },
54992
54993         //private
54994     onRowUpdated : function(v, index, r){
54995         if(this.selection && this.selection.record == r){
54996             v.onCellSelect(index, this.selection.cell[1]);
54997         }
54998     },
54999
55000         //private
55001     onViewChange : function(){
55002         this.clearSelections(true);
55003     },
55004
55005         /**
55006          * Returns the currently selected cell,.
55007          * @return {Array} The selected cell (row, column) or null if none selected.
55008          */
55009     getSelectedCell : function(){
55010         return this.selection ? this.selection.cell : null;
55011     },
55012
55013     /**
55014      * Clears all selections.
55015      * @param {Boolean} true to prevent the gridview from being notified about the change.
55016      */
55017     clearSelections : function(preventNotify){
55018         var s = this.selection;
55019         if(s){
55020             if(preventNotify !== true){
55021                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55022             }
55023             this.selection = null;
55024             this.fireEvent("selectionchange", this, null);
55025         }
55026     },
55027
55028     /**
55029      * Returns true if there is a selection.
55030      * @return {Boolean}
55031      */
55032     hasSelection : function(){
55033         return this.selection ? true : false;
55034     },
55035
55036     /** @ignore */
55037     handleMouseDown : function(e, t){
55038         var v = this.grid.getView();
55039         if(this.isLocked()){
55040             return;
55041         };
55042         var row = v.findRowIndex(t);
55043         var cell = v.findCellIndex(t);
55044         if(row !== false && cell !== false){
55045             this.select(row, cell);
55046         }
55047     },
55048
55049     /**
55050      * Selects a cell.
55051      * @param {Number} rowIndex
55052      * @param {Number} collIndex
55053      */
55054     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55055         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55056             this.clearSelections();
55057             r = r || this.grid.dataSource.getAt(rowIndex);
55058             this.selection = {
55059                 record : r,
55060                 cell : [rowIndex, colIndex]
55061             };
55062             if(!preventViewNotify){
55063                 var v = this.grid.getView();
55064                 v.onCellSelect(rowIndex, colIndex);
55065                 if(preventFocus !== true){
55066                     v.focusCell(rowIndex, colIndex);
55067                 }
55068             }
55069             this.fireEvent("cellselect", this, rowIndex, colIndex);
55070             this.fireEvent("selectionchange", this, this.selection);
55071         }
55072     },
55073
55074         //private
55075     isSelectable : function(rowIndex, colIndex, cm){
55076         return !cm.isHidden(colIndex);
55077     },
55078
55079     /** @ignore */
55080     handleKeyDown : function(e){
55081         //Roo.log('Cell Sel Model handleKeyDown');
55082         if(!e.isNavKeyPress()){
55083             return;
55084         }
55085         var g = this.grid, s = this.selection;
55086         if(!s){
55087             e.stopEvent();
55088             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55089             if(cell){
55090                 this.select(cell[0], cell[1]);
55091             }
55092             return;
55093         }
55094         var sm = this;
55095         var walk = function(row, col, step){
55096             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55097         };
55098         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55099         var newCell;
55100
55101       
55102
55103         switch(k){
55104             case e.TAB:
55105                 // handled by onEditorKey
55106                 if (g.isEditor && g.editing) {
55107                     return;
55108                 }
55109                 if(e.shiftKey) {
55110                     newCell = walk(r, c-1, -1);
55111                 } else {
55112                     newCell = walk(r, c+1, 1);
55113                 }
55114                 break;
55115             
55116             case e.DOWN:
55117                newCell = walk(r+1, c, 1);
55118                 break;
55119             
55120             case e.UP:
55121                 newCell = walk(r-1, c, -1);
55122                 break;
55123             
55124             case e.RIGHT:
55125                 newCell = walk(r, c+1, 1);
55126                 break;
55127             
55128             case e.LEFT:
55129                 newCell = walk(r, c-1, -1);
55130                 break;
55131             
55132             case e.ENTER:
55133                 
55134                 if(g.isEditor && !g.editing){
55135                    g.startEditing(r, c);
55136                    e.stopEvent();
55137                    return;
55138                 }
55139                 
55140                 
55141              break;
55142         };
55143         if(newCell){
55144             this.select(newCell[0], newCell[1]);
55145             e.stopEvent();
55146             
55147         }
55148     },
55149
55150     acceptsNav : function(row, col, cm){
55151         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55152     },
55153     /**
55154      * Selects a cell.
55155      * @param {Number} field (not used) - as it's normally used as a listener
55156      * @param {Number} e - event - fake it by using
55157      *
55158      * var e = Roo.EventObjectImpl.prototype;
55159      * e.keyCode = e.TAB
55160      *
55161      * 
55162      */
55163     onEditorKey : function(field, e){
55164         
55165         var k = e.getKey(),
55166             newCell,
55167             g = this.grid,
55168             ed = g.activeEditor,
55169             forward = false;
55170         ///Roo.log('onEditorKey' + k);
55171         
55172         
55173         if (this.enter_is_tab && k == e.ENTER) {
55174             k = e.TAB;
55175         }
55176         
55177         if(k == e.TAB){
55178             if(e.shiftKey){
55179                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55180             }else{
55181                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55182                 forward = true;
55183             }
55184             
55185             e.stopEvent();
55186             
55187         } else if(k == e.ENTER &&  !e.ctrlKey){
55188             ed.completeEdit();
55189             e.stopEvent();
55190             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55191         
55192                 } else if(k == e.ESC){
55193             ed.cancelEdit();
55194         }
55195                 
55196         if (newCell) {
55197             var ecall = { cell : newCell, forward : forward };
55198             this.fireEvent('beforeeditnext', ecall );
55199             newCell = ecall.cell;
55200                         forward = ecall.forward;
55201         }
55202                 
55203         if(newCell){
55204             //Roo.log('next cell after edit');
55205             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55206         } else if (forward) {
55207             // tabbed past last
55208             this.fireEvent.defer(100, this, ['tabend',this]);
55209         }
55210     }
55211 });/*
55212  * Based on:
55213  * Ext JS Library 1.1.1
55214  * Copyright(c) 2006-2007, Ext JS, LLC.
55215  *
55216  * Originally Released Under LGPL - original licence link has changed is not relivant.
55217  *
55218  * Fork - LGPL
55219  * <script type="text/javascript">
55220  */
55221  
55222 /**
55223  * @class Roo.grid.EditorGrid
55224  * @extends Roo.grid.Grid
55225  * Class for creating and editable grid.
55226  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55227  * The container MUST have some type of size defined for the grid to fill. The container will be 
55228  * automatically set to position relative if it isn't already.
55229  * @param {Object} dataSource The data model to bind to
55230  * @param {Object} colModel The column model with info about this grid's columns
55231  */
55232 Roo.grid.EditorGrid = function(container, config){
55233     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55234     this.getGridEl().addClass("xedit-grid");
55235
55236     if(!this.selModel){
55237         this.selModel = new Roo.grid.CellSelectionModel();
55238     }
55239
55240     this.activeEditor = null;
55241
55242         this.addEvents({
55243             /**
55244              * @event beforeedit
55245              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55246              * <ul style="padding:5px;padding-left:16px;">
55247              * <li>grid - This grid</li>
55248              * <li>record - The record being edited</li>
55249              * <li>field - The field name being edited</li>
55250              * <li>value - The value for the field being edited.</li>
55251              * <li>row - The grid row index</li>
55252              * <li>column - The grid column index</li>
55253              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55254              * </ul>
55255              * @param {Object} e An edit event (see above for description)
55256              */
55257             "beforeedit" : true,
55258             /**
55259              * @event afteredit
55260              * Fires after a cell is edited. <br />
55261              * <ul style="padding:5px;padding-left:16px;">
55262              * <li>grid - This grid</li>
55263              * <li>record - The record being edited</li>
55264              * <li>field - The field name being edited</li>
55265              * <li>value - The value being set</li>
55266              * <li>originalValue - The original value for the field, before the edit.</li>
55267              * <li>row - The grid row index</li>
55268              * <li>column - The grid column index</li>
55269              * </ul>
55270              * @param {Object} e An edit event (see above for description)
55271              */
55272             "afteredit" : true,
55273             /**
55274              * @event validateedit
55275              * Fires after a cell is edited, but before the value is set in the record. 
55276          * You can use this to modify the value being set in the field, Return false
55277              * to cancel the change. The edit event object has the following properties <br />
55278              * <ul style="padding:5px;padding-left:16px;">
55279          * <li>editor - This editor</li>
55280              * <li>grid - This grid</li>
55281              * <li>record - The record being edited</li>
55282              * <li>field - The field name being edited</li>
55283              * <li>value - The value being set</li>
55284              * <li>originalValue - The original value for the field, before the edit.</li>
55285              * <li>row - The grid row index</li>
55286              * <li>column - The grid column index</li>
55287              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55288              * </ul>
55289              * @param {Object} e An edit event (see above for description)
55290              */
55291             "validateedit" : true
55292         });
55293     this.on("bodyscroll", this.stopEditing,  this);
55294     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55295 };
55296
55297 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55298     /**
55299      * @cfg {Number} clicksToEdit
55300      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55301      */
55302     clicksToEdit: 2,
55303
55304     // private
55305     isEditor : true,
55306     // private
55307     trackMouseOver: false, // causes very odd FF errors
55308
55309     onCellDblClick : function(g, row, col){
55310         this.startEditing(row, col);
55311     },
55312
55313     onEditComplete : function(ed, value, startValue){
55314         this.editing = false;
55315         this.activeEditor = null;
55316         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55317         var r = ed.record;
55318         var field = this.colModel.getDataIndex(ed.col);
55319         var e = {
55320             grid: this,
55321             record: r,
55322             field: field,
55323             originalValue: startValue,
55324             value: value,
55325             row: ed.row,
55326             column: ed.col,
55327             cancel:false,
55328             editor: ed
55329         };
55330         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55331         cell.show();
55332           
55333         if(String(value) !== String(startValue)){
55334             
55335             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55336                 r.set(field, e.value);
55337                 // if we are dealing with a combo box..
55338                 // then we also set the 'name' colum to be the displayField
55339                 if (ed.field.displayField && ed.field.name) {
55340                     r.set(ed.field.name, ed.field.el.dom.value);
55341                 }
55342                 
55343                 delete e.cancel; //?? why!!!
55344                 this.fireEvent("afteredit", e);
55345             }
55346         } else {
55347             this.fireEvent("afteredit", e); // always fire it!
55348         }
55349         this.view.focusCell(ed.row, ed.col);
55350     },
55351
55352     /**
55353      * Starts editing the specified for the specified row/column
55354      * @param {Number} rowIndex
55355      * @param {Number} colIndex
55356      */
55357     startEditing : function(row, col){
55358         this.stopEditing();
55359         if(this.colModel.isCellEditable(col, row)){
55360             this.view.ensureVisible(row, col, true);
55361           
55362             var r = this.dataSource.getAt(row);
55363             var field = this.colModel.getDataIndex(col);
55364             var cell = Roo.get(this.view.getCell(row,col));
55365             var e = {
55366                 grid: this,
55367                 record: r,
55368                 field: field,
55369                 value: r.data[field],
55370                 row: row,
55371                 column: col,
55372                 cancel:false 
55373             };
55374             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55375                 this.editing = true;
55376                 var ed = this.colModel.getCellEditor(col, row);
55377                 
55378                 if (!ed) {
55379                     return;
55380                 }
55381                 if(!ed.rendered){
55382                     ed.render(ed.parentEl || document.body);
55383                 }
55384                 ed.field.reset();
55385                
55386                 cell.hide();
55387                 
55388                 (function(){ // complex but required for focus issues in safari, ie and opera
55389                     ed.row = row;
55390                     ed.col = col;
55391                     ed.record = r;
55392                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55393                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55394                     this.activeEditor = ed;
55395                     var v = r.data[field];
55396                     ed.startEdit(this.view.getCell(row, col), v);
55397                     // combo's with 'displayField and name set
55398                     if (ed.field.displayField && ed.field.name) {
55399                         ed.field.el.dom.value = r.data[ed.field.name];
55400                     }
55401                     
55402                     
55403                 }).defer(50, this);
55404             }
55405         }
55406     },
55407         
55408     /**
55409      * Stops any active editing
55410      */
55411     stopEditing : function(){
55412         if(this.activeEditor){
55413             this.activeEditor.completeEdit();
55414         }
55415         this.activeEditor = null;
55416     },
55417         
55418          /**
55419      * Called to get grid's drag proxy text, by default returns this.ddText.
55420      * @return {String}
55421      */
55422     getDragDropText : function(){
55423         var count = this.selModel.getSelectedCell() ? 1 : 0;
55424         return String.format(this.ddText, count, count == 1 ? '' : 's');
55425     }
55426         
55427 });/*
55428  * Based on:
55429  * Ext JS Library 1.1.1
55430  * Copyright(c) 2006-2007, Ext JS, LLC.
55431  *
55432  * Originally Released Under LGPL - original licence link has changed is not relivant.
55433  *
55434  * Fork - LGPL
55435  * <script type="text/javascript">
55436  */
55437
55438 // private - not really -- you end up using it !
55439 // This is a support class used internally by the Grid components
55440
55441 /**
55442  * @class Roo.grid.GridEditor
55443  * @extends Roo.Editor
55444  * Class for creating and editable grid elements.
55445  * @param {Object} config any settings (must include field)
55446  */
55447 Roo.grid.GridEditor = function(field, config){
55448     if (!config && field.field) {
55449         config = field;
55450         field = Roo.factory(config.field, Roo.form);
55451     }
55452     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55453     field.monitorTab = false;
55454 };
55455
55456 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55457     
55458     /**
55459      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55460      */
55461     
55462     alignment: "tl-tl",
55463     autoSize: "width",
55464     hideEl : false,
55465     cls: "x-small-editor x-grid-editor",
55466     shim:false,
55467     shadow:"frame"
55468 });/*
55469  * Based on:
55470  * Ext JS Library 1.1.1
55471  * Copyright(c) 2006-2007, Ext JS, LLC.
55472  *
55473  * Originally Released Under LGPL - original licence link has changed is not relivant.
55474  *
55475  * Fork - LGPL
55476  * <script type="text/javascript">
55477  */
55478   
55479
55480   
55481 Roo.grid.PropertyRecord = Roo.data.Record.create([
55482     {name:'name',type:'string'},  'value'
55483 ]);
55484
55485
55486 Roo.grid.PropertyStore = function(grid, source){
55487     this.grid = grid;
55488     this.store = new Roo.data.Store({
55489         recordType : Roo.grid.PropertyRecord
55490     });
55491     this.store.on('update', this.onUpdate,  this);
55492     if(source){
55493         this.setSource(source);
55494     }
55495     Roo.grid.PropertyStore.superclass.constructor.call(this);
55496 };
55497
55498
55499
55500 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55501     setSource : function(o){
55502         this.source = o;
55503         this.store.removeAll();
55504         var data = [];
55505         for(var k in o){
55506             if(this.isEditableValue(o[k])){
55507                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55508             }
55509         }
55510         this.store.loadRecords({records: data}, {}, true);
55511     },
55512
55513     onUpdate : function(ds, record, type){
55514         if(type == Roo.data.Record.EDIT){
55515             var v = record.data['value'];
55516             var oldValue = record.modified['value'];
55517             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55518                 this.source[record.id] = v;
55519                 record.commit();
55520                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55521             }else{
55522                 record.reject();
55523             }
55524         }
55525     },
55526
55527     getProperty : function(row){
55528        return this.store.getAt(row);
55529     },
55530
55531     isEditableValue: function(val){
55532         if(val && val instanceof Date){
55533             return true;
55534         }else if(typeof val == 'object' || typeof val == 'function'){
55535             return false;
55536         }
55537         return true;
55538     },
55539
55540     setValue : function(prop, value){
55541         this.source[prop] = value;
55542         this.store.getById(prop).set('value', value);
55543     },
55544
55545     getSource : function(){
55546         return this.source;
55547     }
55548 });
55549
55550 Roo.grid.PropertyColumnModel = function(grid, store){
55551     this.grid = grid;
55552     var g = Roo.grid;
55553     g.PropertyColumnModel.superclass.constructor.call(this, [
55554         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55555         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55556     ]);
55557     this.store = store;
55558     this.bselect = Roo.DomHelper.append(document.body, {
55559         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55560             {tag: 'option', value: 'true', html: 'true'},
55561             {tag: 'option', value: 'false', html: 'false'}
55562         ]
55563     });
55564     Roo.id(this.bselect);
55565     var f = Roo.form;
55566     this.editors = {
55567         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55568         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55569         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55570         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55571         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55572     };
55573     this.renderCellDelegate = this.renderCell.createDelegate(this);
55574     this.renderPropDelegate = this.renderProp.createDelegate(this);
55575 };
55576
55577 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55578     
55579     
55580     nameText : 'Name',
55581     valueText : 'Value',
55582     
55583     dateFormat : 'm/j/Y',
55584     
55585     
55586     renderDate : function(dateVal){
55587         return dateVal.dateFormat(this.dateFormat);
55588     },
55589
55590     renderBool : function(bVal){
55591         return bVal ? 'true' : 'false';
55592     },
55593
55594     isCellEditable : function(colIndex, rowIndex){
55595         return colIndex == 1;
55596     },
55597
55598     getRenderer : function(col){
55599         return col == 1 ?
55600             this.renderCellDelegate : this.renderPropDelegate;
55601     },
55602
55603     renderProp : function(v){
55604         return this.getPropertyName(v);
55605     },
55606
55607     renderCell : function(val){
55608         var rv = val;
55609         if(val instanceof Date){
55610             rv = this.renderDate(val);
55611         }else if(typeof val == 'boolean'){
55612             rv = this.renderBool(val);
55613         }
55614         return Roo.util.Format.htmlEncode(rv);
55615     },
55616
55617     getPropertyName : function(name){
55618         var pn = this.grid.propertyNames;
55619         return pn && pn[name] ? pn[name] : name;
55620     },
55621
55622     getCellEditor : function(colIndex, rowIndex){
55623         var p = this.store.getProperty(rowIndex);
55624         var n = p.data['name'], val = p.data['value'];
55625         
55626         if(typeof(this.grid.customEditors[n]) == 'string'){
55627             return this.editors[this.grid.customEditors[n]];
55628         }
55629         if(typeof(this.grid.customEditors[n]) != 'undefined'){
55630             return this.grid.customEditors[n];
55631         }
55632         if(val instanceof Date){
55633             return this.editors['date'];
55634         }else if(typeof val == 'number'){
55635             return this.editors['number'];
55636         }else if(typeof val == 'boolean'){
55637             return this.editors['boolean'];
55638         }else{
55639             return this.editors['string'];
55640         }
55641     }
55642 });
55643
55644 /**
55645  * @class Roo.grid.PropertyGrid
55646  * @extends Roo.grid.EditorGrid
55647  * This class represents the  interface of a component based property grid control.
55648  * <br><br>Usage:<pre><code>
55649  var grid = new Roo.grid.PropertyGrid("my-container-id", {
55650       
55651  });
55652  // set any options
55653  grid.render();
55654  * </code></pre>
55655   
55656  * @constructor
55657  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55658  * The container MUST have some type of size defined for the grid to fill. The container will be
55659  * automatically set to position relative if it isn't already.
55660  * @param {Object} config A config object that sets properties on this grid.
55661  */
55662 Roo.grid.PropertyGrid = function(container, config){
55663     config = config || {};
55664     var store = new Roo.grid.PropertyStore(this);
55665     this.store = store;
55666     var cm = new Roo.grid.PropertyColumnModel(this, store);
55667     store.store.sort('name', 'ASC');
55668     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
55669         ds: store.store,
55670         cm: cm,
55671         enableColLock:false,
55672         enableColumnMove:false,
55673         stripeRows:false,
55674         trackMouseOver: false,
55675         clicksToEdit:1
55676     }, config));
55677     this.getGridEl().addClass('x-props-grid');
55678     this.lastEditRow = null;
55679     this.on('columnresize', this.onColumnResize, this);
55680     this.addEvents({
55681          /**
55682              * @event beforepropertychange
55683              * Fires before a property changes (return false to stop?)
55684              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55685              * @param {String} id Record Id
55686              * @param {String} newval New Value
55687          * @param {String} oldval Old Value
55688              */
55689         "beforepropertychange": true,
55690         /**
55691              * @event propertychange
55692              * Fires after a property changes
55693              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55694              * @param {String} id Record Id
55695              * @param {String} newval New Value
55696          * @param {String} oldval Old Value
55697              */
55698         "propertychange": true
55699     });
55700     this.customEditors = this.customEditors || {};
55701 };
55702 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
55703     
55704      /**
55705      * @cfg {Object} customEditors map of colnames=> custom editors.
55706      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
55707      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
55708      * false disables editing of the field.
55709          */
55710     
55711       /**
55712      * @cfg {Object} propertyNames map of property Names to their displayed value
55713          */
55714     
55715     render : function(){
55716         Roo.grid.PropertyGrid.superclass.render.call(this);
55717         this.autoSize.defer(100, this);
55718     },
55719
55720     autoSize : function(){
55721         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55722         if(this.view){
55723             this.view.fitColumns();
55724         }
55725     },
55726
55727     onColumnResize : function(){
55728         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55729         this.autoSize();
55730     },
55731     /**
55732      * Sets the data for the Grid
55733      * accepts a Key => Value object of all the elements avaiable.
55734      * @param {Object} data  to appear in grid.
55735      */
55736     setSource : function(source){
55737         this.store.setSource(source);
55738         //this.autoSize();
55739     },
55740     /**
55741      * Gets all the data from the grid.
55742      * @return {Object} data  data stored in grid
55743      */
55744     getSource : function(){
55745         return this.store.getSource();
55746     }
55747 });/*
55748  * Based on:
55749  * Ext JS Library 1.1.1
55750  * Copyright(c) 2006-2007, Ext JS, LLC.
55751  *
55752  * Originally Released Under LGPL - original licence link has changed is not relivant.
55753  *
55754  * Fork - LGPL
55755  * <script type="text/javascript">
55756  */
55757  
55758 /**
55759  * @class Roo.LoadMask
55760  * A simple utility class for generically masking elements while loading data.  If the element being masked has
55761  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
55762  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
55763  * element's UpdateManager load indicator and will be destroyed after the initial load.
55764  * @constructor
55765  * Create a new LoadMask
55766  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
55767  * @param {Object} config The config object
55768  */
55769 Roo.LoadMask = function(el, config){
55770     this.el = Roo.get(el);
55771     Roo.apply(this, config);
55772     if(this.store){
55773         this.store.on('beforeload', this.onBeforeLoad, this);
55774         this.store.on('load', this.onLoad, this);
55775         this.store.on('loadexception', this.onLoadException, this);
55776         this.removeMask = false;
55777     }else{
55778         var um = this.el.getUpdateManager();
55779         um.showLoadIndicator = false; // disable the default indicator
55780         um.on('beforeupdate', this.onBeforeLoad, this);
55781         um.on('update', this.onLoad, this);
55782         um.on('failure', this.onLoad, this);
55783         this.removeMask = true;
55784     }
55785 };
55786
55787 Roo.LoadMask.prototype = {
55788     /**
55789      * @cfg {Boolean} removeMask
55790      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
55791      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
55792      */
55793     /**
55794      * @cfg {String} msg
55795      * The text to display in a centered loading message box (defaults to 'Loading...')
55796      */
55797     msg : 'Loading...',
55798     /**
55799      * @cfg {String} msgCls
55800      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
55801      */
55802     msgCls : 'x-mask-loading',
55803
55804     /**
55805      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
55806      * @type Boolean
55807      */
55808     disabled: false,
55809
55810     /**
55811      * Disables the mask to prevent it from being displayed
55812      */
55813     disable : function(){
55814        this.disabled = true;
55815     },
55816
55817     /**
55818      * Enables the mask so that it can be displayed
55819      */
55820     enable : function(){
55821         this.disabled = false;
55822     },
55823     
55824     onLoadException : function()
55825     {
55826         Roo.log(arguments);
55827         
55828         if (typeof(arguments[3]) != 'undefined') {
55829             Roo.MessageBox.alert("Error loading",arguments[3]);
55830         } 
55831         /*
55832         try {
55833             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
55834                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
55835             }   
55836         } catch(e) {
55837             
55838         }
55839         */
55840     
55841         
55842         
55843         this.el.unmask(this.removeMask);
55844     },
55845     // private
55846     onLoad : function()
55847     {
55848         this.el.unmask(this.removeMask);
55849     },
55850
55851     // private
55852     onBeforeLoad : function(){
55853         if(!this.disabled){
55854             this.el.mask(this.msg, this.msgCls);
55855         }
55856     },
55857
55858     // private
55859     destroy : function(){
55860         if(this.store){
55861             this.store.un('beforeload', this.onBeforeLoad, this);
55862             this.store.un('load', this.onLoad, this);
55863             this.store.un('loadexception', this.onLoadException, this);
55864         }else{
55865             var um = this.el.getUpdateManager();
55866             um.un('beforeupdate', this.onBeforeLoad, this);
55867             um.un('update', this.onLoad, this);
55868             um.un('failure', this.onLoad, this);
55869         }
55870     }
55871 };/*
55872  * Based on:
55873  * Ext JS Library 1.1.1
55874  * Copyright(c) 2006-2007, Ext JS, LLC.
55875  *
55876  * Originally Released Under LGPL - original licence link has changed is not relivant.
55877  *
55878  * Fork - LGPL
55879  * <script type="text/javascript">
55880  */
55881
55882
55883 /**
55884  * @class Roo.XTemplate
55885  * @extends Roo.Template
55886  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
55887 <pre><code>
55888 var t = new Roo.XTemplate(
55889         '&lt;select name="{name}"&gt;',
55890                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
55891         '&lt;/select&gt;'
55892 );
55893  
55894 // then append, applying the master template values
55895  </code></pre>
55896  *
55897  * Supported features:
55898  *
55899  *  Tags:
55900
55901 <pre><code>
55902       {a_variable} - output encoded.
55903       {a_variable.format:("Y-m-d")} - call a method on the variable
55904       {a_variable:raw} - unencoded output
55905       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
55906       {a_variable:this.method_on_template(...)} - call a method on the template object.
55907  
55908 </code></pre>
55909  *  The tpl tag:
55910 <pre><code>
55911         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
55912         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
55913         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
55914         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
55915   
55916         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
55917         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
55918 </code></pre>
55919  *      
55920  */
55921 Roo.XTemplate = function()
55922 {
55923     Roo.XTemplate.superclass.constructor.apply(this, arguments);
55924     if (this.html) {
55925         this.compile();
55926     }
55927 };
55928
55929
55930 Roo.extend(Roo.XTemplate, Roo.Template, {
55931
55932     /**
55933      * The various sub templates
55934      */
55935     tpls : false,
55936     /**
55937      *
55938      * basic tag replacing syntax
55939      * WORD:WORD()
55940      *
55941      * // you can fake an object call by doing this
55942      *  x.t:(test,tesT) 
55943      * 
55944      */
55945     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
55946
55947     /**
55948      * compile the template
55949      *
55950      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
55951      *
55952      */
55953     compile: function()
55954     {
55955         var s = this.html;
55956      
55957         s = ['<tpl>', s, '</tpl>'].join('');
55958     
55959         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
55960             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
55961             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
55962             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
55963             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
55964             m,
55965             id     = 0,
55966             tpls   = [];
55967     
55968         while(true == !!(m = s.match(re))){
55969             var forMatch   = m[0].match(nameRe),
55970                 ifMatch   = m[0].match(ifRe),
55971                 execMatch   = m[0].match(execRe),
55972                 namedMatch   = m[0].match(namedRe),
55973                 
55974                 exp  = null, 
55975                 fn   = null,
55976                 exec = null,
55977                 name = forMatch && forMatch[1] ? forMatch[1] : '';
55978                 
55979             if (ifMatch) {
55980                 // if - puts fn into test..
55981                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
55982                 if(exp){
55983                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
55984                 }
55985             }
55986             
55987             if (execMatch) {
55988                 // exec - calls a function... returns empty if true is  returned.
55989                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
55990                 if(exp){
55991                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
55992                 }
55993             }
55994             
55995             
55996             if (name) {
55997                 // for = 
55998                 switch(name){
55999                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
56000                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
56001                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
56002                 }
56003             }
56004             var uid = namedMatch ? namedMatch[1] : id;
56005             
56006             
56007             tpls.push({
56008                 id:     namedMatch ? namedMatch[1] : id,
56009                 target: name,
56010                 exec:   exec,
56011                 test:   fn,
56012                 body:   m[1] || ''
56013             });
56014             if (namedMatch) {
56015                 s = s.replace(m[0], '');
56016             } else { 
56017                 s = s.replace(m[0], '{xtpl'+ id + '}');
56018             }
56019             ++id;
56020         }
56021         this.tpls = [];
56022         for(var i = tpls.length-1; i >= 0; --i){
56023             this.compileTpl(tpls[i]);
56024             this.tpls[tpls[i].id] = tpls[i];
56025         }
56026         this.master = tpls[tpls.length-1];
56027         return this;
56028     },
56029     /**
56030      * same as applyTemplate, except it's done to one of the subTemplates
56031      * when using named templates, you can do:
56032      *
56033      * var str = pl.applySubTemplate('your-name', values);
56034      *
56035      * 
56036      * @param {Number} id of the template
56037      * @param {Object} values to apply to template
56038      * @param {Object} parent (normaly the instance of this object)
56039      */
56040     applySubTemplate : function(id, values, parent)
56041     {
56042         
56043         
56044         var t = this.tpls[id];
56045         
56046         
56047         try { 
56048             if(t.test && !t.test.call(this, values, parent)){
56049                 return '';
56050             }
56051         } catch(e) {
56052             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
56053             Roo.log(e.toString());
56054             Roo.log(t.test);
56055             return ''
56056         }
56057         try { 
56058             
56059             if(t.exec && t.exec.call(this, values, parent)){
56060                 return '';
56061             }
56062         } catch(e) {
56063             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
56064             Roo.log(e.toString());
56065             Roo.log(t.exec);
56066             return ''
56067         }
56068         try {
56069             var vs = t.target ? t.target.call(this, values, parent) : values;
56070             parent = t.target ? values : parent;
56071             if(t.target && vs instanceof Array){
56072                 var buf = [];
56073                 for(var i = 0, len = vs.length; i < len; i++){
56074                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
56075                 }
56076                 return buf.join('');
56077             }
56078             return t.compiled.call(this, vs, parent);
56079         } catch (e) {
56080             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
56081             Roo.log(e.toString());
56082             Roo.log(t.compiled);
56083             return '';
56084         }
56085     },
56086
56087     compileTpl : function(tpl)
56088     {
56089         var fm = Roo.util.Format;
56090         var useF = this.disableFormats !== true;
56091         var sep = Roo.isGecko ? "+" : ",";
56092         var undef = function(str) {
56093             Roo.log("Property not found :"  + str);
56094             return '';
56095         };
56096         
56097         var fn = function(m, name, format, args)
56098         {
56099             //Roo.log(arguments);
56100             args = args ? args.replace(/\\'/g,"'") : args;
56101             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
56102             if (typeof(format) == 'undefined') {
56103                 format= 'htmlEncode';
56104             }
56105             if (format == 'raw' ) {
56106                 format = false;
56107             }
56108             
56109             if(name.substr(0, 4) == 'xtpl'){
56110                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
56111             }
56112             
56113             // build an array of options to determine if value is undefined..
56114             
56115             // basically get 'xxxx.yyyy' then do
56116             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
56117             //    (function () { Roo.log("Property not found"); return ''; })() :
56118             //    ......
56119             
56120             var udef_ar = [];
56121             var lookfor = '';
56122             Roo.each(name.split('.'), function(st) {
56123                 lookfor += (lookfor.length ? '.': '') + st;
56124                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
56125             });
56126             
56127             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
56128             
56129             
56130             if(format && useF){
56131                 
56132                 args = args ? ',' + args : "";
56133                  
56134                 if(format.substr(0, 5) != "this."){
56135                     format = "fm." + format + '(';
56136                 }else{
56137                     format = 'this.call("'+ format.substr(5) + '", ';
56138                     args = ", values";
56139                 }
56140                 
56141                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
56142             }
56143              
56144             if (args.length) {
56145                 // called with xxyx.yuu:(test,test)
56146                 // change to ()
56147                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
56148             }
56149             // raw.. - :raw modifier..
56150             return "'"+ sep + udef_st  + name + ")"+sep+"'";
56151             
56152         };
56153         var body;
56154         // branched to use + in gecko and [].join() in others
56155         if(Roo.isGecko){
56156             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
56157                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
56158                     "';};};";
56159         }else{
56160             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
56161             body.push(tpl.body.replace(/(\r\n|\n)/g,
56162                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
56163             body.push("'].join('');};};");
56164             body = body.join('');
56165         }
56166         
56167         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
56168        
56169         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
56170         eval(body);
56171         
56172         return this;
56173     },
56174
56175     applyTemplate : function(values){
56176         return this.master.compiled.call(this, values, {});
56177         //var s = this.subs;
56178     },
56179
56180     apply : function(){
56181         return this.applyTemplate.apply(this, arguments);
56182     }
56183
56184  });
56185
56186 Roo.XTemplate.from = function(el){
56187     el = Roo.getDom(el);
56188     return new Roo.XTemplate(el.value || el.innerHTML);
56189 };