roojs-core.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             Roo.log('ns');
303             Roo.log(ns);
304             if (c.constructor == ns[c.xtype]) {// already created...
305                 return c;
306             }
307             if (ns[c.xtype]) {
308                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
309                 var ret = new ns[c.xtype](c);
310                 ret.xns = false;
311                 return ret;
312             }
313             c.xns = false; // prevent recursion..
314             return c;
315         },
316          /**
317          * Logs to console if it can.
318          *
319          * @param {String|Object} string
320          * @method log
321          */
322         log : function(s)
323         {
324             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
325                 return; // alerT?
326             }
327             console.log(s);
328             
329         },
330         /**
331          * 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.
332          * @param {Object} o
333          * @return {String}
334          */
335         urlEncode : function(o){
336             if(!o){
337                 return "";
338             }
339             var buf = [];
340             for(var key in o){
341                 var ov = o[key], k = Roo.encodeURIComponent(key);
342                 var type = typeof ov;
343                 if(type == 'undefined'){
344                     buf.push(k, "=&");
345                 }else if(type != "function" && type != "object"){
346                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
347                 }else if(ov instanceof Array){
348                     if (ov.length) {
349                             for(var i = 0, len = ov.length; i < len; i++) {
350                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
351                             }
352                         } else {
353                             buf.push(k, "=&");
354                         }
355                 }
356             }
357             buf.pop();
358             return buf.join("");
359         },
360          /**
361          * Safe version of encodeURIComponent
362          * @param {String} data 
363          * @return {String} 
364          */
365         
366         encodeURIComponent : function (data)
367         {
368             try {
369                 return encodeURIComponent(data);
370             } catch(e) {} // should be an uri encode error.
371             
372             if (data == '' || data == null){
373                return '';
374             }
375             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
376             function nibble_to_hex(nibble){
377                 var chars = '0123456789ABCDEF';
378                 return chars.charAt(nibble);
379             }
380             data = data.toString();
381             var buffer = '';
382             for(var i=0; i<data.length; i++){
383                 var c = data.charCodeAt(i);
384                 var bs = new Array();
385                 if (c > 0x10000){
386                         // 4 bytes
387                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
388                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
389                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
390                     bs[3] = 0x80 | (c & 0x3F);
391                 }else if (c > 0x800){
392                          // 3 bytes
393                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
394                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
395                     bs[2] = 0x80 | (c & 0x3F);
396                 }else if (c > 0x80){
397                        // 2 bytes
398                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
399                     bs[1] = 0x80 | (c & 0x3F);
400                 }else{
401                         // 1 byte
402                     bs[0] = c;
403                 }
404                 for(var j=0; j<bs.length; j++){
405                     var b = bs[j];
406                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
407                             + nibble_to_hex(b &0x0F);
408                     buffer += '%'+hex;
409                }
410             }
411             return buffer;    
412              
413         },
414
415         /**
416          * 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]}.
417          * @param {String} string
418          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
419          * @return {Object} A literal with members
420          */
421         urlDecode : function(string, overwrite){
422             if(!string || !string.length){
423                 return {};
424             }
425             var obj = {};
426             var pairs = string.split('&');
427             var pair, name, value;
428             for(var i = 0, len = pairs.length; i < len; i++){
429                 pair = pairs[i].split('=');
430                 name = decodeURIComponent(pair[0]);
431                 value = decodeURIComponent(pair[1]);
432                 if(overwrite !== true){
433                     if(typeof obj[name] == "undefined"){
434                         obj[name] = value;
435                     }else if(typeof obj[name] == "string"){
436                         obj[name] = [obj[name]];
437                         obj[name].push(value);
438                     }else{
439                         obj[name].push(value);
440                     }
441                 }else{
442                     obj[name] = value;
443                 }
444             }
445             return obj;
446         },
447
448         /**
449          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
450          * passed array is not really an array, your function is called once with it.
451          * The supplied function is called with (Object item, Number index, Array allItems).
452          * @param {Array/NodeList/Mixed} array
453          * @param {Function} fn
454          * @param {Object} scope
455          */
456         each : function(array, fn, scope){
457             if(typeof array.length == "undefined" || typeof array == "string"){
458                 array = [array];
459             }
460             for(var i = 0, len = array.length; i < len; i++){
461                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
462             }
463         },
464
465         // deprecated
466         combine : function(){
467             var as = arguments, l = as.length, r = [];
468             for(var i = 0; i < l; i++){
469                 var a = as[i];
470                 if(a instanceof Array){
471                     r = r.concat(a);
472                 }else if(a.length !== undefined && !a.substr){
473                     r = r.concat(Array.prototype.slice.call(a, 0));
474                 }else{
475                     r.push(a);
476                 }
477             }
478             return r;
479         },
480
481         /**
482          * Escapes the passed string for use in a regular expression
483          * @param {String} str
484          * @return {String}
485          */
486         escapeRe : function(s) {
487             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
488         },
489
490         // internal
491         callback : function(cb, scope, args, delay){
492             if(typeof cb == "function"){
493                 if(delay){
494                     cb.defer(delay, scope, args || []);
495                 }else{
496                     cb.apply(scope, args || []);
497                 }
498             }
499         },
500
501         /**
502          * Return the dom node for the passed string (id), dom node, or Roo.Element
503          * @param {String/HTMLElement/Roo.Element} el
504          * @return HTMLElement
505          */
506         getDom : function(el){
507             if(!el){
508                 return null;
509             }
510             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
511         },
512
513         /**
514         * Shorthand for {@link Roo.ComponentMgr#get}
515         * @param {String} id
516         * @return Roo.Component
517         */
518         getCmp : function(id){
519             return Roo.ComponentMgr.get(id);
520         },
521          
522         num : function(v, defaultValue){
523             if(typeof v != 'number'){
524                 return defaultValue;
525             }
526             return v;
527         },
528
529         destroy : function(){
530             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
531                 var as = a[i];
532                 if(as){
533                     if(as.dom){
534                         as.removeAllListeners();
535                         as.remove();
536                         continue;
537                     }
538                     if(typeof as.purgeListeners == 'function'){
539                         as.purgeListeners();
540                     }
541                     if(typeof as.destroy == 'function'){
542                         as.destroy();
543                     }
544                 }
545             }
546         },
547
548         // inpired by a similar function in mootools library
549         /**
550          * Returns the type of object that is passed in. If the object passed in is null or undefined it
551          * return false otherwise it returns one of the following values:<ul>
552          * <li><b>string</b>: If the object passed is a string</li>
553          * <li><b>number</b>: If the object passed is a number</li>
554          * <li><b>boolean</b>: If the object passed is a boolean value</li>
555          * <li><b>function</b>: If the object passed is a function reference</li>
556          * <li><b>object</b>: If the object passed is an object</li>
557          * <li><b>array</b>: If the object passed is an array</li>
558          * <li><b>regexp</b>: If the object passed is a regular expression</li>
559          * <li><b>element</b>: If the object passed is a DOM Element</li>
560          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
561          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
562          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
563          * @param {Mixed} object
564          * @return {String}
565          */
566         type : function(o){
567             if(o === undefined || o === null){
568                 return false;
569             }
570             if(o.htmlElement){
571                 return 'element';
572             }
573             var t = typeof o;
574             if(t == 'object' && o.nodeName) {
575                 switch(o.nodeType) {
576                     case 1: return 'element';
577                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
578                 }
579             }
580             if(t == 'object' || t == 'function') {
581                 switch(o.constructor) {
582                     case Array: return 'array';
583                     case RegExp: return 'regexp';
584                 }
585                 if(typeof o.length == 'number' && typeof o.item == 'function') {
586                     return 'nodelist';
587                 }
588             }
589             return t;
590         },
591
592         /**
593          * Returns true if the passed value is null, undefined or an empty string (optional).
594          * @param {Mixed} value The value to test
595          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
596          * @return {Boolean}
597          */
598         isEmpty : function(v, allowBlank){
599             return v === null || v === undefined || (!allowBlank ? v === '' : false);
600         },
601         
602         /** @type Boolean */
603         isOpera : isOpera,
604         /** @type Boolean */
605         isSafari : isSafari,
606         /** @type Boolean */
607         isIE : isIE,
608         /** @type Boolean */
609         isIE7 : isIE7,
610         /** @type Boolean */
611         isGecko : isGecko,
612         /** @type Boolean */
613         isBorderBox : isBorderBox,
614         /** @type Boolean */
615         isWindows : isWindows,
616         /** @type Boolean */
617         isLinux : isLinux,
618         /** @type Boolean */
619         isMac : isMac,
620         /** @type Boolean */
621         isTouch : isTouch,
622
623         /**
624          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
625          * you may want to set this to true.
626          * @type Boolean
627          */
628         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
629         
630         
631                 
632         /**
633          * Selects a single element as a Roo Element
634          * This is about as close as you can get to jQuery's $('do crazy stuff')
635          * @param {String} selector The selector/xpath query
636          * @param {Node} root (optional) The start of the query (defaults to document).
637          * @return {Roo.Element}
638          */
639         selectNode : function(selector, root) 
640         {
641             var node = Roo.DomQuery.selectNode(selector,root);
642             return node ? Roo.get(node) : new Roo.Element(false);
643         }
644         
645     });
646
647
648 })();
649
650 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
651                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
652 /*
653  * Based on:
654  * Ext JS Library 1.1.1
655  * Copyright(c) 2006-2007, Ext JS, LLC.
656  *
657  * Originally Released Under LGPL - original licence link has changed is not relivant.
658  *
659  * Fork - LGPL
660  * <script type="text/javascript">
661  */
662
663 (function() {    
664     // wrappedn so fnCleanup is not in global scope...
665     if(Roo.isIE) {
666         function fnCleanUp() {
667             var p = Function.prototype;
668             delete p.createSequence;
669             delete p.defer;
670             delete p.createDelegate;
671             delete p.createCallback;
672             delete p.createInterceptor;
673
674             window.detachEvent("onunload", fnCleanUp);
675         }
676         window.attachEvent("onunload", fnCleanUp);
677     }
678 })();
679
680
681 /**
682  * @class Function
683  * These functions are available on every Function object (any JavaScript function).
684  */
685 Roo.apply(Function.prototype, {
686      /**
687      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
688      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
689      * Will create a function that is bound to those 2 args.
690      * @return {Function} The new function
691     */
692     createCallback : function(/*args...*/){
693         // make args available, in function below
694         var args = arguments;
695         var method = this;
696         return function() {
697             return method.apply(window, args);
698         };
699     },
700
701     /**
702      * Creates a delegate (callback) that sets the scope to obj.
703      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
704      * Will create a function that is automatically scoped to this.
705      * @param {Object} obj (optional) The object for which the scope is set
706      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
707      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
708      *                                             if a number the args are inserted at the specified position
709      * @return {Function} The new function
710      */
711     createDelegate : function(obj, args, appendArgs){
712         var method = this;
713         return function() {
714             var callArgs = args || arguments;
715             if(appendArgs === true){
716                 callArgs = Array.prototype.slice.call(arguments, 0);
717                 callArgs = callArgs.concat(args);
718             }else if(typeof appendArgs == "number"){
719                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
720                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
721                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
722             }
723             return method.apply(obj || window, callArgs);
724         };
725     },
726
727     /**
728      * Calls this function after the number of millseconds specified.
729      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
730      * @param {Object} obj (optional) The object for which the scope is set
731      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
732      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
733      *                                             if a number the args are inserted at the specified position
734      * @return {Number} The timeout id that can be used with clearTimeout
735      */
736     defer : function(millis, obj, args, appendArgs){
737         var fn = this.createDelegate(obj, args, appendArgs);
738         if(millis){
739             return setTimeout(fn, millis);
740         }
741         fn();
742         return 0;
743     },
744     /**
745      * Create a combined function call sequence of the original function + the passed function.
746      * The resulting function returns the results of the original function.
747      * The passed fcn is called with the parameters of the original function
748      * @param {Function} fcn The function to sequence
749      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
750      * @return {Function} The new function
751      */
752     createSequence : function(fcn, scope){
753         if(typeof fcn != "function"){
754             return this;
755         }
756         var method = this;
757         return function() {
758             var retval = method.apply(this || window, arguments);
759             fcn.apply(scope || this || window, arguments);
760             return retval;
761         };
762     },
763
764     /**
765      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
766      * The resulting function returns the results of the original function.
767      * The passed fcn is called with the parameters of the original function.
768      * @addon
769      * @param {Function} fcn The function to call before the original
770      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
771      * @return {Function} The new function
772      */
773     createInterceptor : function(fcn, scope){
774         if(typeof fcn != "function"){
775             return this;
776         }
777         var method = this;
778         return function() {
779             fcn.target = this;
780             fcn.method = method;
781             if(fcn.apply(scope || this || window, arguments) === false){
782                 return;
783             }
784             return method.apply(this || window, arguments);
785         };
786     }
787 });
788 /*
789  * Based on:
790  * Ext JS Library 1.1.1
791  * Copyright(c) 2006-2007, Ext JS, LLC.
792  *
793  * Originally Released Under LGPL - original licence link has changed is not relivant.
794  *
795  * Fork - LGPL
796  * <script type="text/javascript">
797  */
798
799 Roo.applyIf(String, {
800     
801     /** @scope String */
802     
803     /**
804      * Escapes the passed string for ' and \
805      * @param {String} string The string to escape
806      * @return {String} The escaped string
807      * @static
808      */
809     escape : function(string) {
810         return string.replace(/('|\\)/g, "\\$1");
811     },
812
813     /**
814      * Pads the left side of a string with a specified character.  This is especially useful
815      * for normalizing number and date strings.  Example usage:
816      * <pre><code>
817 var s = String.leftPad('123', 5, '0');
818 // s now contains the string: '00123'
819 </code></pre>
820      * @param {String} string The original string
821      * @param {Number} size The total length of the output string
822      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
823      * @return {String} The padded string
824      * @static
825      */
826     leftPad : function (val, size, ch) {
827         var result = new String(val);
828         if(ch === null || ch === undefined || ch === '') {
829             ch = " ";
830         }
831         while (result.length < size) {
832             result = ch + result;
833         }
834         return result;
835     },
836
837     /**
838      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
839      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
840      * <pre><code>
841 var cls = 'my-class', text = 'Some text';
842 var s = String.format('<div class="{0}">{1}</div>', cls, text);
843 // s now contains the string: '<div class="my-class">Some text</div>'
844 </code></pre>
845      * @param {String} string The tokenized string to be formatted
846      * @param {String} value1 The value to replace token {0}
847      * @param {String} value2 Etc...
848      * @return {String} The formatted string
849      * @static
850      */
851     format : function(format){
852         var args = Array.prototype.slice.call(arguments, 1);
853         return format.replace(/\{(\d+)\}/g, function(m, i){
854             return Roo.util.Format.htmlEncode(args[i]);
855         });
856     }
857 });
858
859 /**
860  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
861  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
862  * they are already different, the first value passed in is returned.  Note that this method returns the new value
863  * but does not change the current string.
864  * <pre><code>
865 // alternate sort directions
866 sort = sort.toggle('ASC', 'DESC');
867
868 // instead of conditional logic:
869 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
870 </code></pre>
871  * @param {String} value The value to compare to the current string
872  * @param {String} other The new value to use if the string already equals the first value passed in
873  * @return {String} The new value
874  */
875  
876 String.prototype.toggle = function(value, other){
877     return this == value ? other : value;
878 };/*
879  * Based on:
880  * Ext JS Library 1.1.1
881  * Copyright(c) 2006-2007, Ext JS, LLC.
882  *
883  * Originally Released Under LGPL - original licence link has changed is not relivant.
884  *
885  * Fork - LGPL
886  * <script type="text/javascript">
887  */
888
889  /**
890  * @class Number
891  */
892 Roo.applyIf(Number.prototype, {
893     /**
894      * Checks whether or not the current number is within a desired range.  If the number is already within the
895      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
896      * exceeded.  Note that this method returns the constrained value but does not change the current number.
897      * @param {Number} min The minimum number in the range
898      * @param {Number} max The maximum number in the range
899      * @return {Number} The constrained value if outside the range, otherwise the current value
900      */
901     constrain : function(min, max){
902         return Math.min(Math.max(this, min), max);
903     }
904 });/*
905  * Based on:
906  * Ext JS Library 1.1.1
907  * Copyright(c) 2006-2007, Ext JS, LLC.
908  *
909  * Originally Released Under LGPL - original licence link has changed is not relivant.
910  *
911  * Fork - LGPL
912  * <script type="text/javascript">
913  */
914  /**
915  * @class Array
916  */
917 Roo.applyIf(Array.prototype, {
918     /**
919      * Checks whether or not the specified object exists in the array.
920      * @param {Object} o The object to check for
921      * @return {Number} The index of o in the array (or -1 if it is not found)
922      */
923     indexOf : function(o){
924        for (var i = 0, len = this.length; i < len; i++){
925               if(this[i] == o) return i;
926        }
927            return -1;
928     },
929
930     /**
931      * Removes the specified object from the array.  If the object is not found nothing happens.
932      * @param {Object} o The object to remove
933      */
934     remove : function(o){
935        var index = this.indexOf(o);
936        if(index != -1){
937            this.splice(index, 1);
938        }
939     },
940     /**
941      * Map (JS 1.6 compatibility)
942      * @param {Function} function  to call
943      */
944     map : function(fun )
945     {
946         var len = this.length >>> 0;
947         if (typeof fun != "function")
948             throw new TypeError();
949
950         var res = new Array(len);
951         var thisp = arguments[1];
952         for (var i = 0; i < len; i++)
953         {
954             if (i in this)
955                 res[i] = fun.call(thisp, this[i], i, this);
956         }
957
958         return res;
959     }
960     
961 });
962
963
964  /*
965  * Based on:
966  * Ext JS Library 1.1.1
967  * Copyright(c) 2006-2007, Ext JS, LLC.
968  *
969  * Originally Released Under LGPL - original licence link has changed is not relivant.
970  *
971  * Fork - LGPL
972  * <script type="text/javascript">
973  */
974
975 /**
976  * @class Date
977  *
978  * The date parsing and format syntax is a subset of
979  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
980  * supported will provide results equivalent to their PHP versions.
981  *
982  * Following is the list of all currently supported formats:
983  *<pre>
984 Sample date:
985 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
986
987 Format  Output      Description
988 ------  ----------  --------------------------------------------------------------
989   d      10         Day of the month, 2 digits with leading zeros
990   D      Wed        A textual representation of a day, three letters
991   j      10         Day of the month without leading zeros
992   l      Wednesday  A full textual representation of the day of the week
993   S      th         English ordinal day of month suffix, 2 chars (use with j)
994   w      3          Numeric representation of the day of the week
995   z      9          The julian date, or day of the year (0-365)
996   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
997   F      January    A full textual representation of the month
998   m      01         Numeric representation of a month, with leading zeros
999   M      Jan        Month name abbreviation, three letters
1000   n      1          Numeric representation of a month, without leading zeros
1001   t      31         Number of days in the given month
1002   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1003   Y      2007       A full numeric representation of a year, 4 digits
1004   y      07         A two digit representation of a year
1005   a      pm         Lowercase Ante meridiem and Post meridiem
1006   A      PM         Uppercase Ante meridiem and Post meridiem
1007   g      3          12-hour format of an hour without leading zeros
1008   G      15         24-hour format of an hour without leading zeros
1009   h      03         12-hour format of an hour with leading zeros
1010   H      15         24-hour format of an hour with leading zeros
1011   i      05         Minutes with leading zeros
1012   s      01         Seconds, with leading zeros
1013   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1014   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1015   T      CST        Timezone setting of the machine running the code
1016   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1017 </pre>
1018  *
1019  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1020  * <pre><code>
1021 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1022 document.write(dt.format('Y-m-d'));                         //2007-01-10
1023 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1024 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
1025  </code></pre>
1026  *
1027  * Here are some standard date/time patterns that you might find helpful.  They
1028  * are not part of the source of Date.js, but to use them you can simply copy this
1029  * block of code into any script that is included after Date.js and they will also become
1030  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1031  * <pre><code>
1032 Date.patterns = {
1033     ISO8601Long:"Y-m-d H:i:s",
1034     ISO8601Short:"Y-m-d",
1035     ShortDate: "n/j/Y",
1036     LongDate: "l, F d, Y",
1037     FullDateTime: "l, F d, Y g:i:s A",
1038     MonthDay: "F d",
1039     ShortTime: "g:i A",
1040     LongTime: "g:i:s A",
1041     SortableDateTime: "Y-m-d\\TH:i:s",
1042     UniversalSortableDateTime: "Y-m-d H:i:sO",
1043     YearMonth: "F, Y"
1044 };
1045 </code></pre>
1046  *
1047  * Example usage:
1048  * <pre><code>
1049 var dt = new Date();
1050 document.write(dt.format(Date.patterns.ShortDate));
1051  </code></pre>
1052  */
1053
1054 /*
1055  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1056  * They generate precompiled functions from date formats instead of parsing and
1057  * processing the pattern every time you format a date.  These functions are available
1058  * on every Date object (any javascript function).
1059  *
1060  * The original article and download are here:
1061  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1062  *
1063  */
1064  
1065  
1066  // was in core
1067 /**
1068  Returns the number of milliseconds between this date and date
1069  @param {Date} date (optional) Defaults to now
1070  @return {Number} The diff in milliseconds
1071  @member Date getElapsed
1072  */
1073 Date.prototype.getElapsed = function(date) {
1074         return Math.abs((date || new Date()).getTime()-this.getTime());
1075 };
1076 // was in date file..
1077
1078
1079 // private
1080 Date.parseFunctions = {count:0};
1081 // private
1082 Date.parseRegexes = [];
1083 // private
1084 Date.formatFunctions = {count:0};
1085
1086 // private
1087 Date.prototype.dateFormat = function(format) {
1088     if (Date.formatFunctions[format] == null) {
1089         Date.createNewFormat(format);
1090     }
1091     var func = Date.formatFunctions[format];
1092     return this[func]();
1093 };
1094
1095
1096 /**
1097  * Formats a date given the supplied format string
1098  * @param {String} format The format string
1099  * @return {String} The formatted date
1100  * @method
1101  */
1102 Date.prototype.format = Date.prototype.dateFormat;
1103
1104 // private
1105 Date.createNewFormat = function(format) {
1106     var funcName = "format" + Date.formatFunctions.count++;
1107     Date.formatFunctions[format] = funcName;
1108     var code = "Date.prototype." + funcName + " = function(){return ";
1109     var special = false;
1110     var ch = '';
1111     for (var i = 0; i < format.length; ++i) {
1112         ch = format.charAt(i);
1113         if (!special && ch == "\\") {
1114             special = true;
1115         }
1116         else if (special) {
1117             special = false;
1118             code += "'" + String.escape(ch) + "' + ";
1119         }
1120         else {
1121             code += Date.getFormatCode(ch);
1122         }
1123     }
1124     /** eval:var:zzzzzzzzzzzzz */
1125     eval(code.substring(0, code.length - 3) + ";}");
1126 };
1127
1128 // private
1129 Date.getFormatCode = function(character) {
1130     switch (character) {
1131     case "d":
1132         return "String.leftPad(this.getDate(), 2, '0') + ";
1133     case "D":
1134         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1135     case "j":
1136         return "this.getDate() + ";
1137     case "l":
1138         return "Date.dayNames[this.getDay()] + ";
1139     case "S":
1140         return "this.getSuffix() + ";
1141     case "w":
1142         return "this.getDay() + ";
1143     case "z":
1144         return "this.getDayOfYear() + ";
1145     case "W":
1146         return "this.getWeekOfYear() + ";
1147     case "F":
1148         return "Date.monthNames[this.getMonth()] + ";
1149     case "m":
1150         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1151     case "M":
1152         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1153     case "n":
1154         return "(this.getMonth() + 1) + ";
1155     case "t":
1156         return "this.getDaysInMonth() + ";
1157     case "L":
1158         return "(this.isLeapYear() ? 1 : 0) + ";
1159     case "Y":
1160         return "this.getFullYear() + ";
1161     case "y":
1162         return "('' + this.getFullYear()).substring(2, 4) + ";
1163     case "a":
1164         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1165     case "A":
1166         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1167     case "g":
1168         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1169     case "G":
1170         return "this.getHours() + ";
1171     case "h":
1172         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1173     case "H":
1174         return "String.leftPad(this.getHours(), 2, '0') + ";
1175     case "i":
1176         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1177     case "s":
1178         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1179     case "O":
1180         return "this.getGMTOffset() + ";
1181     case "P":
1182         return "this.getGMTColonOffset() + ";
1183     case "T":
1184         return "this.getTimezone() + ";
1185     case "Z":
1186         return "(this.getTimezoneOffset() * -60) + ";
1187     default:
1188         return "'" + String.escape(character) + "' + ";
1189     }
1190 };
1191
1192 /**
1193  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1194  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1195  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1196  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1197  * string or the parse operation will fail.
1198  * Example Usage:
1199 <pre><code>
1200 //dt = Fri May 25 2007 (current date)
1201 var dt = new Date();
1202
1203 //dt = Thu May 25 2006 (today's month/day in 2006)
1204 dt = Date.parseDate("2006", "Y");
1205
1206 //dt = Sun Jan 15 2006 (all date parts specified)
1207 dt = Date.parseDate("2006-1-15", "Y-m-d");
1208
1209 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1210 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1211 </code></pre>
1212  * @param {String} input The unparsed date as a string
1213  * @param {String} format The format the date is in
1214  * @return {Date} The parsed date
1215  * @static
1216  */
1217 Date.parseDate = function(input, format) {
1218     if (Date.parseFunctions[format] == null) {
1219         Date.createParser(format);
1220     }
1221     var func = Date.parseFunctions[format];
1222     return Date[func](input);
1223 };
1224 /**
1225  * @private
1226  */
1227 Date.createParser = function(format) {
1228     var funcName = "parse" + Date.parseFunctions.count++;
1229     var regexNum = Date.parseRegexes.length;
1230     var currentGroup = 1;
1231     Date.parseFunctions[format] = funcName;
1232
1233     var code = "Date." + funcName + " = function(input){\n"
1234         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1235         + "var d = new Date();\n"
1236         + "y = d.getFullYear();\n"
1237         + "m = d.getMonth();\n"
1238         + "d = d.getDate();\n"
1239         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1240         + "if (results && results.length > 0) {";
1241     var regex = "";
1242
1243     var special = false;
1244     var ch = '';
1245     for (var i = 0; i < format.length; ++i) {
1246         ch = format.charAt(i);
1247         if (!special && ch == "\\") {
1248             special = true;
1249         }
1250         else if (special) {
1251             special = false;
1252             regex += String.escape(ch);
1253         }
1254         else {
1255             var obj = Date.formatCodeToRegex(ch, currentGroup);
1256             currentGroup += obj.g;
1257             regex += obj.s;
1258             if (obj.g && obj.c) {
1259                 code += obj.c;
1260             }
1261         }
1262     }
1263
1264     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i, s);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1267         + "{v = new Date(y, m, d, h, i);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1269         + "{v = new Date(y, m, d, h);}\n"
1270         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1271         + "{v = new Date(y, m, d);}\n"
1272         + "else if (y >= 0 && m >= 0)\n"
1273         + "{v = new Date(y, m);}\n"
1274         + "else if (y >= 0)\n"
1275         + "{v = new Date(y);}\n"
1276         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1277         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1278         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1279         + ";}";
1280
1281     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1282     /** eval:var:zzzzzzzzzzzzz */
1283     eval(code);
1284 };
1285
1286 // private
1287 Date.formatCodeToRegex = function(character, currentGroup) {
1288     switch (character) {
1289     case "D":
1290         return {g:0,
1291         c:null,
1292         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1293     case "j":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{1,2})"}; // day of month without leading zeroes
1297     case "d":
1298         return {g:1,
1299             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1300             s:"(\\d{2})"}; // day of month with leading zeroes
1301     case "l":
1302         return {g:0,
1303             c:null,
1304             s:"(?:" + Date.dayNames.join("|") + ")"};
1305     case "S":
1306         return {g:0,
1307             c:null,
1308             s:"(?:st|nd|rd|th)"};
1309     case "w":
1310         return {g:0,
1311             c:null,
1312             s:"\\d"};
1313     case "z":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{1,3})"};
1317     case "W":
1318         return {g:0,
1319             c:null,
1320             s:"(?:\\d{2})"};
1321     case "F":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1324             s:"(" + Date.monthNames.join("|") + ")"};
1325     case "M":
1326         return {g:1,
1327             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1328             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1329     case "n":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1333     case "m":
1334         return {g:1,
1335             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1336             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1337     case "t":
1338         return {g:0,
1339             c:null,
1340             s:"\\d{1,2}"};
1341     case "L":
1342         return {g:0,
1343             c:null,
1344             s:"(?:1|0)"};
1345     case "Y":
1346         return {g:1,
1347             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1348             s:"(\\d{4})"};
1349     case "y":
1350         return {g:1,
1351             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1352                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1353             s:"(\\d{1,2})"};
1354     case "a":
1355         return {g:1,
1356             c:"if (results[" + currentGroup + "] == 'am') {\n"
1357                 + "if (h == 12) { h = 0; }\n"
1358                 + "} else { if (h < 12) { h += 12; }}",
1359             s:"(am|pm)"};
1360     case "A":
1361         return {g:1,
1362             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1363                 + "if (h == 12) { h = 0; }\n"
1364                 + "} else { if (h < 12) { h += 12; }}",
1365             s:"(AM|PM)"};
1366     case "g":
1367     case "G":
1368         return {g:1,
1369             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1370             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1371     case "h":
1372     case "H":
1373         return {g:1,
1374             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1376     case "i":
1377         return {g:1,
1378             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "s":
1381         return {g:1,
1382             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1383             s:"(\\d{2})"};
1384     case "O":
1385         return {g:1,
1386             c:[
1387                 "o = results[", currentGroup, "];\n",
1388                 "var sn = o.substring(0,1);\n", // get + / - sign
1389                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1390                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1391                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1392                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1393             ].join(""),
1394             s:"([+\-]\\d{2,4})"};
1395     
1396     
1397     case "P":
1398         return {g:1,
1399                 c:[
1400                    "o = results[", currentGroup, "];\n",
1401                    "var sn = o.substring(0,1);\n",
1402                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1403                    "var mn = o.substring(4,6) % 60;\n",
1404                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1405                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1406             ].join(""),
1407             s:"([+\-]\\d{4})"};
1408     case "T":
1409         return {g:0,
1410             c:null,
1411             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1412     case "Z":
1413         return {g:1,
1414             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1415                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1416             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1417     default:
1418         return {g:0,
1419             c:null,
1420             s:String.escape(character)};
1421     }
1422 };
1423
1424 /**
1425  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1426  * @return {String} The abbreviated timezone name (e.g. 'CST')
1427  */
1428 Date.prototype.getTimezone = function() {
1429     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1430 };
1431
1432 /**
1433  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1434  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1435  */
1436 Date.prototype.getGMTOffset = function() {
1437     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1438         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1439         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1440 };
1441
1442 /**
1443  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1444  * @return {String} 2-characters representing hours and 2-characters representing minutes
1445  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1446  */
1447 Date.prototype.getGMTColonOffset = function() {
1448         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1449                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1450                 + ":"
1451                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1452 }
1453
1454 /**
1455  * Get the numeric day number of the year, adjusted for leap year.
1456  * @return {Number} 0 through 364 (365 in leap years)
1457  */
1458 Date.prototype.getDayOfYear = function() {
1459     var num = 0;
1460     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1461     for (var i = 0; i < this.getMonth(); ++i) {
1462         num += Date.daysInMonth[i];
1463     }
1464     return num + this.getDate() - 1;
1465 };
1466
1467 /**
1468  * Get the string representation of the numeric week number of the year
1469  * (equivalent to the format specifier 'W').
1470  * @return {String} '00' through '52'
1471  */
1472 Date.prototype.getWeekOfYear = function() {
1473     // Skip to Thursday of this week
1474     var now = this.getDayOfYear() + (4 - this.getDay());
1475     // Find the first Thursday of the year
1476     var jan1 = new Date(this.getFullYear(), 0, 1);
1477     var then = (7 - jan1.getDay() + 4);
1478     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1479 };
1480
1481 /**
1482  * Whether or not the current date is in a leap year.
1483  * @return {Boolean} True if the current date is in a leap year, else false
1484  */
1485 Date.prototype.isLeapYear = function() {
1486     var year = this.getFullYear();
1487     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1488 };
1489
1490 /**
1491  * Get the first day of the current month, adjusted for leap year.  The returned value
1492  * is the numeric day index within the week (0-6) which can be used in conjunction with
1493  * the {@link #monthNames} array to retrieve the textual day name.
1494  * Example:
1495  *<pre><code>
1496 var dt = new Date('1/10/2007');
1497 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1498 </code></pre>
1499  * @return {Number} The day number (0-6)
1500  */
1501 Date.prototype.getFirstDayOfMonth = function() {
1502     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1503     return (day < 0) ? (day + 7) : day;
1504 };
1505
1506 /**
1507  * Get the last day of the current month, adjusted for leap year.  The returned value
1508  * is the numeric day index within the week (0-6) which can be used in conjunction with
1509  * the {@link #monthNames} array to retrieve the textual day name.
1510  * Example:
1511  *<pre><code>
1512 var dt = new Date('1/10/2007');
1513 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1514 </code></pre>
1515  * @return {Number} The day number (0-6)
1516  */
1517 Date.prototype.getLastDayOfMonth = function() {
1518     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1519     return (day < 0) ? (day + 7) : day;
1520 };
1521
1522
1523 /**
1524  * Get the first date of this date's month
1525  * @return {Date}
1526  */
1527 Date.prototype.getFirstDateOfMonth = function() {
1528     return new Date(this.getFullYear(), this.getMonth(), 1);
1529 };
1530
1531 /**
1532  * Get the last date of this date's month
1533  * @return {Date}
1534  */
1535 Date.prototype.getLastDateOfMonth = function() {
1536     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1537 };
1538 /**
1539  * Get the number of days in the current month, adjusted for leap year.
1540  * @return {Number} The number of days in the month
1541  */
1542 Date.prototype.getDaysInMonth = function() {
1543     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1544     return Date.daysInMonth[this.getMonth()];
1545 };
1546
1547 /**
1548  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1549  * @return {String} 'st, 'nd', 'rd' or 'th'
1550  */
1551 Date.prototype.getSuffix = function() {
1552     switch (this.getDate()) {
1553         case 1:
1554         case 21:
1555         case 31:
1556             return "st";
1557         case 2:
1558         case 22:
1559             return "nd";
1560         case 3:
1561         case 23:
1562             return "rd";
1563         default:
1564             return "th";
1565     }
1566 };
1567
1568 // private
1569 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1570
1571 /**
1572  * An array of textual month names.
1573  * Override these values for international dates, for example...
1574  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1575  * @type Array
1576  * @static
1577  */
1578 Date.monthNames =
1579    ["January",
1580     "February",
1581     "March",
1582     "April",
1583     "May",
1584     "June",
1585     "July",
1586     "August",
1587     "September",
1588     "October",
1589     "November",
1590     "December"];
1591
1592 /**
1593  * An array of textual day names.
1594  * Override these values for international dates, for example...
1595  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1596  * @type Array
1597  * @static
1598  */
1599 Date.dayNames =
1600    ["Sunday",
1601     "Monday",
1602     "Tuesday",
1603     "Wednesday",
1604     "Thursday",
1605     "Friday",
1606     "Saturday"];
1607
1608 // private
1609 Date.y2kYear = 50;
1610 // private
1611 Date.monthNumbers = {
1612     Jan:0,
1613     Feb:1,
1614     Mar:2,
1615     Apr:3,
1616     May:4,
1617     Jun:5,
1618     Jul:6,
1619     Aug:7,
1620     Sep:8,
1621     Oct:9,
1622     Nov:10,
1623     Dec:11};
1624
1625 /**
1626  * Creates and returns a new Date instance with the exact same date value as the called instance.
1627  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1628  * variable will also be changed.  When the intention is to create a new variable that will not
1629  * modify the original instance, you should create a clone.
1630  *
1631  * Example of correctly cloning a date:
1632  * <pre><code>
1633 //wrong way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig;
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 05 2006'!
1638
1639 //correct way:
1640 var orig = new Date('10/1/2006');
1641 var copy = orig.clone();
1642 copy.setDate(5);
1643 document.write(orig);  //returns 'Thu Oct 01 2006'
1644 </code></pre>
1645  * @return {Date} The new Date instance
1646  */
1647 Date.prototype.clone = function() {
1648         return new Date(this.getTime());
1649 };
1650
1651 /**
1652  * Clears any time information from this date
1653  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1654  @return {Date} this or the clone
1655  */
1656 Date.prototype.clearTime = function(clone){
1657     if(clone){
1658         return this.clone().clearTime();
1659     }
1660     this.setHours(0);
1661     this.setMinutes(0);
1662     this.setSeconds(0);
1663     this.setMilliseconds(0);
1664     return this;
1665 };
1666
1667 // private
1668 // safari setMonth is broken
1669 if(Roo.isSafari){
1670     Date.brokenSetMonth = Date.prototype.setMonth;
1671         Date.prototype.setMonth = function(num){
1672                 if(num <= -1){
1673                         var n = Math.ceil(-num);
1674                         var back_year = Math.ceil(n/12);
1675                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1676                         this.setFullYear(this.getFullYear() - back_year);
1677                         return Date.brokenSetMonth.call(this, month);
1678                 } else {
1679                         return Date.brokenSetMonth.apply(this, arguments);
1680                 }
1681         };
1682 }
1683
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.MILLI = "ms";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.SECOND = "s";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.MINUTE = "mi";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.HOUR = "h";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.DAY = "d";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.MONTH = "mo";
1708 /** Date interval constant 
1709 * @static 
1710 * @type String */
1711 Date.YEAR = "y";
1712
1713 /**
1714  * Provides a convenient method of performing basic date arithmetic.  This method
1715  * does not modify the Date instance being called - it creates and returns
1716  * a new Date instance containing the resulting date value.
1717  *
1718  * Examples:
1719  * <pre><code>
1720 //Basic usage:
1721 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1722 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1723
1724 //Negative values will subtract correctly:
1725 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1726 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1727
1728 //You can even chain several calls together in one line!
1729 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1730 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1731  </code></pre>
1732  *
1733  * @param {String} interval   A valid date interval enum value
1734  * @param {Number} value      The amount to add to the current date
1735  * @return {Date} The new Date instance
1736  */
1737 Date.prototype.add = function(interval, value){
1738   var d = this.clone();
1739   if (!interval || value === 0) return d;
1740   switch(interval.toLowerCase()){
1741     case Date.MILLI:
1742       d.setMilliseconds(this.getMilliseconds() + value);
1743       break;
1744     case Date.SECOND:
1745       d.setSeconds(this.getSeconds() + value);
1746       break;
1747     case Date.MINUTE:
1748       d.setMinutes(this.getMinutes() + value);
1749       break;
1750     case Date.HOUR:
1751       d.setHours(this.getHours() + value);
1752       break;
1753     case Date.DAY:
1754       d.setDate(this.getDate() + value);
1755       break;
1756     case Date.MONTH:
1757       var day = this.getDate();
1758       if(day > 28){
1759           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1760       }
1761       d.setDate(day);
1762       d.setMonth(this.getMonth() + value);
1763       break;
1764     case Date.YEAR:
1765       d.setFullYear(this.getFullYear() + value);
1766       break;
1767   }
1768   return d;
1769 };
1770 /*
1771  * Based on:
1772  * Ext JS Library 1.1.1
1773  * Copyright(c) 2006-2007, Ext JS, LLC.
1774  *
1775  * Originally Released Under LGPL - original licence link has changed is not relivant.
1776  *
1777  * Fork - LGPL
1778  * <script type="text/javascript">
1779  */
1780
1781 /**
1782  * @class Roo.lib.Dom
1783  * @static
1784  * 
1785  * Dom utils (from YIU afaik)
1786  * 
1787  **/
1788 Roo.lib.Dom = {
1789     /**
1790      * Get the view width
1791      * @param {Boolean} full True will get the full document, otherwise it's the view width
1792      * @return {Number} The width
1793      */
1794      
1795     getViewWidth : function(full) {
1796         return full ? this.getDocumentWidth() : this.getViewportWidth();
1797     },
1798     /**
1799      * Get the view height
1800      * @param {Boolean} full True will get the full document, otherwise it's the view height
1801      * @return {Number} The height
1802      */
1803     getViewHeight : function(full) {
1804         return full ? this.getDocumentHeight() : this.getViewportHeight();
1805     },
1806
1807     getDocumentHeight: function() {
1808         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1809         return Math.max(scrollHeight, this.getViewportHeight());
1810     },
1811
1812     getDocumentWidth: function() {
1813         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1814         return Math.max(scrollWidth, this.getViewportWidth());
1815     },
1816
1817     getViewportHeight: function() {
1818         var height = self.innerHeight;
1819         var mode = document.compatMode;
1820
1821         if ((mode || Roo.isIE) && !Roo.isOpera) {
1822             height = (mode == "CSS1Compat") ?
1823                      document.documentElement.clientHeight :
1824                      document.body.clientHeight;
1825         }
1826
1827         return height;
1828     },
1829
1830     getViewportWidth: function() {
1831         var width = self.innerWidth;
1832         var mode = document.compatMode;
1833
1834         if (mode || Roo.isIE) {
1835             width = (mode == "CSS1Compat") ?
1836                     document.documentElement.clientWidth :
1837                     document.body.clientWidth;
1838         }
1839         return width;
1840     },
1841
1842     isAncestor : function(p, c) {
1843         p = Roo.getDom(p);
1844         c = Roo.getDom(c);
1845         if (!p || !c) {
1846             return false;
1847         }
1848
1849         if (p.contains && !Roo.isSafari) {
1850             return p.contains(c);
1851         } else if (p.compareDocumentPosition) {
1852             return !!(p.compareDocumentPosition(c) & 16);
1853         } else {
1854             var parent = c.parentNode;
1855             while (parent) {
1856                 if (parent == p) {
1857                     return true;
1858                 }
1859                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1860                     return false;
1861                 }
1862                 parent = parent.parentNode;
1863             }
1864             return false;
1865         }
1866     },
1867
1868     getRegion : function(el) {
1869         return Roo.lib.Region.getRegion(el);
1870     },
1871
1872     getY : function(el) {
1873         return this.getXY(el)[1];
1874     },
1875
1876     getX : function(el) {
1877         return this.getXY(el)[0];
1878     },
1879
1880     getXY : function(el) {
1881         var p, pe, b, scroll, bd = document.body;
1882         el = Roo.getDom(el);
1883         var fly = Roo.lib.AnimBase.fly;
1884         if (el.getBoundingClientRect) {
1885             b = el.getBoundingClientRect();
1886             scroll = fly(document).getScroll();
1887             return [b.left + scroll.left, b.top + scroll.top];
1888         }
1889         var x = 0, y = 0;
1890
1891         p = el;
1892
1893         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1894
1895         while (p) {
1896
1897             x += p.offsetLeft;
1898             y += p.offsetTop;
1899
1900             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1901                 hasAbsolute = true;
1902             }
1903
1904             if (Roo.isGecko) {
1905                 pe = fly(p);
1906
1907                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1908                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1909
1910
1911                 x += bl;
1912                 y += bt;
1913
1914
1915                 if (p != el && pe.getStyle('overflow') != 'visible') {
1916                     x += bl;
1917                     y += bt;
1918                 }
1919             }
1920             p = p.offsetParent;
1921         }
1922
1923         if (Roo.isSafari && hasAbsolute) {
1924             x -= bd.offsetLeft;
1925             y -= bd.offsetTop;
1926         }
1927
1928         if (Roo.isGecko && !hasAbsolute) {
1929             var dbd = fly(bd);
1930             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1931             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1932         }
1933
1934         p = el.parentNode;
1935         while (p && p != bd) {
1936             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1937                 x -= p.scrollLeft;
1938                 y -= p.scrollTop;
1939             }
1940             p = p.parentNode;
1941         }
1942         return [x, y];
1943     },
1944  
1945   
1946
1947
1948     setXY : function(el, xy) {
1949         el = Roo.fly(el, '_setXY');
1950         el.position();
1951         var pts = el.translatePoints(xy);
1952         if (xy[0] !== false) {
1953             el.dom.style.left = pts.left + "px";
1954         }
1955         if (xy[1] !== false) {
1956             el.dom.style.top = pts.top + "px";
1957         }
1958     },
1959
1960     setX : function(el, x) {
1961         this.setXY(el, [x, false]);
1962     },
1963
1964     setY : function(el, y) {
1965         this.setXY(el, [false, y]);
1966     }
1967 };
1968 /*
1969  * Portions of this file are based on pieces of Yahoo User Interface Library
1970  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1971  * YUI licensed under the BSD License:
1972  * http://developer.yahoo.net/yui/license.txt
1973  * <script type="text/javascript">
1974  *
1975  */
1976
1977 Roo.lib.Event = function() {
1978     var loadComplete = false;
1979     var listeners = [];
1980     var unloadListeners = [];
1981     var retryCount = 0;
1982     var onAvailStack = [];
1983     var counter = 0;
1984     var lastError = null;
1985
1986     return {
1987         POLL_RETRYS: 200,
1988         POLL_INTERVAL: 20,
1989         EL: 0,
1990         TYPE: 1,
1991         FN: 2,
1992         WFN: 3,
1993         OBJ: 3,
1994         ADJ_SCOPE: 4,
1995         _interval: null,
1996
1997         startInterval: function() {
1998             if (!this._interval) {
1999                 var self = this;
2000                 var callback = function() {
2001                     self._tryPreloadAttach();
2002                 };
2003                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2004
2005             }
2006         },
2007
2008         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2009             onAvailStack.push({ id:         p_id,
2010                 fn:         p_fn,
2011                 obj:        p_obj,
2012                 override:   p_override,
2013                 checkReady: false    });
2014
2015             retryCount = this.POLL_RETRYS;
2016             this.startInterval();
2017         },
2018
2019
2020         addListener: function(el, eventName, fn) {
2021             el = Roo.getDom(el);
2022             if (!el || !fn) {
2023                 return false;
2024             }
2025
2026             if ("unload" == eventName) {
2027                 unloadListeners[unloadListeners.length] =
2028                 [el, eventName, fn];
2029                 return true;
2030             }
2031
2032             var wrappedFn = function(e) {
2033                 return fn(Roo.lib.Event.getEvent(e));
2034             };
2035
2036             var li = [el, eventName, fn, wrappedFn];
2037
2038             var index = listeners.length;
2039             listeners[index] = li;
2040
2041             this.doAdd(el, eventName, wrappedFn, false);
2042             return true;
2043
2044         },
2045
2046
2047         removeListener: function(el, eventName, fn) {
2048             var i, len;
2049
2050             el = Roo.getDom(el);
2051
2052             if(!fn) {
2053                 return this.purgeElement(el, false, eventName);
2054             }
2055
2056
2057             if ("unload" == eventName) {
2058
2059                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2060                     var li = unloadListeners[i];
2061                     if (li &&
2062                         li[0] == el &&
2063                         li[1] == eventName &&
2064                         li[2] == fn) {
2065                         unloadListeners.splice(i, 1);
2066                         return true;
2067                     }
2068                 }
2069
2070                 return false;
2071             }
2072
2073             var cacheItem = null;
2074
2075
2076             var index = arguments[3];
2077
2078             if ("undefined" == typeof index) {
2079                 index = this._getCacheIndex(el, eventName, fn);
2080             }
2081
2082             if (index >= 0) {
2083                 cacheItem = listeners[index];
2084             }
2085
2086             if (!el || !cacheItem) {
2087                 return false;
2088             }
2089
2090             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2091
2092             delete listeners[index][this.WFN];
2093             delete listeners[index][this.FN];
2094             listeners.splice(index, 1);
2095
2096             return true;
2097
2098         },
2099
2100
2101         getTarget: function(ev, resolveTextNode) {
2102             ev = ev.browserEvent || ev;
2103             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2104             var t = ev.target || ev.srcElement;
2105             return this.resolveTextNode(t);
2106         },
2107
2108
2109         resolveTextNode: function(node) {
2110             if (Roo.isSafari && node && 3 == node.nodeType) {
2111                 return node.parentNode;
2112             } else {
2113                 return node;
2114             }
2115         },
2116
2117
2118         getPageX: function(ev) {
2119             ev = ev.browserEvent || ev;
2120             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2121             var x = ev.pageX;
2122             if (!x && 0 !== x) {
2123                 x = ev.clientX || 0;
2124
2125                 if (Roo.isIE) {
2126                     x += this.getScroll()[1];
2127                 }
2128             }
2129
2130             return x;
2131         },
2132
2133
2134         getPageY: function(ev) {
2135             ev = ev.browserEvent || ev;
2136             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2137             var y = ev.pageY;
2138             if (!y && 0 !== y) {
2139                 y = ev.clientY || 0;
2140
2141                 if (Roo.isIE) {
2142                     y += this.getScroll()[0];
2143                 }
2144             }
2145
2146
2147             return y;
2148         },
2149
2150
2151         getXY: function(ev) {
2152             ev = ev.browserEvent || ev;
2153             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2154             return [this.getPageX(ev), this.getPageY(ev)];
2155         },
2156
2157
2158         getRelatedTarget: function(ev) {
2159             ev = ev.browserEvent || ev;
2160             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2161             var t = ev.relatedTarget;
2162             if (!t) {
2163                 if (ev.type == "mouseout") {
2164                     t = ev.toElement;
2165                 } else if (ev.type == "mouseover") {
2166                     t = ev.fromElement;
2167                 }
2168             }
2169
2170             return this.resolveTextNode(t);
2171         },
2172
2173
2174         getTime: function(ev) {
2175             ev = ev.browserEvent || ev;
2176             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2177             if (!ev.time) {
2178                 var t = new Date().getTime();
2179                 try {
2180                     ev.time = t;
2181                 } catch(ex) {
2182                     this.lastError = ex;
2183                     return t;
2184                 }
2185             }
2186
2187             return ev.time;
2188         },
2189
2190
2191         stopEvent: function(ev) {
2192             this.stopPropagation(ev);
2193             this.preventDefault(ev);
2194         },
2195
2196
2197         stopPropagation: function(ev) {
2198             ev = ev.browserEvent || ev;
2199             if (ev.stopPropagation) {
2200                 ev.stopPropagation();
2201             } else {
2202                 ev.cancelBubble = true;
2203             }
2204         },
2205
2206
2207         preventDefault: function(ev) {
2208             ev = ev.browserEvent || ev;
2209             if(ev.preventDefault) {
2210                 ev.preventDefault();
2211             } else {
2212                 ev.returnValue = false;
2213             }
2214         },
2215
2216
2217         getEvent: function(e) {
2218             var ev = e || window.event;
2219             if (!ev) {
2220                 var c = this.getEvent.caller;
2221                 while (c) {
2222                     ev = c.arguments[0];
2223                     if (ev && Event == ev.constructor) {
2224                         break;
2225                     }
2226                     c = c.caller;
2227                 }
2228             }
2229             return ev;
2230         },
2231
2232
2233         getCharCode: function(ev) {
2234             ev = ev.browserEvent || ev;
2235             return ev.charCode || ev.keyCode || 0;
2236         },
2237
2238
2239         _getCacheIndex: function(el, eventName, fn) {
2240             for (var i = 0,len = listeners.length; i < len; ++i) {
2241                 var li = listeners[i];
2242                 if (li &&
2243                     li[this.FN] == fn &&
2244                     li[this.EL] == el &&
2245                     li[this.TYPE] == eventName) {
2246                     return i;
2247                 }
2248             }
2249
2250             return -1;
2251         },
2252
2253
2254         elCache: {},
2255
2256
2257         getEl: function(id) {
2258             return document.getElementById(id);
2259         },
2260
2261
2262         clearCache: function() {
2263         },
2264
2265
2266         _load: function(e) {
2267             loadComplete = true;
2268             var EU = Roo.lib.Event;
2269
2270
2271             if (Roo.isIE) {
2272                 EU.doRemove(window, "load", EU._load);
2273             }
2274         },
2275
2276
2277         _tryPreloadAttach: function() {
2278
2279             if (this.locked) {
2280                 return false;
2281             }
2282
2283             this.locked = true;
2284
2285
2286             var tryAgain = !loadComplete;
2287             if (!tryAgain) {
2288                 tryAgain = (retryCount > 0);
2289             }
2290
2291
2292             var notAvail = [];
2293             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2294                 var item = onAvailStack[i];
2295                 if (item) {
2296                     var el = this.getEl(item.id);
2297
2298                     if (el) {
2299                         if (!item.checkReady ||
2300                             loadComplete ||
2301                             el.nextSibling ||
2302                             (document && document.body)) {
2303
2304                             var scope = el;
2305                             if (item.override) {
2306                                 if (item.override === true) {
2307                                     scope = item.obj;
2308                                 } else {
2309                                     scope = item.override;
2310                                 }
2311                             }
2312                             item.fn.call(scope, item.obj);
2313                             onAvailStack[i] = null;
2314                         }
2315                     } else {
2316                         notAvail.push(item);
2317                     }
2318                 }
2319             }
2320
2321             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2322
2323             if (tryAgain) {
2324
2325                 this.startInterval();
2326             } else {
2327                 clearInterval(this._interval);
2328                 this._interval = null;
2329             }
2330
2331             this.locked = false;
2332
2333             return true;
2334
2335         },
2336
2337
2338         purgeElement: function(el, recurse, eventName) {
2339             var elListeners = this.getListeners(el, eventName);
2340             if (elListeners) {
2341                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2342                     var l = elListeners[i];
2343                     this.removeListener(el, l.type, l.fn);
2344                 }
2345             }
2346
2347             if (recurse && el && el.childNodes) {
2348                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2349                     this.purgeElement(el.childNodes[i], recurse, eventName);
2350                 }
2351             }
2352         },
2353
2354
2355         getListeners: function(el, eventName) {
2356             var results = [], searchLists;
2357             if (!eventName) {
2358                 searchLists = [listeners, unloadListeners];
2359             } else if (eventName == "unload") {
2360                 searchLists = [unloadListeners];
2361             } else {
2362                 searchLists = [listeners];
2363             }
2364
2365             for (var j = 0; j < searchLists.length; ++j) {
2366                 var searchList = searchLists[j];
2367                 if (searchList && searchList.length > 0) {
2368                     for (var i = 0,len = searchList.length; i < len; ++i) {
2369                         var l = searchList[i];
2370                         if (l && l[this.EL] === el &&
2371                             (!eventName || eventName === l[this.TYPE])) {
2372                             results.push({
2373                                 type:   l[this.TYPE],
2374                                 fn:     l[this.FN],
2375                                 obj:    l[this.OBJ],
2376                                 adjust: l[this.ADJ_SCOPE],
2377                                 index:  i
2378                             });
2379                         }
2380                     }
2381                 }
2382             }
2383
2384             return (results.length) ? results : null;
2385         },
2386
2387
2388         _unload: function(e) {
2389
2390             var EU = Roo.lib.Event, i, j, l, len, index;
2391
2392             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2393                 l = unloadListeners[i];
2394                 if (l) {
2395                     var scope = window;
2396                     if (l[EU.ADJ_SCOPE]) {
2397                         if (l[EU.ADJ_SCOPE] === true) {
2398                             scope = l[EU.OBJ];
2399                         } else {
2400                             scope = l[EU.ADJ_SCOPE];
2401                         }
2402                     }
2403                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2404                     unloadListeners[i] = null;
2405                     l = null;
2406                     scope = null;
2407                 }
2408             }
2409
2410             unloadListeners = null;
2411
2412             if (listeners && listeners.length > 0) {
2413                 j = listeners.length;
2414                 while (j) {
2415                     index = j - 1;
2416                     l = listeners[index];
2417                     if (l) {
2418                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2419                                 l[EU.FN], index);
2420                     }
2421                     j = j - 1;
2422                 }
2423                 l = null;
2424
2425                 EU.clearCache();
2426             }
2427
2428             EU.doRemove(window, "unload", EU._unload);
2429
2430         },
2431
2432
2433         getScroll: function() {
2434             var dd = document.documentElement, db = document.body;
2435             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2436                 return [dd.scrollTop, dd.scrollLeft];
2437             } else if (db) {
2438                 return [db.scrollTop, db.scrollLeft];
2439             } else {
2440                 return [0, 0];
2441             }
2442         },
2443
2444
2445         doAdd: function () {
2446             if (window.addEventListener) {
2447                 return function(el, eventName, fn, capture) {
2448                     el.addEventListener(eventName, fn, (capture));
2449                 };
2450             } else if (window.attachEvent) {
2451                 return function(el, eventName, fn, capture) {
2452                     el.attachEvent("on" + eventName, fn);
2453                 };
2454             } else {
2455                 return function() {
2456                 };
2457             }
2458         }(),
2459
2460
2461         doRemove: function() {
2462             if (window.removeEventListener) {
2463                 return function (el, eventName, fn, capture) {
2464                     el.removeEventListener(eventName, fn, (capture));
2465                 };
2466             } else if (window.detachEvent) {
2467                 return function (el, eventName, fn) {
2468                     el.detachEvent("on" + eventName, fn);
2469                 };
2470             } else {
2471                 return function() {
2472                 };
2473             }
2474         }()
2475     };
2476     
2477 }();
2478 (function() {     
2479    
2480     var E = Roo.lib.Event;
2481     E.on = E.addListener;
2482     E.un = E.removeListener;
2483
2484     if (document && document.body) {
2485         E._load();
2486     } else {
2487         E.doAdd(window, "load", E._load);
2488     }
2489     E.doAdd(window, "unload", E._unload);
2490     E._tryPreloadAttach();
2491 })();
2492
2493 /*
2494  * Portions of this file are based on pieces of Yahoo User Interface Library
2495  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2496  * YUI licensed under the BSD License:
2497  * http://developer.yahoo.net/yui/license.txt
2498  * <script type="text/javascript">
2499  *
2500  */
2501
2502 (function() {
2503     /**
2504      * @class Roo.lib.Ajax
2505      *
2506      */
2507     Roo.lib.Ajax = {
2508         /**
2509          * @static 
2510          */
2511         request : function(method, uri, cb, data, options) {
2512             if(options){
2513                 var hs = options.headers;
2514                 if(hs){
2515                     for(var h in hs){
2516                         if(hs.hasOwnProperty(h)){
2517                             this.initHeader(h, hs[h], false);
2518                         }
2519                     }
2520                 }
2521                 if(options.xmlData){
2522                     this.initHeader('Content-Type', 'text/xml', false);
2523                     method = 'POST';
2524                     data = options.xmlData;
2525                 }
2526             }
2527
2528             return this.asyncRequest(method, uri, cb, data);
2529         },
2530
2531         serializeForm : function(form) {
2532             if(typeof form == 'string') {
2533                 form = (document.getElementById(form) || document.forms[form]);
2534             }
2535
2536             var el, name, val, disabled, data = '', hasSubmit = false;
2537             for (var i = 0; i < form.elements.length; i++) {
2538                 el = form.elements[i];
2539                 disabled = form.elements[i].disabled;
2540                 name = form.elements[i].name;
2541                 val = form.elements[i].value;
2542
2543                 if (!disabled && name){
2544                     switch (el.type)
2545                             {
2546                         case 'select-one':
2547                         case 'select-multiple':
2548                             for (var j = 0; j < el.options.length; j++) {
2549                                 if (el.options[j].selected) {
2550                                     if (Roo.isIE) {
2551                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2552                                     }
2553                                     else {
2554                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2555                                     }
2556                                 }
2557                             }
2558                             break;
2559                         case 'radio':
2560                         case 'checkbox':
2561                             if (el.checked) {
2562                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2563                             }
2564                             break;
2565                         case 'file':
2566
2567                         case undefined:
2568
2569                         case 'reset':
2570
2571                         case 'button':
2572
2573                             break;
2574                         case 'submit':
2575                             if(hasSubmit == false) {
2576                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2577                                 hasSubmit = true;
2578                             }
2579                             break;
2580                         default:
2581                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2582                             break;
2583                     }
2584                 }
2585             }
2586             data = data.substr(0, data.length - 1);
2587             return data;
2588         },
2589
2590         headers:{},
2591
2592         hasHeaders:false,
2593
2594         useDefaultHeader:true,
2595
2596         defaultPostHeader:'application/x-www-form-urlencoded',
2597
2598         useDefaultXhrHeader:true,
2599
2600         defaultXhrHeader:'XMLHttpRequest',
2601
2602         hasDefaultHeaders:true,
2603
2604         defaultHeaders:{},
2605
2606         poll:{},
2607
2608         timeout:{},
2609
2610         pollInterval:50,
2611
2612         transactionId:0,
2613
2614         setProgId:function(id)
2615         {
2616             this.activeX.unshift(id);
2617         },
2618
2619         setDefaultPostHeader:function(b)
2620         {
2621             this.useDefaultHeader = b;
2622         },
2623
2624         setDefaultXhrHeader:function(b)
2625         {
2626             this.useDefaultXhrHeader = b;
2627         },
2628
2629         setPollingInterval:function(i)
2630         {
2631             if (typeof i == 'number' && isFinite(i)) {
2632                 this.pollInterval = i;
2633             }
2634         },
2635
2636         createXhrObject:function(transactionId)
2637         {
2638             var obj,http;
2639             try
2640             {
2641
2642                 http = new XMLHttpRequest();
2643
2644                 obj = { conn:http, tId:transactionId };
2645             }
2646             catch(e)
2647             {
2648                 for (var i = 0; i < this.activeX.length; ++i) {
2649                     try
2650                     {
2651
2652                         http = new ActiveXObject(this.activeX[i]);
2653
2654                         obj = { conn:http, tId:transactionId };
2655                         break;
2656                     }
2657                     catch(e) {
2658                     }
2659                 }
2660             }
2661             finally
2662             {
2663                 return obj;
2664             }
2665         },
2666
2667         getConnectionObject:function()
2668         {
2669             var o;
2670             var tId = this.transactionId;
2671
2672             try
2673             {
2674                 o = this.createXhrObject(tId);
2675                 if (o) {
2676                     this.transactionId++;
2677                 }
2678             }
2679             catch(e) {
2680             }
2681             finally
2682             {
2683                 return o;
2684             }
2685         },
2686
2687         asyncRequest:function(method, uri, callback, postData)
2688         {
2689             var o = this.getConnectionObject();
2690
2691             if (!o) {
2692                 return null;
2693             }
2694             else {
2695                 o.conn.open(method, uri, true);
2696
2697                 if (this.useDefaultXhrHeader) {
2698                     if (!this.defaultHeaders['X-Requested-With']) {
2699                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2700                     }
2701                 }
2702
2703                 if(postData && this.useDefaultHeader){
2704                     this.initHeader('Content-Type', this.defaultPostHeader);
2705                 }
2706
2707                  if (this.hasDefaultHeaders || this.hasHeaders) {
2708                     this.setHeader(o);
2709                 }
2710
2711                 this.handleReadyState(o, callback);
2712                 o.conn.send(postData || null);
2713
2714                 return o;
2715             }
2716         },
2717
2718         handleReadyState:function(o, callback)
2719         {
2720             var oConn = this;
2721
2722             if (callback && callback.timeout) {
2723                 
2724                 this.timeout[o.tId] = window.setTimeout(function() {
2725                     oConn.abort(o, callback, true);
2726                 }, callback.timeout);
2727             }
2728
2729             this.poll[o.tId] = window.setInterval(
2730                     function() {
2731                         if (o.conn && o.conn.readyState == 4) {
2732                             window.clearInterval(oConn.poll[o.tId]);
2733                             delete oConn.poll[o.tId];
2734
2735                             if(callback && callback.timeout) {
2736                                 window.clearTimeout(oConn.timeout[o.tId]);
2737                                 delete oConn.timeout[o.tId];
2738                             }
2739
2740                             oConn.handleTransactionResponse(o, callback);
2741                         }
2742                     }
2743                     , this.pollInterval);
2744         },
2745
2746         handleTransactionResponse:function(o, callback, isAbort)
2747         {
2748
2749             if (!callback) {
2750                 this.releaseObject(o);
2751                 return;
2752             }
2753
2754             var httpStatus, responseObject;
2755
2756             try
2757             {
2758                 if (o.conn.status !== undefined && o.conn.status != 0) {
2759                     httpStatus = o.conn.status;
2760                 }
2761                 else {
2762                     httpStatus = 13030;
2763                 }
2764             }
2765             catch(e) {
2766
2767
2768                 httpStatus = 13030;
2769             }
2770
2771             if (httpStatus >= 200 && httpStatus < 300) {
2772                 responseObject = this.createResponseObject(o, callback.argument);
2773                 if (callback.success) {
2774                     if (!callback.scope) {
2775                         callback.success(responseObject);
2776                     }
2777                     else {
2778
2779
2780                         callback.success.apply(callback.scope, [responseObject]);
2781                     }
2782                 }
2783             }
2784             else {
2785                 switch (httpStatus) {
2786
2787                     case 12002:
2788                     case 12029:
2789                     case 12030:
2790                     case 12031:
2791                     case 12152:
2792                     case 13030:
2793                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2794                         if (callback.failure) {
2795                             if (!callback.scope) {
2796                                 callback.failure(responseObject);
2797                             }
2798                             else {
2799                                 callback.failure.apply(callback.scope, [responseObject]);
2800                             }
2801                         }
2802                         break;
2803                     default:
2804                         responseObject = this.createResponseObject(o, callback.argument);
2805                         if (callback.failure) {
2806                             if (!callback.scope) {
2807                                 callback.failure(responseObject);
2808                             }
2809                             else {
2810                                 callback.failure.apply(callback.scope, [responseObject]);
2811                             }
2812                         }
2813                 }
2814             }
2815
2816             this.releaseObject(o);
2817             responseObject = null;
2818         },
2819
2820         createResponseObject:function(o, callbackArg)
2821         {
2822             var obj = {};
2823             var headerObj = {};
2824
2825             try
2826             {
2827                 var headerStr = o.conn.getAllResponseHeaders();
2828                 var header = headerStr.split('\n');
2829                 for (var i = 0; i < header.length; i++) {
2830                     var delimitPos = header[i].indexOf(':');
2831                     if (delimitPos != -1) {
2832                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2833                     }
2834                 }
2835             }
2836             catch(e) {
2837             }
2838
2839             obj.tId = o.tId;
2840             obj.status = o.conn.status;
2841             obj.statusText = o.conn.statusText;
2842             obj.getResponseHeader = headerObj;
2843             obj.getAllResponseHeaders = headerStr;
2844             obj.responseText = o.conn.responseText;
2845             obj.responseXML = o.conn.responseXML;
2846
2847             if (typeof callbackArg !== undefined) {
2848                 obj.argument = callbackArg;
2849             }
2850
2851             return obj;
2852         },
2853
2854         createExceptionObject:function(tId, callbackArg, isAbort)
2855         {
2856             var COMM_CODE = 0;
2857             var COMM_ERROR = 'communication failure';
2858             var ABORT_CODE = -1;
2859             var ABORT_ERROR = 'transaction aborted';
2860
2861             var obj = {};
2862
2863             obj.tId = tId;
2864             if (isAbort) {
2865                 obj.status = ABORT_CODE;
2866                 obj.statusText = ABORT_ERROR;
2867             }
2868             else {
2869                 obj.status = COMM_CODE;
2870                 obj.statusText = COMM_ERROR;
2871             }
2872
2873             if (callbackArg) {
2874                 obj.argument = callbackArg;
2875             }
2876
2877             return obj;
2878         },
2879
2880         initHeader:function(label, value, isDefault)
2881         {
2882             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2883
2884             if (headerObj[label] === undefined) {
2885                 headerObj[label] = value;
2886             }
2887             else {
2888
2889
2890                 headerObj[label] = value + "," + headerObj[label];
2891             }
2892
2893             if (isDefault) {
2894                 this.hasDefaultHeaders = true;
2895             }
2896             else {
2897                 this.hasHeaders = true;
2898             }
2899         },
2900
2901
2902         setHeader:function(o)
2903         {
2904             if (this.hasDefaultHeaders) {
2905                 for (var prop in this.defaultHeaders) {
2906                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2907                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2908                     }
2909                 }
2910             }
2911
2912             if (this.hasHeaders) {
2913                 for (var prop in this.headers) {
2914                     if (this.headers.hasOwnProperty(prop)) {
2915                         o.conn.setRequestHeader(prop, this.headers[prop]);
2916                     }
2917                 }
2918                 this.headers = {};
2919                 this.hasHeaders = false;
2920             }
2921         },
2922
2923         resetDefaultHeaders:function() {
2924             delete this.defaultHeaders;
2925             this.defaultHeaders = {};
2926             this.hasDefaultHeaders = false;
2927         },
2928
2929         abort:function(o, callback, isTimeout)
2930         {
2931             if(this.isCallInProgress(o)) {
2932                 o.conn.abort();
2933                 window.clearInterval(this.poll[o.tId]);
2934                 delete this.poll[o.tId];
2935                 if (isTimeout) {
2936                     delete this.timeout[o.tId];
2937                 }
2938
2939                 this.handleTransactionResponse(o, callback, true);
2940
2941                 return true;
2942             }
2943             else {
2944                 return false;
2945             }
2946         },
2947
2948
2949         isCallInProgress:function(o)
2950         {
2951             if (o && o.conn) {
2952                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2953             }
2954             else {
2955
2956                 return false;
2957             }
2958         },
2959
2960
2961         releaseObject:function(o)
2962         {
2963
2964             o.conn = null;
2965
2966             o = null;
2967         },
2968
2969         activeX:[
2970         'MSXML2.XMLHTTP.3.0',
2971         'MSXML2.XMLHTTP',
2972         'Microsoft.XMLHTTP'
2973         ]
2974
2975
2976     };
2977 })();/*
2978  * Portions of this file are based on pieces of Yahoo User Interface Library
2979  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2980  * YUI licensed under the BSD License:
2981  * http://developer.yahoo.net/yui/license.txt
2982  * <script type="text/javascript">
2983  *
2984  */
2985
2986 Roo.lib.Region = function(t, r, b, l) {
2987     this.top = t;
2988     this[1] = t;
2989     this.right = r;
2990     this.bottom = b;
2991     this.left = l;
2992     this[0] = l;
2993 };
2994
2995
2996 Roo.lib.Region.prototype = {
2997     contains : function(region) {
2998         return ( region.left >= this.left &&
2999                  region.right <= this.right &&
3000                  region.top >= this.top &&
3001                  region.bottom <= this.bottom    );
3002
3003     },
3004
3005     getArea : function() {
3006         return ( (this.bottom - this.top) * (this.right - this.left) );
3007     },
3008
3009     intersect : function(region) {
3010         var t = Math.max(this.top, region.top);
3011         var r = Math.min(this.right, region.right);
3012         var b = Math.min(this.bottom, region.bottom);
3013         var l = Math.max(this.left, region.left);
3014
3015         if (b >= t && r >= l) {
3016             return new Roo.lib.Region(t, r, b, l);
3017         } else {
3018             return null;
3019         }
3020     },
3021     union : function(region) {
3022         var t = Math.min(this.top, region.top);
3023         var r = Math.max(this.right, region.right);
3024         var b = Math.max(this.bottom, region.bottom);
3025         var l = Math.min(this.left, region.left);
3026
3027         return new Roo.lib.Region(t, r, b, l);
3028     },
3029
3030     adjust : function(t, l, b, r) {
3031         this.top += t;
3032         this.left += l;
3033         this.right += r;
3034         this.bottom += b;
3035         return this;
3036     }
3037 };
3038
3039 Roo.lib.Region.getRegion = function(el) {
3040     var p = Roo.lib.Dom.getXY(el);
3041
3042     var t = p[1];
3043     var r = p[0] + el.offsetWidth;
3044     var b = p[1] + el.offsetHeight;
3045     var l = p[0];
3046
3047     return new Roo.lib.Region(t, r, b, l);
3048 };
3049 /*
3050  * Portions of this file are based on pieces of Yahoo User Interface Library
3051  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3052  * YUI licensed under the BSD License:
3053  * http://developer.yahoo.net/yui/license.txt
3054  * <script type="text/javascript">
3055  *
3056  */
3057 //@@dep Roo.lib.Region
3058
3059
3060 Roo.lib.Point = function(x, y) {
3061     if (x instanceof Array) {
3062         y = x[1];
3063         x = x[0];
3064     }
3065     this.x = this.right = this.left = this[0] = x;
3066     this.y = this.top = this.bottom = this[1] = y;
3067 };
3068
3069 Roo.lib.Point.prototype = new Roo.lib.Region();
3070 /*
3071  * Portions of this file are based on pieces of Yahoo User Interface Library
3072  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3073  * YUI licensed under the BSD License:
3074  * http://developer.yahoo.net/yui/license.txt
3075  * <script type="text/javascript">
3076  *
3077  */
3078  
3079 (function() {   
3080
3081     Roo.lib.Anim = {
3082         scroll : function(el, args, duration, easing, cb, scope) {
3083             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3084         },
3085
3086         motion : function(el, args, duration, easing, cb, scope) {
3087             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3088         },
3089
3090         color : function(el, args, duration, easing, cb, scope) {
3091             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3092         },
3093
3094         run : function(el, args, duration, easing, cb, scope, type) {
3095             type = type || Roo.lib.AnimBase;
3096             if (typeof easing == "string") {
3097                 easing = Roo.lib.Easing[easing];
3098             }
3099             var anim = new type(el, args, duration, easing);
3100             anim.animateX(function() {
3101                 Roo.callback(cb, scope);
3102             });
3103             return anim;
3104         }
3105     };
3106 })();/*
3107  * Portions of this file are based on pieces of Yahoo User Interface Library
3108  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3109  * YUI licensed under the BSD License:
3110  * http://developer.yahoo.net/yui/license.txt
3111  * <script type="text/javascript">
3112  *
3113  */
3114
3115 (function() {    
3116     var libFlyweight;
3117     
3118     function fly(el) {
3119         if (!libFlyweight) {
3120             libFlyweight = new Roo.Element.Flyweight();
3121         }
3122         libFlyweight.dom = el;
3123         return libFlyweight;
3124     }
3125
3126     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3127     
3128    
3129     
3130     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3131         if (el) {
3132             this.init(el, attributes, duration, method);
3133         }
3134     };
3135
3136     Roo.lib.AnimBase.fly = fly;
3137     
3138     
3139     
3140     Roo.lib.AnimBase.prototype = {
3141
3142         toString: function() {
3143             var el = this.getEl();
3144             var id = el.id || el.tagName;
3145             return ("Anim " + id);
3146         },
3147
3148         patterns: {
3149             noNegatives:        /width|height|opacity|padding/i,
3150             offsetAttribute:  /^((width|height)|(top|left))$/,
3151             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3152             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3153         },
3154
3155
3156         doMethod: function(attr, start, end) {
3157             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3158         },
3159
3160
3161         setAttribute: function(attr, val, unit) {
3162             if (this.patterns.noNegatives.test(attr)) {
3163                 val = (val > 0) ? val : 0;
3164             }
3165
3166             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3167         },
3168
3169
3170         getAttribute: function(attr) {
3171             var el = this.getEl();
3172             var val = fly(el).getStyle(attr);
3173
3174             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3175                 return parseFloat(val);
3176             }
3177
3178             var a = this.patterns.offsetAttribute.exec(attr) || [];
3179             var pos = !!( a[3] );
3180             var box = !!( a[2] );
3181
3182
3183             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3184                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3185             } else {
3186                 val = 0;
3187             }
3188
3189             return val;
3190         },
3191
3192
3193         getDefaultUnit: function(attr) {
3194             if (this.patterns.defaultUnit.test(attr)) {
3195                 return 'px';
3196             }
3197
3198             return '';
3199         },
3200
3201         animateX : function(callback, scope) {
3202             var f = function() {
3203                 this.onComplete.removeListener(f);
3204                 if (typeof callback == "function") {
3205                     callback.call(scope || this, this);
3206                 }
3207             };
3208             this.onComplete.addListener(f, this);
3209             this.animate();
3210         },
3211
3212
3213         setRuntimeAttribute: function(attr) {
3214             var start;
3215             var end;
3216             var attributes = this.attributes;
3217
3218             this.runtimeAttributes[attr] = {};
3219
3220             var isset = function(prop) {
3221                 return (typeof prop !== 'undefined');
3222             };
3223
3224             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3225                 return false;
3226             }
3227
3228             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3229
3230
3231             if (isset(attributes[attr]['to'])) {
3232                 end = attributes[attr]['to'];
3233             } else if (isset(attributes[attr]['by'])) {
3234                 if (start.constructor == Array) {
3235                     end = [];
3236                     for (var i = 0, len = start.length; i < len; ++i) {
3237                         end[i] = start[i] + attributes[attr]['by'][i];
3238                     }
3239                 } else {
3240                     end = start + attributes[attr]['by'];
3241                 }
3242             }
3243
3244             this.runtimeAttributes[attr].start = start;
3245             this.runtimeAttributes[attr].end = end;
3246
3247
3248             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3249         },
3250
3251
3252         init: function(el, attributes, duration, method) {
3253
3254             var isAnimated = false;
3255
3256
3257             var startTime = null;
3258
3259
3260             var actualFrames = 0;
3261
3262
3263             el = Roo.getDom(el);
3264
3265
3266             this.attributes = attributes || {};
3267
3268
3269             this.duration = duration || 1;
3270
3271
3272             this.method = method || Roo.lib.Easing.easeNone;
3273
3274
3275             this.useSeconds = true;
3276
3277
3278             this.currentFrame = 0;
3279
3280
3281             this.totalFrames = Roo.lib.AnimMgr.fps;
3282
3283
3284             this.getEl = function() {
3285                 return el;
3286             };
3287
3288
3289             this.isAnimated = function() {
3290                 return isAnimated;
3291             };
3292
3293
3294             this.getStartTime = function() {
3295                 return startTime;
3296             };
3297
3298             this.runtimeAttributes = {};
3299
3300
3301             this.animate = function() {
3302                 if (this.isAnimated()) {
3303                     return false;
3304                 }
3305
3306                 this.currentFrame = 0;
3307
3308                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3309
3310                 Roo.lib.AnimMgr.registerElement(this);
3311             };
3312
3313
3314             this.stop = function(finish) {
3315                 if (finish) {
3316                     this.currentFrame = this.totalFrames;
3317                     this._onTween.fire();
3318                 }
3319                 Roo.lib.AnimMgr.stop(this);
3320             };
3321
3322             var onStart = function() {
3323                 this.onStart.fire();
3324
3325                 this.runtimeAttributes = {};
3326                 for (var attr in this.attributes) {
3327                     this.setRuntimeAttribute(attr);
3328                 }
3329
3330                 isAnimated = true;
3331                 actualFrames = 0;
3332                 startTime = new Date();
3333             };
3334
3335
3336             var onTween = function() {
3337                 var data = {
3338                     duration: new Date() - this.getStartTime(),
3339                     currentFrame: this.currentFrame
3340                 };
3341
3342                 data.toString = function() {
3343                     return (
3344                             'duration: ' + data.duration +
3345                             ', currentFrame: ' + data.currentFrame
3346                             );
3347                 };
3348
3349                 this.onTween.fire(data);
3350
3351                 var runtimeAttributes = this.runtimeAttributes;
3352
3353                 for (var attr in runtimeAttributes) {
3354                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3355                 }
3356
3357                 actualFrames += 1;
3358             };
3359
3360             var onComplete = function() {
3361                 var actual_duration = (new Date() - startTime) / 1000 ;
3362
3363                 var data = {
3364                     duration: actual_duration,
3365                     frames: actualFrames,
3366                     fps: actualFrames / actual_duration
3367                 };
3368
3369                 data.toString = function() {
3370                     return (
3371                             'duration: ' + data.duration +
3372                             ', frames: ' + data.frames +
3373                             ', fps: ' + data.fps
3374                             );
3375                 };
3376
3377                 isAnimated = false;
3378                 actualFrames = 0;
3379                 this.onComplete.fire(data);
3380             };
3381
3382
3383             this._onStart = new Roo.util.Event(this);
3384             this.onStart = new Roo.util.Event(this);
3385             this.onTween = new Roo.util.Event(this);
3386             this._onTween = new Roo.util.Event(this);
3387             this.onComplete = new Roo.util.Event(this);
3388             this._onComplete = new Roo.util.Event(this);
3389             this._onStart.addListener(onStart);
3390             this._onTween.addListener(onTween);
3391             this._onComplete.addListener(onComplete);
3392         }
3393     };
3394 })();
3395 /*
3396  * Portions of this file are based on pieces of Yahoo User Interface Library
3397  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3398  * YUI licensed under the BSD License:
3399  * http://developer.yahoo.net/yui/license.txt
3400  * <script type="text/javascript">
3401  *
3402  */
3403
3404 Roo.lib.AnimMgr = new function() {
3405
3406     var thread = null;
3407
3408
3409     var queue = [];
3410
3411
3412     var tweenCount = 0;
3413
3414
3415     this.fps = 1000;
3416
3417
3418     this.delay = 1;
3419
3420
3421     this.registerElement = function(tween) {
3422         queue[queue.length] = tween;
3423         tweenCount += 1;
3424         tween._onStart.fire();
3425         this.start();
3426     };
3427
3428
3429     this.unRegister = function(tween, index) {
3430         tween._onComplete.fire();
3431         index = index || getIndex(tween);
3432         if (index != -1) {
3433             queue.splice(index, 1);
3434         }
3435
3436         tweenCount -= 1;
3437         if (tweenCount <= 0) {
3438             this.stop();
3439         }
3440     };
3441
3442
3443     this.start = function() {
3444         if (thread === null) {
3445             thread = setInterval(this.run, this.delay);
3446         }
3447     };
3448
3449
3450     this.stop = function(tween) {
3451         if (!tween) {
3452             clearInterval(thread);
3453
3454             for (var i = 0, len = queue.length; i < len; ++i) {
3455                 if (queue[0].isAnimated()) {
3456                     this.unRegister(queue[0], 0);
3457                 }
3458             }
3459
3460             queue = [];
3461             thread = null;
3462             tweenCount = 0;
3463         }
3464         else {
3465             this.unRegister(tween);
3466         }
3467     };
3468
3469
3470     this.run = function() {
3471         for (var i = 0, len = queue.length; i < len; ++i) {
3472             var tween = queue[i];
3473             if (!tween || !tween.isAnimated()) {
3474                 continue;
3475             }
3476
3477             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3478             {
3479                 tween.currentFrame += 1;
3480
3481                 if (tween.useSeconds) {
3482                     correctFrame(tween);
3483                 }
3484                 tween._onTween.fire();
3485             }
3486             else {
3487                 Roo.lib.AnimMgr.stop(tween, i);
3488             }
3489         }
3490     };
3491
3492     var getIndex = function(anim) {
3493         for (var i = 0, len = queue.length; i < len; ++i) {
3494             if (queue[i] == anim) {
3495                 return i;
3496             }
3497         }
3498         return -1;
3499     };
3500
3501
3502     var correctFrame = function(tween) {
3503         var frames = tween.totalFrames;
3504         var frame = tween.currentFrame;
3505         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3506         var elapsed = (new Date() - tween.getStartTime());
3507         var tweak = 0;
3508
3509         if (elapsed < tween.duration * 1000) {
3510             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3511         } else {
3512             tweak = frames - (frame + 1);
3513         }
3514         if (tweak > 0 && isFinite(tweak)) {
3515             if (tween.currentFrame + tweak >= frames) {
3516                 tweak = frames - (frame + 1);
3517             }
3518
3519             tween.currentFrame += tweak;
3520         }
3521     };
3522 };
3523
3524     /*
3525  * Portions of this file are based on pieces of Yahoo User Interface Library
3526  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3527  * YUI licensed under the BSD License:
3528  * http://developer.yahoo.net/yui/license.txt
3529  * <script type="text/javascript">
3530  *
3531  */
3532 Roo.lib.Bezier = new function() {
3533
3534         this.getPosition = function(points, t) {
3535             var n = points.length;
3536             var tmp = [];
3537
3538             for (var i = 0; i < n; ++i) {
3539                 tmp[i] = [points[i][0], points[i][1]];
3540             }
3541
3542             for (var j = 1; j < n; ++j) {
3543                 for (i = 0; i < n - j; ++i) {
3544                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3545                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3546                 }
3547             }
3548
3549             return [ tmp[0][0], tmp[0][1] ];
3550
3551         };
3552     };/*
3553  * Portions of this file are based on pieces of Yahoo User Interface Library
3554  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3555  * YUI licensed under the BSD License:
3556  * http://developer.yahoo.net/yui/license.txt
3557  * <script type="text/javascript">
3558  *
3559  */
3560 (function() {
3561
3562     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3563         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3564     };
3565
3566     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3567
3568     var fly = Roo.lib.AnimBase.fly;
3569     var Y = Roo.lib;
3570     var superclass = Y.ColorAnim.superclass;
3571     var proto = Y.ColorAnim.prototype;
3572
3573     proto.toString = function() {
3574         var el = this.getEl();
3575         var id = el.id || el.tagName;
3576         return ("ColorAnim " + id);
3577     };
3578
3579     proto.patterns.color = /color$/i;
3580     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3581     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3582     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3583     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3584
3585
3586     proto.parseColor = function(s) {
3587         if (s.length == 3) {
3588             return s;
3589         }
3590
3591         var c = this.patterns.hex.exec(s);
3592         if (c && c.length == 4) {
3593             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3594         }
3595
3596         c = this.patterns.rgb.exec(s);
3597         if (c && c.length == 4) {
3598             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3599         }
3600
3601         c = this.patterns.hex3.exec(s);
3602         if (c && c.length == 4) {
3603             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3604         }
3605
3606         return null;
3607     };
3608     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3609     proto.getAttribute = function(attr) {
3610         var el = this.getEl();
3611         if (this.patterns.color.test(attr)) {
3612             var val = fly(el).getStyle(attr);
3613
3614             if (this.patterns.transparent.test(val)) {
3615                 var parent = el.parentNode;
3616                 val = fly(parent).getStyle(attr);
3617
3618                 while (parent && this.patterns.transparent.test(val)) {
3619                     parent = parent.parentNode;
3620                     val = fly(parent).getStyle(attr);
3621                     if (parent.tagName.toUpperCase() == 'HTML') {
3622                         val = '#fff';
3623                     }
3624                 }
3625             }
3626         } else {
3627             val = superclass.getAttribute.call(this, attr);
3628         }
3629
3630         return val;
3631     };
3632     proto.getAttribute = function(attr) {
3633         var el = this.getEl();
3634         if (this.patterns.color.test(attr)) {
3635             var val = fly(el).getStyle(attr);
3636
3637             if (this.patterns.transparent.test(val)) {
3638                 var parent = el.parentNode;
3639                 val = fly(parent).getStyle(attr);
3640
3641                 while (parent && this.patterns.transparent.test(val)) {
3642                     parent = parent.parentNode;
3643                     val = fly(parent).getStyle(attr);
3644                     if (parent.tagName.toUpperCase() == 'HTML') {
3645                         val = '#fff';
3646                     }
3647                 }
3648             }
3649         } else {
3650             val = superclass.getAttribute.call(this, attr);
3651         }
3652
3653         return val;
3654     };
3655
3656     proto.doMethod = function(attr, start, end) {
3657         var val;
3658
3659         if (this.patterns.color.test(attr)) {
3660             val = [];
3661             for (var i = 0, len = start.length; i < len; ++i) {
3662                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3663             }
3664
3665             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3666         }
3667         else {
3668             val = superclass.doMethod.call(this, attr, start, end);
3669         }
3670
3671         return val;
3672     };
3673
3674     proto.setRuntimeAttribute = function(attr) {
3675         superclass.setRuntimeAttribute.call(this, attr);
3676
3677         if (this.patterns.color.test(attr)) {
3678             var attributes = this.attributes;
3679             var start = this.parseColor(this.runtimeAttributes[attr].start);
3680             var end = this.parseColor(this.runtimeAttributes[attr].end);
3681
3682             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3683                 end = this.parseColor(attributes[attr].by);
3684
3685                 for (var i = 0, len = start.length; i < len; ++i) {
3686                     end[i] = start[i] + end[i];
3687                 }
3688             }
3689
3690             this.runtimeAttributes[attr].start = start;
3691             this.runtimeAttributes[attr].end = end;
3692         }
3693     };
3694 })();
3695
3696 /*
3697  * Portions of this file are based on pieces of Yahoo User Interface Library
3698  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3699  * YUI licensed under the BSD License:
3700  * http://developer.yahoo.net/yui/license.txt
3701  * <script type="text/javascript">
3702  *
3703  */
3704 Roo.lib.Easing = {
3705
3706
3707     easeNone: function (t, b, c, d) {
3708         return c * t / d + b;
3709     },
3710
3711
3712     easeIn: function (t, b, c, d) {
3713         return c * (t /= d) * t + b;
3714     },
3715
3716
3717     easeOut: function (t, b, c, d) {
3718         return -c * (t /= d) * (t - 2) + b;
3719     },
3720
3721
3722     easeBoth: function (t, b, c, d) {
3723         if ((t /= d / 2) < 1) {
3724             return c / 2 * t * t + b;
3725         }
3726
3727         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3728     },
3729
3730
3731     easeInStrong: function (t, b, c, d) {
3732         return c * (t /= d) * t * t * t + b;
3733     },
3734
3735
3736     easeOutStrong: function (t, b, c, d) {
3737         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3738     },
3739
3740
3741     easeBothStrong: function (t, b, c, d) {
3742         if ((t /= d / 2) < 1) {
3743             return c / 2 * t * t * t * t + b;
3744         }
3745
3746         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3747     },
3748
3749
3750
3751     elasticIn: function (t, b, c, d, a, p) {
3752         if (t == 0) {
3753             return b;
3754         }
3755         if ((t /= d) == 1) {
3756             return b + c;
3757         }
3758         if (!p) {
3759             p = d * .3;
3760         }
3761
3762         if (!a || a < Math.abs(c)) {
3763             a = c;
3764             var s = p / 4;
3765         }
3766         else {
3767             var s = p / (2 * Math.PI) * Math.asin(c / a);
3768         }
3769
3770         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3771     },
3772
3773
3774     elasticOut: function (t, b, c, d, a, p) {
3775         if (t == 0) {
3776             return b;
3777         }
3778         if ((t /= d) == 1) {
3779             return b + c;
3780         }
3781         if (!p) {
3782             p = d * .3;
3783         }
3784
3785         if (!a || a < Math.abs(c)) {
3786             a = c;
3787             var s = p / 4;
3788         }
3789         else {
3790             var s = p / (2 * Math.PI) * Math.asin(c / a);
3791         }
3792
3793         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3794     },
3795
3796
3797     elasticBoth: function (t, b, c, d, a, p) {
3798         if (t == 0) {
3799             return b;
3800         }
3801
3802         if ((t /= d / 2) == 2) {
3803             return b + c;
3804         }
3805
3806         if (!p) {
3807             p = d * (.3 * 1.5);
3808         }
3809
3810         if (!a || a < Math.abs(c)) {
3811             a = c;
3812             var s = p / 4;
3813         }
3814         else {
3815             var s = p / (2 * Math.PI) * Math.asin(c / a);
3816         }
3817
3818         if (t < 1) {
3819             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3820                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3821         }
3822         return a * Math.pow(2, -10 * (t -= 1)) *
3823                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3824     },
3825
3826
3827
3828     backIn: function (t, b, c, d, s) {
3829         if (typeof s == 'undefined') {
3830             s = 1.70158;
3831         }
3832         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3833     },
3834
3835
3836     backOut: function (t, b, c, d, s) {
3837         if (typeof s == 'undefined') {
3838             s = 1.70158;
3839         }
3840         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3841     },
3842
3843
3844     backBoth: function (t, b, c, d, s) {
3845         if (typeof s == 'undefined') {
3846             s = 1.70158;
3847         }
3848
3849         if ((t /= d / 2 ) < 1) {
3850             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3851         }
3852         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3853     },
3854
3855
3856     bounceIn: function (t, b, c, d) {
3857         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3858     },
3859
3860
3861     bounceOut: function (t, b, c, d) {
3862         if ((t /= d) < (1 / 2.75)) {
3863             return c * (7.5625 * t * t) + b;
3864         } else if (t < (2 / 2.75)) {
3865             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3866         } else if (t < (2.5 / 2.75)) {
3867             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3868         }
3869         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3870     },
3871
3872
3873     bounceBoth: function (t, b, c, d) {
3874         if (t < d / 2) {
3875             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3876         }
3877         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3878     }
3879 };/*
3880  * Portions of this file are based on pieces of Yahoo User Interface Library
3881  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3882  * YUI licensed under the BSD License:
3883  * http://developer.yahoo.net/yui/license.txt
3884  * <script type="text/javascript">
3885  *
3886  */
3887     (function() {
3888         Roo.lib.Motion = function(el, attributes, duration, method) {
3889             if (el) {
3890                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3891             }
3892         };
3893
3894         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3895
3896
3897         var Y = Roo.lib;
3898         var superclass = Y.Motion.superclass;
3899         var proto = Y.Motion.prototype;
3900
3901         proto.toString = function() {
3902             var el = this.getEl();
3903             var id = el.id || el.tagName;
3904             return ("Motion " + id);
3905         };
3906
3907         proto.patterns.points = /^points$/i;
3908
3909         proto.setAttribute = function(attr, val, unit) {
3910             if (this.patterns.points.test(attr)) {
3911                 unit = unit || 'px';
3912                 superclass.setAttribute.call(this, 'left', val[0], unit);
3913                 superclass.setAttribute.call(this, 'top', val[1], unit);
3914             } else {
3915                 superclass.setAttribute.call(this, attr, val, unit);
3916             }
3917         };
3918
3919         proto.getAttribute = function(attr) {
3920             if (this.patterns.points.test(attr)) {
3921                 var val = [
3922                         superclass.getAttribute.call(this, 'left'),
3923                         superclass.getAttribute.call(this, 'top')
3924                         ];
3925             } else {
3926                 val = superclass.getAttribute.call(this, attr);
3927             }
3928
3929             return val;
3930         };
3931
3932         proto.doMethod = function(attr, start, end) {
3933             var val = null;
3934
3935             if (this.patterns.points.test(attr)) {
3936                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3937                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3938             } else {
3939                 val = superclass.doMethod.call(this, attr, start, end);
3940             }
3941             return val;
3942         };
3943
3944         proto.setRuntimeAttribute = function(attr) {
3945             if (this.patterns.points.test(attr)) {
3946                 var el = this.getEl();
3947                 var attributes = this.attributes;
3948                 var start;
3949                 var control = attributes['points']['control'] || [];
3950                 var end;
3951                 var i, len;
3952
3953                 if (control.length > 0 && !(control[0] instanceof Array)) {
3954                     control = [control];
3955                 } else {
3956                     var tmp = [];
3957                     for (i = 0,len = control.length; i < len; ++i) {
3958                         tmp[i] = control[i];
3959                     }
3960                     control = tmp;
3961                 }
3962
3963                 Roo.fly(el).position();
3964
3965                 if (isset(attributes['points']['from'])) {
3966                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3967                 }
3968                 else {
3969                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3970                 }
3971
3972                 start = this.getAttribute('points');
3973
3974
3975                 if (isset(attributes['points']['to'])) {
3976                     end = translateValues.call(this, attributes['points']['to'], start);
3977
3978                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3979                     for (i = 0,len = control.length; i < len; ++i) {
3980                         control[i] = translateValues.call(this, control[i], start);
3981                     }
3982
3983
3984                 } else if (isset(attributes['points']['by'])) {
3985                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3986
3987                     for (i = 0,len = control.length; i < len; ++i) {
3988                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3989                     }
3990                 }
3991
3992                 this.runtimeAttributes[attr] = [start];
3993
3994                 if (control.length > 0) {
3995                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3996                 }
3997
3998                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3999             }
4000             else {
4001                 superclass.setRuntimeAttribute.call(this, attr);
4002             }
4003         };
4004
4005         var translateValues = function(val, start) {
4006             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4007             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4008
4009             return val;
4010         };
4011
4012         var isset = function(prop) {
4013             return (typeof prop !== 'undefined');
4014         };
4015     })();
4016 /*
4017  * Portions of this file are based on pieces of Yahoo User Interface Library
4018  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4019  * YUI licensed under the BSD License:
4020  * http://developer.yahoo.net/yui/license.txt
4021  * <script type="text/javascript">
4022  *
4023  */
4024     (function() {
4025         Roo.lib.Scroll = function(el, attributes, duration, method) {
4026             if (el) {
4027                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4028             }
4029         };
4030
4031         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4032
4033
4034         var Y = Roo.lib;
4035         var superclass = Y.Scroll.superclass;
4036         var proto = Y.Scroll.prototype;
4037
4038         proto.toString = function() {
4039             var el = this.getEl();
4040             var id = el.id || el.tagName;
4041             return ("Scroll " + id);
4042         };
4043
4044         proto.doMethod = function(attr, start, end) {
4045             var val = null;
4046
4047             if (attr == 'scroll') {
4048                 val = [
4049                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4050                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4051                         ];
4052
4053             } else {
4054                 val = superclass.doMethod.call(this, attr, start, end);
4055             }
4056             return val;
4057         };
4058
4059         proto.getAttribute = function(attr) {
4060             var val = null;
4061             var el = this.getEl();
4062
4063             if (attr == 'scroll') {
4064                 val = [ el.scrollLeft, el.scrollTop ];
4065             } else {
4066                 val = superclass.getAttribute.call(this, attr);
4067             }
4068
4069             return val;
4070         };
4071
4072         proto.setAttribute = function(attr, val, unit) {
4073             var el = this.getEl();
4074
4075             if (attr == 'scroll') {
4076                 el.scrollLeft = val[0];
4077                 el.scrollTop = val[1];
4078             } else {
4079                 superclass.setAttribute.call(this, attr, val, unit);
4080             }
4081         };
4082     })();
4083 /*
4084  * Based on:
4085  * Ext JS Library 1.1.1
4086  * Copyright(c) 2006-2007, Ext JS, LLC.
4087  *
4088  * Originally Released Under LGPL - original licence link has changed is not relivant.
4089  *
4090  * Fork - LGPL
4091  * <script type="text/javascript">
4092  */
4093
4094
4095 // nasty IE9 hack - what a pile of crap that is..
4096
4097  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4098     Range.prototype.createContextualFragment = function (html) {
4099         var doc = window.document;
4100         var container = doc.createElement("div");
4101         container.innerHTML = html;
4102         var frag = doc.createDocumentFragment(), n;
4103         while ((n = container.firstChild)) {
4104             frag.appendChild(n);
4105         }
4106         return frag;
4107     };
4108 }
4109
4110 /**
4111  * @class Roo.DomHelper
4112  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4113  * 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>.
4114  * @singleton
4115  */
4116 Roo.DomHelper = function(){
4117     var tempTableEl = null;
4118     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4119     var tableRe = /^table|tbody|tr|td$/i;
4120     var xmlns = {};
4121     // build as innerHTML where available
4122     /** @ignore */
4123     var createHtml = function(o){
4124         if(typeof o == 'string'){
4125             return o;
4126         }
4127         var b = "";
4128         if(!o.tag){
4129             o.tag = "div";
4130         }
4131         b += "<" + o.tag;
4132         for(var attr in o){
4133             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4134             if(attr == "style"){
4135                 var s = o["style"];
4136                 if(typeof s == "function"){
4137                     s = s.call();
4138                 }
4139                 if(typeof s == "string"){
4140                     b += ' style="' + s + '"';
4141                 }else if(typeof s == "object"){
4142                     b += ' style="';
4143                     for(var key in s){
4144                         if(typeof s[key] != "function"){
4145                             b += key + ":" + s[key] + ";";
4146                         }
4147                     }
4148                     b += '"';
4149                 }
4150             }else{
4151                 if(attr == "cls"){
4152                     b += ' class="' + o["cls"] + '"';
4153                 }else if(attr == "htmlFor"){
4154                     b += ' for="' + o["htmlFor"] + '"';
4155                 }else{
4156                     b += " " + attr + '="' + o[attr] + '"';
4157                 }
4158             }
4159         }
4160         if(emptyTags.test(o.tag)){
4161             b += "/>";
4162         }else{
4163             b += ">";
4164             var cn = o.children || o.cn;
4165             if(cn){
4166                 //http://bugs.kde.org/show_bug.cgi?id=71506
4167                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4168                     for(var i = 0, len = cn.length; i < len; i++) {
4169                         b += createHtml(cn[i], b);
4170                     }
4171                 }else{
4172                     b += createHtml(cn, b);
4173                 }
4174             }
4175             if(o.html){
4176                 b += o.html;
4177             }
4178             b += "</" + o.tag + ">";
4179         }
4180         return b;
4181     };
4182
4183     // build as dom
4184     /** @ignore */
4185     var createDom = function(o, parentNode){
4186          
4187         // defininition craeted..
4188         var ns = false;
4189         if (o.ns && o.ns != 'html') {
4190                
4191             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4192                 xmlns[o.ns] = o.xmlns;
4193                 ns = o.xmlns;
4194             }
4195             if (typeof(xmlns[o.ns]) == 'undefined') {
4196                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4197             }
4198             ns = xmlns[o.ns];
4199         }
4200         
4201         
4202         if (typeof(o) == 'string') {
4203             return parentNode.appendChild(document.createTextNode(o));
4204         }
4205         o.tag = o.tag || div;
4206         if (o.ns && Roo.isIE) {
4207             ns = false;
4208             o.tag = o.ns + ':' + o.tag;
4209             
4210         }
4211         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4212         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4213         for(var attr in o){
4214             
4215             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4216                     attr == "style" || typeof o[attr] == "function") continue;
4217                     
4218             if(attr=="cls" && Roo.isIE){
4219                 el.className = o["cls"];
4220             }else{
4221                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4222                 else el[attr] = o[attr];
4223             }
4224         }
4225         Roo.DomHelper.applyStyles(el, o.style);
4226         var cn = o.children || o.cn;
4227         if(cn){
4228             //http://bugs.kde.org/show_bug.cgi?id=71506
4229              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                 for(var i = 0, len = cn.length; i < len; i++) {
4231                     createDom(cn[i], el);
4232                 }
4233             }else{
4234                 createDom(cn, el);
4235             }
4236         }
4237         if(o.html){
4238             el.innerHTML = o.html;
4239         }
4240         if(parentNode){
4241            parentNode.appendChild(el);
4242         }
4243         return el;
4244     };
4245
4246     var ieTable = function(depth, s, h, e){
4247         tempTableEl.innerHTML = [s, h, e].join('');
4248         var i = -1, el = tempTableEl;
4249         while(++i < depth){
4250             el = el.firstChild;
4251         }
4252         return el;
4253     };
4254
4255     // kill repeat to save bytes
4256     var ts = '<table>',
4257         te = '</table>',
4258         tbs = ts+'<tbody>',
4259         tbe = '</tbody>'+te,
4260         trs = tbs + '<tr>',
4261         tre = '</tr>'+tbe;
4262
4263     /**
4264      * @ignore
4265      * Nasty code for IE's broken table implementation
4266      */
4267     var insertIntoTable = function(tag, where, el, html){
4268         if(!tempTableEl){
4269             tempTableEl = document.createElement('div');
4270         }
4271         var node;
4272         var before = null;
4273         if(tag == 'td'){
4274             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4275                 return;
4276             }
4277             if(where == 'beforebegin'){
4278                 before = el;
4279                 el = el.parentNode;
4280             } else{
4281                 before = el.nextSibling;
4282                 el = el.parentNode;
4283             }
4284             node = ieTable(4, trs, html, tre);
4285         }
4286         else if(tag == 'tr'){
4287             if(where == 'beforebegin'){
4288                 before = el;
4289                 el = el.parentNode;
4290                 node = ieTable(3, tbs, html, tbe);
4291             } else if(where == 'afterend'){
4292                 before = el.nextSibling;
4293                 el = el.parentNode;
4294                 node = ieTable(3, tbs, html, tbe);
4295             } else{ // INTO a TR
4296                 if(where == 'afterbegin'){
4297                     before = el.firstChild;
4298                 }
4299                 node = ieTable(4, trs, html, tre);
4300             }
4301         } else if(tag == 'tbody'){
4302             if(where == 'beforebegin'){
4303                 before = el;
4304                 el = el.parentNode;
4305                 node = ieTable(2, ts, html, te);
4306             } else if(where == 'afterend'){
4307                 before = el.nextSibling;
4308                 el = el.parentNode;
4309                 node = ieTable(2, ts, html, te);
4310             } else{
4311                 if(where == 'afterbegin'){
4312                     before = el.firstChild;
4313                 }
4314                 node = ieTable(3, tbs, html, tbe);
4315             }
4316         } else{ // TABLE
4317             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4318                 return;
4319             }
4320             if(where == 'afterbegin'){
4321                 before = el.firstChild;
4322             }
4323             node = ieTable(2, ts, html, te);
4324         }
4325         el.insertBefore(node, before);
4326         return node;
4327     };
4328
4329     return {
4330     /** True to force the use of DOM instead of html fragments @type Boolean */
4331     useDom : false,
4332
4333     /**
4334      * Returns the markup for the passed Element(s) config
4335      * @param {Object} o The Dom object spec (and children)
4336      * @return {String}
4337      */
4338     markup : function(o){
4339         return createHtml(o);
4340     },
4341
4342     /**
4343      * Applies a style specification to an element
4344      * @param {String/HTMLElement} el The element to apply styles to
4345      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4346      * a function which returns such a specification.
4347      */
4348     applyStyles : function(el, styles){
4349         if(styles){
4350            el = Roo.fly(el);
4351            if(typeof styles == "string"){
4352                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4353                var matches;
4354                while ((matches = re.exec(styles)) != null){
4355                    el.setStyle(matches[1], matches[2]);
4356                }
4357            }else if (typeof styles == "object"){
4358                for (var style in styles){
4359                   el.setStyle(style, styles[style]);
4360                }
4361            }else if (typeof styles == "function"){
4362                 Roo.DomHelper.applyStyles(el, styles.call());
4363            }
4364         }
4365     },
4366
4367     /**
4368      * Inserts an HTML fragment into the Dom
4369      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4370      * @param {HTMLElement} el The context element
4371      * @param {String} html The HTML fragmenet
4372      * @return {HTMLElement} The new node
4373      */
4374     insertHtml : function(where, el, html){
4375         where = where.toLowerCase();
4376         if(el.insertAdjacentHTML){
4377             if(tableRe.test(el.tagName)){
4378                 var rs;
4379                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4380                     return rs;
4381                 }
4382             }
4383             switch(where){
4384                 case "beforebegin":
4385                     el.insertAdjacentHTML('BeforeBegin', html);
4386                     return el.previousSibling;
4387                 case "afterbegin":
4388                     el.insertAdjacentHTML('AfterBegin', html);
4389                     return el.firstChild;
4390                 case "beforeend":
4391                     el.insertAdjacentHTML('BeforeEnd', html);
4392                     return el.lastChild;
4393                 case "afterend":
4394                     el.insertAdjacentHTML('AfterEnd', html);
4395                     return el.nextSibling;
4396             }
4397             throw 'Illegal insertion point -> "' + where + '"';
4398         }
4399         var range = el.ownerDocument.createRange();
4400         var frag;
4401         switch(where){
4402              case "beforebegin":
4403                 range.setStartBefore(el);
4404                 frag = range.createContextualFragment(html);
4405                 el.parentNode.insertBefore(frag, el);
4406                 return el.previousSibling;
4407              case "afterbegin":
4408                 if(el.firstChild){
4409                     range.setStartBefore(el.firstChild);
4410                     frag = range.createContextualFragment(html);
4411                     el.insertBefore(frag, el.firstChild);
4412                     return el.firstChild;
4413                 }else{
4414                     el.innerHTML = html;
4415                     return el.firstChild;
4416                 }
4417             case "beforeend":
4418                 if(el.lastChild){
4419                     range.setStartAfter(el.lastChild);
4420                     frag = range.createContextualFragment(html);
4421                     el.appendChild(frag);
4422                     return el.lastChild;
4423                 }else{
4424                     el.innerHTML = html;
4425                     return el.lastChild;
4426                 }
4427             case "afterend":
4428                 range.setStartAfter(el);
4429                 frag = range.createContextualFragment(html);
4430                 el.parentNode.insertBefore(frag, el.nextSibling);
4431                 return el.nextSibling;
4432             }
4433             throw 'Illegal insertion point -> "' + where + '"';
4434     },
4435
4436     /**
4437      * Creates new Dom element(s) and inserts them before el
4438      * @param {String/HTMLElement/Element} el The context element
4439      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4440      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4441      * @return {HTMLElement/Roo.Element} The new node
4442      */
4443     insertBefore : function(el, o, returnElement){
4444         return this.doInsert(el, o, returnElement, "beforeBegin");
4445     },
4446
4447     /**
4448      * Creates new Dom element(s) and inserts them after el
4449      * @param {String/HTMLElement/Element} el The context element
4450      * @param {Object} o The Dom object spec (and children)
4451      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4452      * @return {HTMLElement/Roo.Element} The new node
4453      */
4454     insertAfter : function(el, o, returnElement){
4455         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4456     },
4457
4458     /**
4459      * Creates new Dom element(s) and inserts them as the first child of el
4460      * @param {String/HTMLElement/Element} el The context element
4461      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4462      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4463      * @return {HTMLElement/Roo.Element} The new node
4464      */
4465     insertFirst : function(el, o, returnElement){
4466         return this.doInsert(el, o, returnElement, "afterBegin");
4467     },
4468
4469     // private
4470     doInsert : function(el, o, returnElement, pos, sibling){
4471         el = Roo.getDom(el);
4472         var newNode;
4473         if(this.useDom || o.ns){
4474             newNode = createDom(o, null);
4475             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4476         }else{
4477             var html = createHtml(o);
4478             newNode = this.insertHtml(pos, el, html);
4479         }
4480         return returnElement ? Roo.get(newNode, true) : newNode;
4481     },
4482
4483     /**
4484      * Creates new Dom element(s) and appends them to el
4485      * @param {String/HTMLElement/Element} el The context element
4486      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4487      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4488      * @return {HTMLElement/Roo.Element} The new node
4489      */
4490     append : function(el, o, returnElement){
4491         el = Roo.getDom(el);
4492         var newNode;
4493         if(this.useDom || o.ns){
4494             newNode = createDom(o, null);
4495             el.appendChild(newNode);
4496         }else{
4497             var html = createHtml(o);
4498             newNode = this.insertHtml("beforeEnd", el, html);
4499         }
4500         return returnElement ? Roo.get(newNode, true) : newNode;
4501     },
4502
4503     /**
4504      * Creates new Dom element(s) and overwrites the contents of el with them
4505      * @param {String/HTMLElement/Element} el The context element
4506      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4507      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4508      * @return {HTMLElement/Roo.Element} The new node
4509      */
4510     overwrite : function(el, o, returnElement){
4511         el = Roo.getDom(el);
4512         if (o.ns) {
4513           
4514             while (el.childNodes.length) {
4515                 el.removeChild(el.firstChild);
4516             }
4517             createDom(o, el);
4518         } else {
4519             el.innerHTML = createHtml(o);   
4520         }
4521         
4522         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4523     },
4524
4525     /**
4526      * Creates a new Roo.DomHelper.Template from the Dom object spec
4527      * @param {Object} o The Dom object spec (and children)
4528      * @return {Roo.DomHelper.Template} The new template
4529      */
4530     createTemplate : function(o){
4531         var html = createHtml(o);
4532         return new Roo.Template(html);
4533     }
4534     };
4535 }();
4536 /*
4537  * Based on:
4538  * Ext JS Library 1.1.1
4539  * Copyright(c) 2006-2007, Ext JS, LLC.
4540  *
4541  * Originally Released Under LGPL - original licence link has changed is not relivant.
4542  *
4543  * Fork - LGPL
4544  * <script type="text/javascript">
4545  */
4546  
4547 /**
4548 * @class Roo.Template
4549 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4550 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4551 * Usage:
4552 <pre><code>
4553 var t = new Roo.Template({
4554     html :  '&lt;div name="{id}"&gt;' + 
4555         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4556         '&lt;/div&gt;',
4557     myformat: function (value, allValues) {
4558         return 'XX' + value;
4559     }
4560 });
4561 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4562 </code></pre>
4563 * For more information see this blog post with examples:
4564 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4565      - Create Elements using DOM, HTML fragments and Templates</a>. 
4566 * @constructor
4567 * @param {Object} cfg - Configuration object.
4568 */
4569 Roo.Template = function(cfg){
4570     // BC!
4571     if(cfg instanceof Array){
4572         cfg = cfg.join("");
4573     }else if(arguments.length > 1){
4574         cfg = Array.prototype.join.call(arguments, "");
4575     }
4576     
4577     
4578     if (typeof(cfg) == 'object') {
4579         Roo.apply(this,cfg)
4580     } else {
4581         // bc
4582         this.html = cfg;
4583     }
4584     if (this.url) {
4585         this.load();
4586     }
4587     
4588 };
4589 Roo.Template.prototype = {
4590     
4591     /**
4592      * @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..
4593      *                    it should be fixed so that template is observable...
4594      */
4595     url : false,
4596     /**
4597      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4598      */
4599     html : '',
4600     /**
4601      * Returns an HTML fragment of this template with the specified values applied.
4602      * @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'})
4603      * @return {String} The HTML fragment
4604      */
4605     applyTemplate : function(values){
4606         try {
4607            
4608             if(this.compiled){
4609                 return this.compiled(values);
4610             }
4611             var useF = this.disableFormats !== true;
4612             var fm = Roo.util.Format, tpl = this;
4613             var fn = function(m, name, format, args){
4614                 if(format && useF){
4615                     if(format.substr(0, 5) == "this."){
4616                         return tpl.call(format.substr(5), values[name], values);
4617                     }else{
4618                         if(args){
4619                             // quoted values are required for strings in compiled templates, 
4620                             // but for non compiled we need to strip them
4621                             // quoted reversed for jsmin
4622                             var re = /^\s*['"](.*)["']\s*$/;
4623                             args = args.split(',');
4624                             for(var i = 0, len = args.length; i < len; i++){
4625                                 args[i] = args[i].replace(re, "$1");
4626                             }
4627                             args = [values[name]].concat(args);
4628                         }else{
4629                             args = [values[name]];
4630                         }
4631                         return fm[format].apply(fm, args);
4632                     }
4633                 }else{
4634                     return values[name] !== undefined ? values[name] : "";
4635                 }
4636             };
4637             return this.html.replace(this.re, fn);
4638         } catch (e) {
4639             Roo.log(e);
4640             throw e;
4641         }
4642          
4643     },
4644     
4645     loading : false,
4646       
4647     load : function ()
4648     {
4649          
4650         if (this.loading) {
4651             return;
4652         }
4653         var _t = this;
4654         
4655         this.loading = true;
4656         this.compiled = false;
4657         
4658         var cx = new Roo.data.Connection();
4659         cx.request({
4660             url : this.url,
4661             method : 'GET',
4662             success : function (response) {
4663                 _t.loading = false;
4664                 _t.html = response.responseText;
4665                 _t.url = false;
4666                 _t.compile();
4667              },
4668             failure : function(response) {
4669                 Roo.log("Template failed to load from " + _t.url);
4670                 _t.loading = false;
4671             }
4672         });
4673     },
4674
4675     /**
4676      * Sets the HTML used as the template and optionally compiles it.
4677      * @param {String} html
4678      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4679      * @return {Roo.Template} this
4680      */
4681     set : function(html, compile){
4682         this.html = html;
4683         this.compiled = null;
4684         if(compile){
4685             this.compile();
4686         }
4687         return this;
4688     },
4689     
4690     /**
4691      * True to disable format functions (defaults to false)
4692      * @type Boolean
4693      */
4694     disableFormats : false,
4695     
4696     /**
4697     * The regular expression used to match template variables 
4698     * @type RegExp
4699     * @property 
4700     */
4701     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4702     
4703     /**
4704      * Compiles the template into an internal function, eliminating the RegEx overhead.
4705      * @return {Roo.Template} this
4706      */
4707     compile : function(){
4708         var fm = Roo.util.Format;
4709         var useF = this.disableFormats !== true;
4710         var sep = Roo.isGecko ? "+" : ",";
4711         var fn = function(m, name, format, args){
4712             if(format && useF){
4713                 args = args ? ',' + args : "";
4714                 if(format.substr(0, 5) != "this."){
4715                     format = "fm." + format + '(';
4716                 }else{
4717                     format = 'this.call("'+ format.substr(5) + '", ';
4718                     args = ", values";
4719                 }
4720             }else{
4721                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4722             }
4723             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4724         };
4725         var body;
4726         // branched to use + in gecko and [].join() in others
4727         if(Roo.isGecko){
4728             body = "this.compiled = function(values){ return '" +
4729                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4730                     "';};";
4731         }else{
4732             body = ["this.compiled = function(values){ return ['"];
4733             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4734             body.push("'].join('');};");
4735             body = body.join('');
4736         }
4737         /**
4738          * eval:var:values
4739          * eval:var:fm
4740          */
4741         eval(body);
4742         return this;
4743     },
4744     
4745     // private function used to call members
4746     call : function(fnName, value, allValues){
4747         return this[fnName](value, allValues);
4748     },
4749     
4750     /**
4751      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4752      * @param {String/HTMLElement/Roo.Element} el The context element
4753      * @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'})
4754      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4755      * @return {HTMLElement/Roo.Element} The new node or Element
4756      */
4757     insertFirst: function(el, values, returnElement){
4758         return this.doInsert('afterBegin', el, values, returnElement);
4759     },
4760
4761     /**
4762      * Applies the supplied values to the template and inserts the new node(s) before el.
4763      * @param {String/HTMLElement/Roo.Element} el The context element
4764      * @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'})
4765      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4766      * @return {HTMLElement/Roo.Element} The new node or Element
4767      */
4768     insertBefore: function(el, values, returnElement){
4769         return this.doInsert('beforeBegin', el, values, returnElement);
4770     },
4771
4772     /**
4773      * Applies the supplied values to the template and inserts the new node(s) after el.
4774      * @param {String/HTMLElement/Roo.Element} el The context element
4775      * @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'})
4776      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4777      * @return {HTMLElement/Roo.Element} The new node or Element
4778      */
4779     insertAfter : function(el, values, returnElement){
4780         return this.doInsert('afterEnd', el, values, returnElement);
4781     },
4782     
4783     /**
4784      * Applies the supplied values to the template and appends the new node(s) to el.
4785      * @param {String/HTMLElement/Roo.Element} el The context element
4786      * @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'})
4787      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4788      * @return {HTMLElement/Roo.Element} The new node or Element
4789      */
4790     append : function(el, values, returnElement){
4791         return this.doInsert('beforeEnd', el, values, returnElement);
4792     },
4793
4794     doInsert : function(where, el, values, returnEl){
4795         el = Roo.getDom(el);
4796         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4797         return returnEl ? Roo.get(newNode, true) : newNode;
4798     },
4799
4800     /**
4801      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4802      * @param {String/HTMLElement/Roo.Element} el The context element
4803      * @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'})
4804      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4805      * @return {HTMLElement/Roo.Element} The new node or Element
4806      */
4807     overwrite : function(el, values, returnElement){
4808         el = Roo.getDom(el);
4809         el.innerHTML = this.applyTemplate(values);
4810         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4811     }
4812 };
4813 /**
4814  * Alias for {@link #applyTemplate}
4815  * @method
4816  */
4817 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4818
4819 // backwards compat
4820 Roo.DomHelper.Template = Roo.Template;
4821
4822 /**
4823  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4824  * @param {String/HTMLElement} el A DOM element or its id
4825  * @returns {Roo.Template} The created template
4826  * @static
4827  */
4828 Roo.Template.from = function(el){
4829     el = Roo.getDom(el);
4830     return new Roo.Template(el.value || el.innerHTML);
4831 };/*
4832  * Based on:
4833  * Ext JS Library 1.1.1
4834  * Copyright(c) 2006-2007, Ext JS, LLC.
4835  *
4836  * Originally Released Under LGPL - original licence link has changed is not relivant.
4837  *
4838  * Fork - LGPL
4839  * <script type="text/javascript">
4840  */
4841  
4842
4843 /*
4844  * This is code is also distributed under MIT license for use
4845  * with jQuery and prototype JavaScript libraries.
4846  */
4847 /**
4848  * @class Roo.DomQuery
4849 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).
4850 <p>
4851 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>
4852
4853 <p>
4854 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.
4855 </p>
4856 <h4>Element Selectors:</h4>
4857 <ul class="list">
4858     <li> <b>*</b> any element</li>
4859     <li> <b>E</b> an element with the tag E</li>
4860     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4861     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4862     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4863     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4864 </ul>
4865 <h4>Attribute Selectors:</h4>
4866 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4867 <ul class="list">
4868     <li> <b>E[foo]</b> has an attribute "foo"</li>
4869     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4870     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4871     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4872     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4873     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4874     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4875 </ul>
4876 <h4>Pseudo Classes:</h4>
4877 <ul class="list">
4878     <li> <b>E:first-child</b> E is the first child of its parent</li>
4879     <li> <b>E:last-child</b> E is the last child of its parent</li>
4880     <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>
4881     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4882     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4883     <li> <b>E:only-child</b> E is the only child of its parent</li>
4884     <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>
4885     <li> <b>E:first</b> the first E in the resultset</li>
4886     <li> <b>E:last</b> the last E in the resultset</li>
4887     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4888     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4889     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4890     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4891     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4892     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4893     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4894     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4895     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4896 </ul>
4897 <h4>CSS Value Selectors:</h4>
4898 <ul class="list">
4899     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4900     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4901     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4902     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4903     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4904     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4905 </ul>
4906  * @singleton
4907  */
4908 Roo.DomQuery = function(){
4909     var cache = {}, simpleCache = {}, valueCache = {};
4910     var nonSpace = /\S/;
4911     var trimRe = /^\s+|\s+$/g;
4912     var tplRe = /\{(\d+)\}/g;
4913     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4914     var tagTokenRe = /^(#)?([\w-\*]+)/;
4915     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4916
4917     function child(p, index){
4918         var i = 0;
4919         var n = p.firstChild;
4920         while(n){
4921             if(n.nodeType == 1){
4922                if(++i == index){
4923                    return n;
4924                }
4925             }
4926             n = n.nextSibling;
4927         }
4928         return null;
4929     };
4930
4931     function next(n){
4932         while((n = n.nextSibling) && n.nodeType != 1);
4933         return n;
4934     };
4935
4936     function prev(n){
4937         while((n = n.previousSibling) && n.nodeType != 1);
4938         return n;
4939     };
4940
4941     function children(d){
4942         var n = d.firstChild, ni = -1;
4943             while(n){
4944                 var nx = n.nextSibling;
4945                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4946                     d.removeChild(n);
4947                 }else{
4948                     n.nodeIndex = ++ni;
4949                 }
4950                 n = nx;
4951             }
4952             return this;
4953         };
4954
4955     function byClassName(c, a, v){
4956         if(!v){
4957             return c;
4958         }
4959         var r = [], ri = -1, cn;
4960         for(var i = 0, ci; ci = c[i]; i++){
4961             if((' '+ci.className+' ').indexOf(v) != -1){
4962                 r[++ri] = ci;
4963             }
4964         }
4965         return r;
4966     };
4967
4968     function attrValue(n, attr){
4969         if(!n.tagName && typeof n.length != "undefined"){
4970             n = n[0];
4971         }
4972         if(!n){
4973             return null;
4974         }
4975         if(attr == "for"){
4976             return n.htmlFor;
4977         }
4978         if(attr == "class" || attr == "className"){
4979             return n.className;
4980         }
4981         return n.getAttribute(attr) || n[attr];
4982
4983     };
4984
4985     function getNodes(ns, mode, tagName){
4986         var result = [], ri = -1, cs;
4987         if(!ns){
4988             return result;
4989         }
4990         tagName = tagName || "*";
4991         if(typeof ns.getElementsByTagName != "undefined"){
4992             ns = [ns];
4993         }
4994         if(!mode){
4995             for(var i = 0, ni; ni = ns[i]; i++){
4996                 cs = ni.getElementsByTagName(tagName);
4997                 for(var j = 0, ci; ci = cs[j]; j++){
4998                     result[++ri] = ci;
4999                 }
5000             }
5001         }else if(mode == "/" || mode == ">"){
5002             var utag = tagName.toUpperCase();
5003             for(var i = 0, ni, cn; ni = ns[i]; i++){
5004                 cn = ni.children || ni.childNodes;
5005                 for(var j = 0, cj; cj = cn[j]; j++){
5006                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5007                         result[++ri] = cj;
5008                     }
5009                 }
5010             }
5011         }else if(mode == "+"){
5012             var utag = tagName.toUpperCase();
5013             for(var i = 0, n; n = ns[i]; i++){
5014                 while((n = n.nextSibling) && n.nodeType != 1);
5015                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5016                     result[++ri] = n;
5017                 }
5018             }
5019         }else if(mode == "~"){
5020             for(var i = 0, n; n = ns[i]; i++){
5021                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5022                 if(n){
5023                     result[++ri] = n;
5024                 }
5025             }
5026         }
5027         return result;
5028     };
5029
5030     function concat(a, b){
5031         if(b.slice){
5032             return a.concat(b);
5033         }
5034         for(var i = 0, l = b.length; i < l; i++){
5035             a[a.length] = b[i];
5036         }
5037         return a;
5038     }
5039
5040     function byTag(cs, tagName){
5041         if(cs.tagName || cs == document){
5042             cs = [cs];
5043         }
5044         if(!tagName){
5045             return cs;
5046         }
5047         var r = [], ri = -1;
5048         tagName = tagName.toLowerCase();
5049         for(var i = 0, ci; ci = cs[i]; i++){
5050             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5051                 r[++ri] = ci;
5052             }
5053         }
5054         return r;
5055     };
5056
5057     function byId(cs, attr, id){
5058         if(cs.tagName || cs == document){
5059             cs = [cs];
5060         }
5061         if(!id){
5062             return cs;
5063         }
5064         var r = [], ri = -1;
5065         for(var i = 0,ci; ci = cs[i]; i++){
5066             if(ci && ci.id == id){
5067                 r[++ri] = ci;
5068                 return r;
5069             }
5070         }
5071         return r;
5072     };
5073
5074     function byAttribute(cs, attr, value, op, custom){
5075         var r = [], ri = -1, st = custom=="{";
5076         var f = Roo.DomQuery.operators[op];
5077         for(var i = 0, ci; ci = cs[i]; i++){
5078             var a;
5079             if(st){
5080                 a = Roo.DomQuery.getStyle(ci, attr);
5081             }
5082             else if(attr == "class" || attr == "className"){
5083                 a = ci.className;
5084             }else if(attr == "for"){
5085                 a = ci.htmlFor;
5086             }else if(attr == "href"){
5087                 a = ci.getAttribute("href", 2);
5088             }else{
5089                 a = ci.getAttribute(attr);
5090             }
5091             if((f && f(a, value)) || (!f && a)){
5092                 r[++ri] = ci;
5093             }
5094         }
5095         return r;
5096     };
5097
5098     function byPseudo(cs, name, value){
5099         return Roo.DomQuery.pseudos[name](cs, value);
5100     };
5101
5102     // This is for IE MSXML which does not support expandos.
5103     // IE runs the same speed using setAttribute, however FF slows way down
5104     // and Safari completely fails so they need to continue to use expandos.
5105     var isIE = window.ActiveXObject ? true : false;
5106
5107     // this eval is stop the compressor from
5108     // renaming the variable to something shorter
5109     
5110     /** eval:var:batch */
5111     var batch = 30803; 
5112
5113     var key = 30803;
5114
5115     function nodupIEXml(cs){
5116         var d = ++key;
5117         cs[0].setAttribute("_nodup", d);
5118         var r = [cs[0]];
5119         for(var i = 1, len = cs.length; i < len; i++){
5120             var c = cs[i];
5121             if(!c.getAttribute("_nodup") != d){
5122                 c.setAttribute("_nodup", d);
5123                 r[r.length] = c;
5124             }
5125         }
5126         for(var i = 0, len = cs.length; i < len; i++){
5127             cs[i].removeAttribute("_nodup");
5128         }
5129         return r;
5130     }
5131
5132     function nodup(cs){
5133         if(!cs){
5134             return [];
5135         }
5136         var len = cs.length, c, i, r = cs, cj, ri = -1;
5137         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5138             return cs;
5139         }
5140         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5141             return nodupIEXml(cs);
5142         }
5143         var d = ++key;
5144         cs[0]._nodup = d;
5145         for(i = 1; c = cs[i]; i++){
5146             if(c._nodup != d){
5147                 c._nodup = d;
5148             }else{
5149                 r = [];
5150                 for(var j = 0; j < i; j++){
5151                     r[++ri] = cs[j];
5152                 }
5153                 for(j = i+1; cj = cs[j]; j++){
5154                     if(cj._nodup != d){
5155                         cj._nodup = d;
5156                         r[++ri] = cj;
5157                     }
5158                 }
5159                 return r;
5160             }
5161         }
5162         return r;
5163     }
5164
5165     function quickDiffIEXml(c1, c2){
5166         var d = ++key;
5167         for(var i = 0, len = c1.length; i < len; i++){
5168             c1[i].setAttribute("_qdiff", d);
5169         }
5170         var r = [];
5171         for(var i = 0, len = c2.length; i < len; i++){
5172             if(c2[i].getAttribute("_qdiff") != d){
5173                 r[r.length] = c2[i];
5174             }
5175         }
5176         for(var i = 0, len = c1.length; i < len; i++){
5177            c1[i].removeAttribute("_qdiff");
5178         }
5179         return r;
5180     }
5181
5182     function quickDiff(c1, c2){
5183         var len1 = c1.length;
5184         if(!len1){
5185             return c2;
5186         }
5187         if(isIE && c1[0].selectSingleNode){
5188             return quickDiffIEXml(c1, c2);
5189         }
5190         var d = ++key;
5191         for(var i = 0; i < len1; i++){
5192             c1[i]._qdiff = d;
5193         }
5194         var r = [];
5195         for(var i = 0, len = c2.length; i < len; i++){
5196             if(c2[i]._qdiff != d){
5197                 r[r.length] = c2[i];
5198             }
5199         }
5200         return r;
5201     }
5202
5203     function quickId(ns, mode, root, id){
5204         if(ns == root){
5205            var d = root.ownerDocument || root;
5206            return d.getElementById(id);
5207         }
5208         ns = getNodes(ns, mode, "*");
5209         return byId(ns, null, id);
5210     }
5211
5212     return {
5213         getStyle : function(el, name){
5214             return Roo.fly(el).getStyle(name);
5215         },
5216         /**
5217          * Compiles a selector/xpath query into a reusable function. The returned function
5218          * takes one parameter "root" (optional), which is the context node from where the query should start.
5219          * @param {String} selector The selector/xpath query
5220          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5221          * @return {Function}
5222          */
5223         compile : function(path, type){
5224             type = type || "select";
5225             
5226             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5227             var q = path, mode, lq;
5228             var tk = Roo.DomQuery.matchers;
5229             var tklen = tk.length;
5230             var mm;
5231
5232             // accept leading mode switch
5233             var lmode = q.match(modeRe);
5234             if(lmode && lmode[1]){
5235                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5236                 q = q.replace(lmode[1], "");
5237             }
5238             // strip leading slashes
5239             while(path.substr(0, 1)=="/"){
5240                 path = path.substr(1);
5241             }
5242
5243             while(q && lq != q){
5244                 lq = q;
5245                 var tm = q.match(tagTokenRe);
5246                 if(type == "select"){
5247                     if(tm){
5248                         if(tm[1] == "#"){
5249                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5250                         }else{
5251                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5252                         }
5253                         q = q.replace(tm[0], "");
5254                     }else if(q.substr(0, 1) != '@'){
5255                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5256                     }
5257                 }else{
5258                     if(tm){
5259                         if(tm[1] == "#"){
5260                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5261                         }else{
5262                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5263                         }
5264                         q = q.replace(tm[0], "");
5265                     }
5266                 }
5267                 while(!(mm = q.match(modeRe))){
5268                     var matched = false;
5269                     for(var j = 0; j < tklen; j++){
5270                         var t = tk[j];
5271                         var m = q.match(t.re);
5272                         if(m){
5273                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5274                                                     return m[i];
5275                                                 });
5276                             q = q.replace(m[0], "");
5277                             matched = true;
5278                             break;
5279                         }
5280                     }
5281                     // prevent infinite loop on bad selector
5282                     if(!matched){
5283                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5284                     }
5285                 }
5286                 if(mm[1]){
5287                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5288                     q = q.replace(mm[1], "");
5289                 }
5290             }
5291             fn[fn.length] = "return nodup(n);\n}";
5292             
5293              /** 
5294               * list of variables that need from compression as they are used by eval.
5295              *  eval:var:batch 
5296              *  eval:var:nodup
5297              *  eval:var:byTag
5298              *  eval:var:ById
5299              *  eval:var:getNodes
5300              *  eval:var:quickId
5301              *  eval:var:mode
5302              *  eval:var:root
5303              *  eval:var:n
5304              *  eval:var:byClassName
5305              *  eval:var:byPseudo
5306              *  eval:var:byAttribute
5307              *  eval:var:attrValue
5308              * 
5309              **/ 
5310             eval(fn.join(""));
5311             return f;
5312         },
5313
5314         /**
5315          * Selects a group of elements.
5316          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5317          * @param {Node} root (optional) The start of the query (defaults to document).
5318          * @return {Array}
5319          */
5320         select : function(path, root, type){
5321             if(!root || root == document){
5322                 root = document;
5323             }
5324             if(typeof root == "string"){
5325                 root = document.getElementById(root);
5326             }
5327             var paths = path.split(",");
5328             var results = [];
5329             for(var i = 0, len = paths.length; i < len; i++){
5330                 var p = paths[i].replace(trimRe, "");
5331                 if(!cache[p]){
5332                     cache[p] = Roo.DomQuery.compile(p);
5333                     if(!cache[p]){
5334                         throw p + " is not a valid selector";
5335                     }
5336                 }
5337                 var result = cache[p](root);
5338                 if(result && result != document){
5339                     results = results.concat(result);
5340                 }
5341             }
5342             if(paths.length > 1){
5343                 return nodup(results);
5344             }
5345             return results;
5346         },
5347
5348         /**
5349          * Selects a single element.
5350          * @param {String} selector The selector/xpath query
5351          * @param {Node} root (optional) The start of the query (defaults to document).
5352          * @return {Element}
5353          */
5354         selectNode : function(path, root){
5355             return Roo.DomQuery.select(path, root)[0];
5356         },
5357
5358         /**
5359          * Selects the value of a node, optionally replacing null with the defaultValue.
5360          * @param {String} selector The selector/xpath query
5361          * @param {Node} root (optional) The start of the query (defaults to document).
5362          * @param {String} defaultValue
5363          */
5364         selectValue : function(path, root, defaultValue){
5365             path = path.replace(trimRe, "");
5366             if(!valueCache[path]){
5367                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5368             }
5369             var n = valueCache[path](root);
5370             n = n[0] ? n[0] : n;
5371             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5372             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5373         },
5374
5375         /**
5376          * Selects the value of a node, parsing integers and floats.
5377          * @param {String} selector The selector/xpath query
5378          * @param {Node} root (optional) The start of the query (defaults to document).
5379          * @param {Number} defaultValue
5380          * @return {Number}
5381          */
5382         selectNumber : function(path, root, defaultValue){
5383             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5384             return parseFloat(v);
5385         },
5386
5387         /**
5388          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5389          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5390          * @param {String} selector The simple selector to test
5391          * @return {Boolean}
5392          */
5393         is : function(el, ss){
5394             if(typeof el == "string"){
5395                 el = document.getElementById(el);
5396             }
5397             var isArray = (el instanceof Array);
5398             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5399             return isArray ? (result.length == el.length) : (result.length > 0);
5400         },
5401
5402         /**
5403          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5404          * @param {Array} el An array of elements to filter
5405          * @param {String} selector The simple selector to test
5406          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5407          * the selector instead of the ones that match
5408          * @return {Array}
5409          */
5410         filter : function(els, ss, nonMatches){
5411             ss = ss.replace(trimRe, "");
5412             if(!simpleCache[ss]){
5413                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5414             }
5415             var result = simpleCache[ss](els);
5416             return nonMatches ? quickDiff(result, els) : result;
5417         },
5418
5419         /**
5420          * Collection of matching regular expressions and code snippets.
5421          */
5422         matchers : [{
5423                 re: /^\.([\w-]+)/,
5424                 select: 'n = byClassName(n, null, " {1} ");'
5425             }, {
5426                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5427                 select: 'n = byPseudo(n, "{1}", "{2}");'
5428             },{
5429                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5430                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5431             }, {
5432                 re: /^#([\w-]+)/,
5433                 select: 'n = byId(n, null, "{1}");'
5434             },{
5435                 re: /^@([\w-]+)/,
5436                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5437             }
5438         ],
5439
5440         /**
5441          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5442          * 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;.
5443          */
5444         operators : {
5445             "=" : function(a, v){
5446                 return a == v;
5447             },
5448             "!=" : function(a, v){
5449                 return a != v;
5450             },
5451             "^=" : function(a, v){
5452                 return a && a.substr(0, v.length) == v;
5453             },
5454             "$=" : function(a, v){
5455                 return a && a.substr(a.length-v.length) == v;
5456             },
5457             "*=" : function(a, v){
5458                 return a && a.indexOf(v) !== -1;
5459             },
5460             "%=" : function(a, v){
5461                 return (a % v) == 0;
5462             },
5463             "|=" : function(a, v){
5464                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5465             },
5466             "~=" : function(a, v){
5467                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5468             }
5469         },
5470
5471         /**
5472          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5473          * and the argument (if any) supplied in the selector.
5474          */
5475         pseudos : {
5476             "first-child" : function(c){
5477                 var r = [], ri = -1, n;
5478                 for(var i = 0, ci; ci = n = c[i]; i++){
5479                     while((n = n.previousSibling) && n.nodeType != 1);
5480                     if(!n){
5481                         r[++ri] = ci;
5482                     }
5483                 }
5484                 return r;
5485             },
5486
5487             "last-child" : function(c){
5488                 var r = [], ri = -1, n;
5489                 for(var i = 0, ci; ci = n = c[i]; i++){
5490                     while((n = n.nextSibling) && n.nodeType != 1);
5491                     if(!n){
5492                         r[++ri] = ci;
5493                     }
5494                 }
5495                 return r;
5496             },
5497
5498             "nth-child" : function(c, a) {
5499                 var r = [], ri = -1;
5500                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5501                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5502                 for(var i = 0, n; n = c[i]; i++){
5503                     var pn = n.parentNode;
5504                     if (batch != pn._batch) {
5505                         var j = 0;
5506                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5507                             if(cn.nodeType == 1){
5508                                cn.nodeIndex = ++j;
5509                             }
5510                         }
5511                         pn._batch = batch;
5512                     }
5513                     if (f == 1) {
5514                         if (l == 0 || n.nodeIndex == l){
5515                             r[++ri] = n;
5516                         }
5517                     } else if ((n.nodeIndex + l) % f == 0){
5518                         r[++ri] = n;
5519                     }
5520                 }
5521
5522                 return r;
5523             },
5524
5525             "only-child" : function(c){
5526                 var r = [], ri = -1;;
5527                 for(var i = 0, ci; ci = c[i]; i++){
5528                     if(!prev(ci) && !next(ci)){
5529                         r[++ri] = ci;
5530                     }
5531                 }
5532                 return r;
5533             },
5534
5535             "empty" : function(c){
5536                 var r = [], ri = -1;
5537                 for(var i = 0, ci; ci = c[i]; i++){
5538                     var cns = ci.childNodes, j = 0, cn, empty = true;
5539                     while(cn = cns[j]){
5540                         ++j;
5541                         if(cn.nodeType == 1 || cn.nodeType == 3){
5542                             empty = false;
5543                             break;
5544                         }
5545                     }
5546                     if(empty){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "contains" : function(c, v){
5554                 var r = [], ri = -1;
5555                 for(var i = 0, ci; ci = c[i]; i++){
5556                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "nodeValue" : function(c, v){
5564                 var r = [], ri = -1;
5565                 for(var i = 0, ci; ci = c[i]; i++){
5566                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5567                         r[++ri] = ci;
5568                     }
5569                 }
5570                 return r;
5571             },
5572
5573             "checked" : function(c){
5574                 var r = [], ri = -1;
5575                 for(var i = 0, ci; ci = c[i]; i++){
5576                     if(ci.checked == true){
5577                         r[++ri] = ci;
5578                     }
5579                 }
5580                 return r;
5581             },
5582
5583             "not" : function(c, ss){
5584                 return Roo.DomQuery.filter(c, ss, true);
5585             },
5586
5587             "odd" : function(c){
5588                 return this["nth-child"](c, "odd");
5589             },
5590
5591             "even" : function(c){
5592                 return this["nth-child"](c, "even");
5593             },
5594
5595             "nth" : function(c, a){
5596                 return c[a-1] || [];
5597             },
5598
5599             "first" : function(c){
5600                 return c[0] || [];
5601             },
5602
5603             "last" : function(c){
5604                 return c[c.length-1] || [];
5605             },
5606
5607             "has" : function(c, ss){
5608                 var s = Roo.DomQuery.select;
5609                 var r = [], ri = -1;
5610                 for(var i = 0, ci; ci = c[i]; i++){
5611                     if(s(ss, ci).length > 0){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "next" : function(c, ss){
5619                 var is = Roo.DomQuery.is;
5620                 var r = [], ri = -1;
5621                 for(var i = 0, ci; ci = c[i]; i++){
5622                     var n = next(ci);
5623                     if(n && is(n, ss)){
5624                         r[++ri] = ci;
5625                     }
5626                 }
5627                 return r;
5628             },
5629
5630             "prev" : function(c, ss){
5631                 var is = Roo.DomQuery.is;
5632                 var r = [], ri = -1;
5633                 for(var i = 0, ci; ci = c[i]; i++){
5634                     var n = prev(ci);
5635                     if(n && is(n, ss)){
5636                         r[++ri] = ci;
5637                     }
5638                 }
5639                 return r;
5640             }
5641         }
5642     };
5643 }();
5644
5645 /**
5646  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5647  * @param {String} path The selector/xpath query
5648  * @param {Node} root (optional) The start of the query (defaults to document).
5649  * @return {Array}
5650  * @member Roo
5651  * @method query
5652  */
5653 Roo.query = Roo.DomQuery.select;
5654 /*
5655  * Based on:
5656  * Ext JS Library 1.1.1
5657  * Copyright(c) 2006-2007, Ext JS, LLC.
5658  *
5659  * Originally Released Under LGPL - original licence link has changed is not relivant.
5660  *
5661  * Fork - LGPL
5662  * <script type="text/javascript">
5663  */
5664
5665 /**
5666  * @class Roo.util.Observable
5667  * Base class that provides a common interface for publishing events. Subclasses are expected to
5668  * to have a property "events" with all the events defined.<br>
5669  * For example:
5670  * <pre><code>
5671  Employee = function(name){
5672     this.name = name;
5673     this.addEvents({
5674         "fired" : true,
5675         "quit" : true
5676     });
5677  }
5678  Roo.extend(Employee, Roo.util.Observable);
5679 </code></pre>
5680  * @param {Object} config properties to use (incuding events / listeners)
5681  */
5682
5683 Roo.util.Observable = function(cfg){
5684     
5685     cfg = cfg|| {};
5686     this.addEvents(cfg.events || {});
5687     if (cfg.events) {
5688         delete cfg.events; // make sure
5689     }
5690      
5691     Roo.apply(this, cfg);
5692     
5693     if(this.listeners){
5694         this.on(this.listeners);
5695         delete this.listeners;
5696     }
5697 };
5698 Roo.util.Observable.prototype = {
5699     /** 
5700  * @cfg {Object} listeners  list of events and functions to call for this object, 
5701  * For example :
5702  * <pre><code>
5703     listeners :  { 
5704        'click' : function(e) {
5705            ..... 
5706         } ,
5707         .... 
5708     } 
5709   </code></pre>
5710  */
5711     
5712     
5713     /**
5714      * Fires the specified event with the passed parameters (minus the event name).
5715      * @param {String} eventName
5716      * @param {Object...} args Variable number of parameters are passed to handlers
5717      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5718      */
5719     fireEvent : function(){
5720         var ce = this.events[arguments[0].toLowerCase()];
5721         if(typeof ce == "object"){
5722             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5723         }else{
5724             return true;
5725         }
5726     },
5727
5728     // private
5729     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5730
5731     /**
5732      * Appends an event handler to this component
5733      * @param {String}   eventName The type of event to listen for
5734      * @param {Function} handler The method the event invokes
5735      * @param {Object}   scope (optional) The scope in which to execute the handler
5736      * function. The handler function's "this" context.
5737      * @param {Object}   options (optional) An object containing handler configuration
5738      * properties. This may contain any of the following properties:<ul>
5739      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5740      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5741      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5742      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5743      * by the specified number of milliseconds. If the event fires again within that time, the original
5744      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5745      * </ul><br>
5746      * <p>
5747      * <b>Combining Options</b><br>
5748      * Using the options argument, it is possible to combine different types of listeners:<br>
5749      * <br>
5750      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5751                 <pre><code>
5752                 el.on('click', this.onClick, this, {
5753                         single: true,
5754                 delay: 100,
5755                 forumId: 4
5756                 });
5757                 </code></pre>
5758      * <p>
5759      * <b>Attaching multiple handlers in 1 call</b><br>
5760      * The method also allows for a single argument to be passed which is a config object containing properties
5761      * which specify multiple handlers.
5762      * <pre><code>
5763                 el.on({
5764                         'click': {
5765                         fn: this.onClick,
5766                         scope: this,
5767                         delay: 100
5768                 }, 
5769                 'mouseover': {
5770                         fn: this.onMouseOver,
5771                         scope: this
5772                 },
5773                 'mouseout': {
5774                         fn: this.onMouseOut,
5775                         scope: this
5776                 }
5777                 });
5778                 </code></pre>
5779      * <p>
5780      * Or a shorthand syntax which passes the same scope object to all handlers:
5781         <pre><code>
5782                 el.on({
5783                         'click': this.onClick,
5784                 'mouseover': this.onMouseOver,
5785                 'mouseout': this.onMouseOut,
5786                 scope: this
5787                 });
5788                 </code></pre>
5789      */
5790     addListener : function(eventName, fn, scope, o){
5791         if(typeof eventName == "object"){
5792             o = eventName;
5793             for(var e in o){
5794                 if(this.filterOptRe.test(e)){
5795                     continue;
5796                 }
5797                 if(typeof o[e] == "function"){
5798                     // shared options
5799                     this.addListener(e, o[e], o.scope,  o);
5800                 }else{
5801                     // individual options
5802                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5803                 }
5804             }
5805             return;
5806         }
5807         o = (!o || typeof o == "boolean") ? {} : o;
5808         eventName = eventName.toLowerCase();
5809         var ce = this.events[eventName] || true;
5810         if(typeof ce == "boolean"){
5811             ce = new Roo.util.Event(this, eventName);
5812             this.events[eventName] = ce;
5813         }
5814         ce.addListener(fn, scope, o);
5815     },
5816
5817     /**
5818      * Removes a listener
5819      * @param {String}   eventName     The type of event to listen for
5820      * @param {Function} handler        The handler to remove
5821      * @param {Object}   scope  (optional) The scope (this object) for the handler
5822      */
5823     removeListener : function(eventName, fn, scope){
5824         var ce = this.events[eventName.toLowerCase()];
5825         if(typeof ce == "object"){
5826             ce.removeListener(fn, scope);
5827         }
5828     },
5829
5830     /**
5831      * Removes all listeners for this object
5832      */
5833     purgeListeners : function(){
5834         for(var evt in this.events){
5835             if(typeof this.events[evt] == "object"){
5836                  this.events[evt].clearListeners();
5837             }
5838         }
5839     },
5840
5841     relayEvents : function(o, events){
5842         var createHandler = function(ename){
5843             return function(){
5844                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5845             };
5846         };
5847         for(var i = 0, len = events.length; i < len; i++){
5848             var ename = events[i];
5849             if(!this.events[ename]){ this.events[ename] = true; };
5850             o.on(ename, createHandler(ename), this);
5851         }
5852     },
5853
5854     /**
5855      * Used to define events on this Observable
5856      * @param {Object} object The object with the events defined
5857      */
5858     addEvents : function(o){
5859         if(!this.events){
5860             this.events = {};
5861         }
5862         Roo.applyIf(this.events, o);
5863     },
5864
5865     /**
5866      * Checks to see if this object has any listeners for a specified event
5867      * @param {String} eventName The name of the event to check for
5868      * @return {Boolean} True if the event is being listened for, else false
5869      */
5870     hasListener : function(eventName){
5871         var e = this.events[eventName];
5872         return typeof e == "object" && e.listeners.length > 0;
5873     }
5874 };
5875 /**
5876  * Appends an event handler to this element (shorthand for addListener)
5877  * @param {String}   eventName     The type of event to listen for
5878  * @param {Function} handler        The method the event invokes
5879  * @param {Object}   scope (optional) The scope in which to execute the handler
5880  * function. The handler function's "this" context.
5881  * @param {Object}   options  (optional)
5882  * @method
5883  */
5884 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5885 /**
5886  * Removes a listener (shorthand for removeListener)
5887  * @param {String}   eventName     The type of event to listen for
5888  * @param {Function} handler        The handler to remove
5889  * @param {Object}   scope  (optional) The scope (this object) for the handler
5890  * @method
5891  */
5892 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5893
5894 /**
5895  * Starts capture on the specified Observable. All events will be passed
5896  * to the supplied function with the event name + standard signature of the event
5897  * <b>before</b> the event is fired. If the supplied function returns false,
5898  * the event will not fire.
5899  * @param {Observable} o The Observable to capture
5900  * @param {Function} fn The function to call
5901  * @param {Object} scope (optional) The scope (this object) for the fn
5902  * @static
5903  */
5904 Roo.util.Observable.capture = function(o, fn, scope){
5905     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5906 };
5907
5908 /**
5909  * Removes <b>all</b> added captures from the Observable.
5910  * @param {Observable} o The Observable to release
5911  * @static
5912  */
5913 Roo.util.Observable.releaseCapture = function(o){
5914     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5915 };
5916
5917 (function(){
5918
5919     var createBuffered = function(h, o, scope){
5920         var task = new Roo.util.DelayedTask();
5921         return function(){
5922             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5923         };
5924     };
5925
5926     var createSingle = function(h, e, fn, scope){
5927         return function(){
5928             e.removeListener(fn, scope);
5929             return h.apply(scope, arguments);
5930         };
5931     };
5932
5933     var createDelayed = function(h, o, scope){
5934         return function(){
5935             var args = Array.prototype.slice.call(arguments, 0);
5936             setTimeout(function(){
5937                 h.apply(scope, args);
5938             }, o.delay || 10);
5939         };
5940     };
5941
5942     Roo.util.Event = function(obj, name){
5943         this.name = name;
5944         this.obj = obj;
5945         this.listeners = [];
5946     };
5947
5948     Roo.util.Event.prototype = {
5949         addListener : function(fn, scope, options){
5950             var o = options || {};
5951             scope = scope || this.obj;
5952             if(!this.isListening(fn, scope)){
5953                 var l = {fn: fn, scope: scope, options: o};
5954                 var h = fn;
5955                 if(o.delay){
5956                     h = createDelayed(h, o, scope);
5957                 }
5958                 if(o.single){
5959                     h = createSingle(h, this, fn, scope);
5960                 }
5961                 if(o.buffer){
5962                     h = createBuffered(h, o, scope);
5963                 }
5964                 l.fireFn = h;
5965                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5966                     this.listeners.push(l);
5967                 }else{
5968                     this.listeners = this.listeners.slice(0);
5969                     this.listeners.push(l);
5970                 }
5971             }
5972         },
5973
5974         findListener : function(fn, scope){
5975             scope = scope || this.obj;
5976             var ls = this.listeners;
5977             for(var i = 0, len = ls.length; i < len; i++){
5978                 var l = ls[i];
5979                 if(l.fn == fn && l.scope == scope){
5980                     return i;
5981                 }
5982             }
5983             return -1;
5984         },
5985
5986         isListening : function(fn, scope){
5987             return this.findListener(fn, scope) != -1;
5988         },
5989
5990         removeListener : function(fn, scope){
5991             var index;
5992             if((index = this.findListener(fn, scope)) != -1){
5993                 if(!this.firing){
5994                     this.listeners.splice(index, 1);
5995                 }else{
5996                     this.listeners = this.listeners.slice(0);
5997                     this.listeners.splice(index, 1);
5998                 }
5999                 return true;
6000             }
6001             return false;
6002         },
6003
6004         clearListeners : function(){
6005             this.listeners = [];
6006         },
6007
6008         fire : function(){
6009             var ls = this.listeners, scope, len = ls.length;
6010             if(len > 0){
6011                 this.firing = true;
6012                 var args = Array.prototype.slice.call(arguments, 0);
6013                 for(var i = 0; i < len; i++){
6014                     var l = ls[i];
6015                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6016                         this.firing = false;
6017                         return false;
6018                     }
6019                 }
6020                 this.firing = false;
6021             }
6022             return true;
6023         }
6024     };
6025 })();/*
6026  * Based on:
6027  * Ext JS Library 1.1.1
6028  * Copyright(c) 2006-2007, Ext JS, LLC.
6029  *
6030  * Originally Released Under LGPL - original licence link has changed is not relivant.
6031  *
6032  * Fork - LGPL
6033  * <script type="text/javascript">
6034  */
6035
6036 /**
6037  * @class Roo.EventManager
6038  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6039  * several useful events directly.
6040  * See {@link Roo.EventObject} for more details on normalized event objects.
6041  * @singleton
6042  */
6043 Roo.EventManager = function(){
6044     var docReadyEvent, docReadyProcId, docReadyState = false;
6045     var resizeEvent, resizeTask, textEvent, textSize;
6046     var E = Roo.lib.Event;
6047     var D = Roo.lib.Dom;
6048
6049
6050     var fireDocReady = function(){
6051         if(!docReadyState){
6052             docReadyState = true;
6053             Roo.isReady = true;
6054             if(docReadyProcId){
6055                 clearInterval(docReadyProcId);
6056             }
6057             if(Roo.isGecko || Roo.isOpera) {
6058                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6059             }
6060             if(Roo.isIE){
6061                 var defer = document.getElementById("ie-deferred-loader");
6062                 if(defer){
6063                     defer.onreadystatechange = null;
6064                     defer.parentNode.removeChild(defer);
6065                 }
6066             }
6067             if(docReadyEvent){
6068                 docReadyEvent.fire();
6069                 docReadyEvent.clearListeners();
6070             }
6071         }
6072     };
6073     
6074     var initDocReady = function(){
6075         docReadyEvent = new Roo.util.Event();
6076         if(Roo.isGecko || Roo.isOpera) {
6077             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6078         }else if(Roo.isIE){
6079             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6080             var defer = document.getElementById("ie-deferred-loader");
6081             defer.onreadystatechange = function(){
6082                 if(this.readyState == "complete"){
6083                     fireDocReady();
6084                 }
6085             };
6086         }else if(Roo.isSafari){ 
6087             docReadyProcId = setInterval(function(){
6088                 var rs = document.readyState;
6089                 if(rs == "complete") {
6090                     fireDocReady();     
6091                  }
6092             }, 10);
6093         }
6094         // no matter what, make sure it fires on load
6095         E.on(window, "load", fireDocReady);
6096     };
6097
6098     var createBuffered = function(h, o){
6099         var task = new Roo.util.DelayedTask(h);
6100         return function(e){
6101             // create new event object impl so new events don't wipe out properties
6102             e = new Roo.EventObjectImpl(e);
6103             task.delay(o.buffer, h, null, [e]);
6104         };
6105     };
6106
6107     var createSingle = function(h, el, ename, fn){
6108         return function(e){
6109             Roo.EventManager.removeListener(el, ename, fn);
6110             h(e);
6111         };
6112     };
6113
6114     var createDelayed = function(h, o){
6115         return function(e){
6116             // create new event object impl so new events don't wipe out properties
6117             e = new Roo.EventObjectImpl(e);
6118             setTimeout(function(){
6119                 h(e);
6120             }, o.delay || 10);
6121         };
6122     };
6123
6124     var listen = function(element, ename, opt, fn, scope){
6125         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6126         fn = fn || o.fn; scope = scope || o.scope;
6127         var el = Roo.getDom(element);
6128         if(!el){
6129             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6130         }
6131         var h = function(e){
6132             e = Roo.EventObject.setEvent(e);
6133             var t;
6134             if(o.delegate){
6135                 t = e.getTarget(o.delegate, el);
6136                 if(!t){
6137                     return;
6138                 }
6139             }else{
6140                 t = e.target;
6141             }
6142             if(o.stopEvent === true){
6143                 e.stopEvent();
6144             }
6145             if(o.preventDefault === true){
6146                e.preventDefault();
6147             }
6148             if(o.stopPropagation === true){
6149                 e.stopPropagation();
6150             }
6151
6152             if(o.normalized === false){
6153                 e = e.browserEvent;
6154             }
6155
6156             fn.call(scope || el, e, t, o);
6157         };
6158         if(o.delay){
6159             h = createDelayed(h, o);
6160         }
6161         if(o.single){
6162             h = createSingle(h, el, ename, fn);
6163         }
6164         if(o.buffer){
6165             h = createBuffered(h, o);
6166         }
6167         fn._handlers = fn._handlers || [];
6168         fn._handlers.push([Roo.id(el), ename, h]);
6169
6170         E.on(el, ename, h);
6171         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6172             el.addEventListener("DOMMouseScroll", h, false);
6173             E.on(window, 'unload', function(){
6174                 el.removeEventListener("DOMMouseScroll", h, false);
6175             });
6176         }
6177         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6178             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6179         }
6180         return h;
6181     };
6182
6183     var stopListening = function(el, ename, fn){
6184         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6185         if(hds){
6186             for(var i = 0, len = hds.length; i < len; i++){
6187                 var h = hds[i];
6188                 if(h[0] == id && h[1] == ename){
6189                     hd = h[2];
6190                     hds.splice(i, 1);
6191                     break;
6192                 }
6193             }
6194         }
6195         E.un(el, ename, hd);
6196         el = Roo.getDom(el);
6197         if(ename == "mousewheel" && el.addEventListener){
6198             el.removeEventListener("DOMMouseScroll", hd, false);
6199         }
6200         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6201             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6202         }
6203     };
6204
6205     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6206     
6207     var pub = {
6208         
6209         
6210         /** 
6211          * Fix for doc tools
6212          * @scope Roo.EventManager
6213          */
6214         
6215         
6216         /** 
6217          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6218          * object with a Roo.EventObject
6219          * @param {Function} fn        The method the event invokes
6220          * @param {Object}   scope    An object that becomes the scope of the handler
6221          * @param {boolean}  override If true, the obj passed in becomes
6222          *                             the execution scope of the listener
6223          * @return {Function} The wrapped function
6224          * @deprecated
6225          */
6226         wrap : function(fn, scope, override){
6227             return function(e){
6228                 Roo.EventObject.setEvent(e);
6229                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6230             };
6231         },
6232         
6233         /**
6234      * Appends an event handler to an element (shorthand for addListener)
6235      * @param {String/HTMLElement}   element        The html element or id to assign the
6236      * @param {String}   eventName The type of event to listen for
6237      * @param {Function} handler The method the event invokes
6238      * @param {Object}   scope (optional) The scope in which to execute the handler
6239      * function. The handler function's "this" context.
6240      * @param {Object}   options (optional) An object containing handler configuration
6241      * properties. This may contain any of the following properties:<ul>
6242      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6243      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6244      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6245      * <li>preventDefault {Boolean} True to prevent the default action</li>
6246      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6247      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6248      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6249      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6250      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6251      * by the specified number of milliseconds. If the event fires again within that time, the original
6252      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6253      * </ul><br>
6254      * <p>
6255      * <b>Combining Options</b><br>
6256      * Using the options argument, it is possible to combine different types of listeners:<br>
6257      * <br>
6258      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6259      * Code:<pre><code>
6260 el.on('click', this.onClick, this, {
6261     single: true,
6262     delay: 100,
6263     stopEvent : true,
6264     forumId: 4
6265 });</code></pre>
6266      * <p>
6267      * <b>Attaching multiple handlers in 1 call</b><br>
6268       * The method also allows for a single argument to be passed which is a config object containing properties
6269      * which specify multiple handlers.
6270      * <p>
6271      * Code:<pre><code>
6272 el.on({
6273     'click' : {
6274         fn: this.onClick
6275         scope: this,
6276         delay: 100
6277     },
6278     'mouseover' : {
6279         fn: this.onMouseOver
6280         scope: this
6281     },
6282     'mouseout' : {
6283         fn: this.onMouseOut
6284         scope: this
6285     }
6286 });</code></pre>
6287      * <p>
6288      * Or a shorthand syntax:<br>
6289      * Code:<pre><code>
6290 el.on({
6291     'click' : this.onClick,
6292     'mouseover' : this.onMouseOver,
6293     'mouseout' : this.onMouseOut
6294     scope: this
6295 });</code></pre>
6296      */
6297         addListener : function(element, eventName, fn, scope, options){
6298             if(typeof eventName == "object"){
6299                 var o = eventName;
6300                 for(var e in o){
6301                     if(propRe.test(e)){
6302                         continue;
6303                     }
6304                     if(typeof o[e] == "function"){
6305                         // shared options
6306                         listen(element, e, o, o[e], o.scope);
6307                     }else{
6308                         // individual options
6309                         listen(element, e, o[e]);
6310                     }
6311                 }
6312                 return;
6313             }
6314             return listen(element, eventName, options, fn, scope);
6315         },
6316         
6317         /**
6318          * Removes an event handler
6319          *
6320          * @param {String/HTMLElement}   element        The id or html element to remove the 
6321          *                             event from
6322          * @param {String}   eventName     The type of event
6323          * @param {Function} fn
6324          * @return {Boolean} True if a listener was actually removed
6325          */
6326         removeListener : function(element, eventName, fn){
6327             return stopListening(element, eventName, fn);
6328         },
6329         
6330         /**
6331          * Fires when the document is ready (before onload and before images are loaded). Can be 
6332          * accessed shorthanded Roo.onReady().
6333          * @param {Function} fn        The method the event invokes
6334          * @param {Object}   scope    An  object that becomes the scope of the handler
6335          * @param {boolean}  options
6336          */
6337         onDocumentReady : function(fn, scope, options){
6338             if(docReadyState){ // if it already fired
6339                 docReadyEvent.addListener(fn, scope, options);
6340                 docReadyEvent.fire();
6341                 docReadyEvent.clearListeners();
6342                 return;
6343             }
6344             if(!docReadyEvent){
6345                 initDocReady();
6346             }
6347             docReadyEvent.addListener(fn, scope, options);
6348         },
6349         
6350         /**
6351          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6352          * @param {Function} fn        The method the event invokes
6353          * @param {Object}   scope    An object that becomes the scope of the handler
6354          * @param {boolean}  options
6355          */
6356         onWindowResize : function(fn, scope, options){
6357             if(!resizeEvent){
6358                 resizeEvent = new Roo.util.Event();
6359                 resizeTask = new Roo.util.DelayedTask(function(){
6360                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6361                 });
6362                 E.on(window, "resize", function(){
6363                     if(Roo.isIE){
6364                         resizeTask.delay(50);
6365                     }else{
6366                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6367                     }
6368                 });
6369             }
6370             resizeEvent.addListener(fn, scope, options);
6371         },
6372
6373         /**
6374          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6375          * @param {Function} fn        The method the event invokes
6376          * @param {Object}   scope    An object that becomes the scope of the handler
6377          * @param {boolean}  options
6378          */
6379         onTextResize : function(fn, scope, options){
6380             if(!textEvent){
6381                 textEvent = new Roo.util.Event();
6382                 var textEl = new Roo.Element(document.createElement('div'));
6383                 textEl.dom.className = 'x-text-resize';
6384                 textEl.dom.innerHTML = 'X';
6385                 textEl.appendTo(document.body);
6386                 textSize = textEl.dom.offsetHeight;
6387                 setInterval(function(){
6388                     if(textEl.dom.offsetHeight != textSize){
6389                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6390                     }
6391                 }, this.textResizeInterval);
6392             }
6393             textEvent.addListener(fn, scope, options);
6394         },
6395
6396         /**
6397          * Removes the passed window resize listener.
6398          * @param {Function} fn        The method the event invokes
6399          * @param {Object}   scope    The scope of handler
6400          */
6401         removeResizeListener : function(fn, scope){
6402             if(resizeEvent){
6403                 resizeEvent.removeListener(fn, scope);
6404             }
6405         },
6406
6407         // private
6408         fireResize : function(){
6409             if(resizeEvent){
6410                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6411             }   
6412         },
6413         /**
6414          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6415          */
6416         ieDeferSrc : false,
6417         /**
6418          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6419          */
6420         textResizeInterval : 50
6421     };
6422     
6423     /**
6424      * Fix for doc tools
6425      * @scopeAlias pub=Roo.EventManager
6426      */
6427     
6428      /**
6429      * Appends an event handler to an element (shorthand for addListener)
6430      * @param {String/HTMLElement}   element        The html element or id to assign the
6431      * @param {String}   eventName The type of event to listen for
6432      * @param {Function} handler The method the event invokes
6433      * @param {Object}   scope (optional) The scope in which to execute the handler
6434      * function. The handler function's "this" context.
6435      * @param {Object}   options (optional) An object containing handler configuration
6436      * properties. This may contain any of the following properties:<ul>
6437      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6438      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6439      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6440      * <li>preventDefault {Boolean} True to prevent the default action</li>
6441      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6442      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6443      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6444      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6445      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6446      * by the specified number of milliseconds. If the event fires again within that time, the original
6447      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6448      * </ul><br>
6449      * <p>
6450      * <b>Combining Options</b><br>
6451      * Using the options argument, it is possible to combine different types of listeners:<br>
6452      * <br>
6453      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6454      * Code:<pre><code>
6455 el.on('click', this.onClick, this, {
6456     single: true,
6457     delay: 100,
6458     stopEvent : true,
6459     forumId: 4
6460 });</code></pre>
6461      * <p>
6462      * <b>Attaching multiple handlers in 1 call</b><br>
6463       * The method also allows for a single argument to be passed which is a config object containing properties
6464      * which specify multiple handlers.
6465      * <p>
6466      * Code:<pre><code>
6467 el.on({
6468     'click' : {
6469         fn: this.onClick
6470         scope: this,
6471         delay: 100
6472     },
6473     'mouseover' : {
6474         fn: this.onMouseOver
6475         scope: this
6476     },
6477     'mouseout' : {
6478         fn: this.onMouseOut
6479         scope: this
6480     }
6481 });</code></pre>
6482      * <p>
6483      * Or a shorthand syntax:<br>
6484      * Code:<pre><code>
6485 el.on({
6486     'click' : this.onClick,
6487     'mouseover' : this.onMouseOver,
6488     'mouseout' : this.onMouseOut
6489     scope: this
6490 });</code></pre>
6491      */
6492     pub.on = pub.addListener;
6493     pub.un = pub.removeListener;
6494
6495     pub.stoppedMouseDownEvent = new Roo.util.Event();
6496     return pub;
6497 }();
6498 /**
6499   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6500   * @param {Function} fn        The method the event invokes
6501   * @param {Object}   scope    An  object that becomes the scope of the handler
6502   * @param {boolean}  override If true, the obj passed in becomes
6503   *                             the execution scope of the listener
6504   * @member Roo
6505   * @method onReady
6506  */
6507 Roo.onReady = Roo.EventManager.onDocumentReady;
6508
6509 Roo.onReady(function(){
6510     var bd = Roo.get(document.body);
6511     if(!bd){ return; }
6512
6513     var cls = [
6514             Roo.isIE ? "roo-ie"
6515             : Roo.isGecko ? "roo-gecko"
6516             : Roo.isOpera ? "roo-opera"
6517             : Roo.isSafari ? "roo-safari" : ""];
6518
6519     if(Roo.isMac){
6520         cls.push("roo-mac");
6521     }
6522     if(Roo.isLinux){
6523         cls.push("roo-linux");
6524     }
6525     if(Roo.isBorderBox){
6526         cls.push('roo-border-box');
6527     }
6528     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6529         var p = bd.dom.parentNode;
6530         if(p){
6531             p.className += ' roo-strict';
6532         }
6533     }
6534     bd.addClass(cls.join(' '));
6535 });
6536
6537 /**
6538  * @class Roo.EventObject
6539  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6540  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6541  * Example:
6542  * <pre><code>
6543  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6544     e.preventDefault();
6545     var target = e.getTarget();
6546     ...
6547  }
6548  var myDiv = Roo.get("myDiv");
6549  myDiv.on("click", handleClick);
6550  //or
6551  Roo.EventManager.on("myDiv", 'click', handleClick);
6552  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6553  </code></pre>
6554  * @singleton
6555  */
6556 Roo.EventObject = function(){
6557     
6558     var E = Roo.lib.Event;
6559     
6560     // safari keypress events for special keys return bad keycodes
6561     var safariKeys = {
6562         63234 : 37, // left
6563         63235 : 39, // right
6564         63232 : 38, // up
6565         63233 : 40, // down
6566         63276 : 33, // page up
6567         63277 : 34, // page down
6568         63272 : 46, // delete
6569         63273 : 36, // home
6570         63275 : 35  // end
6571     };
6572
6573     // normalize button clicks
6574     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6575                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6576
6577     Roo.EventObjectImpl = function(e){
6578         if(e){
6579             this.setEvent(e.browserEvent || e);
6580         }
6581     };
6582     Roo.EventObjectImpl.prototype = {
6583         /**
6584          * Used to fix doc tools.
6585          * @scope Roo.EventObject.prototype
6586          */
6587             
6588
6589         
6590         
6591         /** The normal browser event */
6592         browserEvent : null,
6593         /** The button pressed in a mouse event */
6594         button : -1,
6595         /** True if the shift key was down during the event */
6596         shiftKey : false,
6597         /** True if the control key was down during the event */
6598         ctrlKey : false,
6599         /** True if the alt key was down during the event */
6600         altKey : false,
6601
6602         /** Key constant 
6603         * @type Number */
6604         BACKSPACE : 8,
6605         /** Key constant 
6606         * @type Number */
6607         TAB : 9,
6608         /** Key constant 
6609         * @type Number */
6610         RETURN : 13,
6611         /** Key constant 
6612         * @type Number */
6613         ENTER : 13,
6614         /** Key constant 
6615         * @type Number */
6616         SHIFT : 16,
6617         /** Key constant 
6618         * @type Number */
6619         CONTROL : 17,
6620         /** Key constant 
6621         * @type Number */
6622         ESC : 27,
6623         /** Key constant 
6624         * @type Number */
6625         SPACE : 32,
6626         /** Key constant 
6627         * @type Number */
6628         PAGEUP : 33,
6629         /** Key constant 
6630         * @type Number */
6631         PAGEDOWN : 34,
6632         /** Key constant 
6633         * @type Number */
6634         END : 35,
6635         /** Key constant 
6636         * @type Number */
6637         HOME : 36,
6638         /** Key constant 
6639         * @type Number */
6640         LEFT : 37,
6641         /** Key constant 
6642         * @type Number */
6643         UP : 38,
6644         /** Key constant 
6645         * @type Number */
6646         RIGHT : 39,
6647         /** Key constant 
6648         * @type Number */
6649         DOWN : 40,
6650         /** Key constant 
6651         * @type Number */
6652         DELETE : 46,
6653         /** Key constant 
6654         * @type Number */
6655         F5 : 116,
6656
6657            /** @private */
6658         setEvent : function(e){
6659             if(e == this || (e && e.browserEvent)){ // already wrapped
6660                 return e;
6661             }
6662             this.browserEvent = e;
6663             if(e){
6664                 // normalize buttons
6665                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6666                 if(e.type == 'click' && this.button == -1){
6667                     this.button = 0;
6668                 }
6669                 this.type = e.type;
6670                 this.shiftKey = e.shiftKey;
6671                 // mac metaKey behaves like ctrlKey
6672                 this.ctrlKey = e.ctrlKey || e.metaKey;
6673                 this.altKey = e.altKey;
6674                 // in getKey these will be normalized for the mac
6675                 this.keyCode = e.keyCode;
6676                 // keyup warnings on firefox.
6677                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6678                 // cache the target for the delayed and or buffered events
6679                 this.target = E.getTarget(e);
6680                 // same for XY
6681                 this.xy = E.getXY(e);
6682             }else{
6683                 this.button = -1;
6684                 this.shiftKey = false;
6685                 this.ctrlKey = false;
6686                 this.altKey = false;
6687                 this.keyCode = 0;
6688                 this.charCode =0;
6689                 this.target = null;
6690                 this.xy = [0, 0];
6691             }
6692             return this;
6693         },
6694
6695         /**
6696          * Stop the event (preventDefault and stopPropagation)
6697          */
6698         stopEvent : function(){
6699             if(this.browserEvent){
6700                 if(this.browserEvent.type == 'mousedown'){
6701                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6702                 }
6703                 E.stopEvent(this.browserEvent);
6704             }
6705         },
6706
6707         /**
6708          * Prevents the browsers default handling of the event.
6709          */
6710         preventDefault : function(){
6711             if(this.browserEvent){
6712                 E.preventDefault(this.browserEvent);
6713             }
6714         },
6715
6716         /** @private */
6717         isNavKeyPress : function(){
6718             var k = this.keyCode;
6719             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6720             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6721         },
6722
6723         isSpecialKey : function(){
6724             var k = this.keyCode;
6725             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6726             (k == 16) || (k == 17) ||
6727             (k >= 18 && k <= 20) ||
6728             (k >= 33 && k <= 35) ||
6729             (k >= 36 && k <= 39) ||
6730             (k >= 44 && k <= 45);
6731         },
6732         /**
6733          * Cancels bubbling of the event.
6734          */
6735         stopPropagation : function(){
6736             if(this.browserEvent){
6737                 if(this.type == 'mousedown'){
6738                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6739                 }
6740                 E.stopPropagation(this.browserEvent);
6741             }
6742         },
6743
6744         /**
6745          * Gets the key code for the event.
6746          * @return {Number}
6747          */
6748         getCharCode : function(){
6749             return this.charCode || this.keyCode;
6750         },
6751
6752         /**
6753          * Returns a normalized keyCode for the event.
6754          * @return {Number} The key code
6755          */
6756         getKey : function(){
6757             var k = this.keyCode || this.charCode;
6758             return Roo.isSafari ? (safariKeys[k] || k) : k;
6759         },
6760
6761         /**
6762          * Gets the x coordinate of the event.
6763          * @return {Number}
6764          */
6765         getPageX : function(){
6766             return this.xy[0];
6767         },
6768
6769         /**
6770          * Gets the y coordinate of the event.
6771          * @return {Number}
6772          */
6773         getPageY : function(){
6774             return this.xy[1];
6775         },
6776
6777         /**
6778          * Gets the time of the event.
6779          * @return {Number}
6780          */
6781         getTime : function(){
6782             if(this.browserEvent){
6783                 return E.getTime(this.browserEvent);
6784             }
6785             return null;
6786         },
6787
6788         /**
6789          * Gets the page coordinates of the event.
6790          * @return {Array} The xy values like [x, y]
6791          */
6792         getXY : function(){
6793             return this.xy;
6794         },
6795
6796         /**
6797          * Gets the target for the event.
6798          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6799          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6800                 search as a number or element (defaults to 10 || document.body)
6801          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6802          * @return {HTMLelement}
6803          */
6804         getTarget : function(selector, maxDepth, returnEl){
6805             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6806         },
6807         /**
6808          * Gets the related target.
6809          * @return {HTMLElement}
6810          */
6811         getRelatedTarget : function(){
6812             if(this.browserEvent){
6813                 return E.getRelatedTarget(this.browserEvent);
6814             }
6815             return null;
6816         },
6817
6818         /**
6819          * Normalizes mouse wheel delta across browsers
6820          * @return {Number} The delta
6821          */
6822         getWheelDelta : function(){
6823             var e = this.browserEvent;
6824             var delta = 0;
6825             if(e.wheelDelta){ /* IE/Opera. */
6826                 delta = e.wheelDelta/120;
6827             }else if(e.detail){ /* Mozilla case. */
6828                 delta = -e.detail/3;
6829             }
6830             return delta;
6831         },
6832
6833         /**
6834          * Returns true if the control, meta, shift or alt key was pressed during this event.
6835          * @return {Boolean}
6836          */
6837         hasModifier : function(){
6838             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6839         },
6840
6841         /**
6842          * Returns true if the target of this event equals el or is a child of el
6843          * @param {String/HTMLElement/Element} el
6844          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6845          * @return {Boolean}
6846          */
6847         within : function(el, related){
6848             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6849             return t && Roo.fly(el).contains(t);
6850         },
6851
6852         getPoint : function(){
6853             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6854         }
6855     };
6856
6857     return new Roo.EventObjectImpl();
6858 }();
6859             
6860     /*
6861  * Based on:
6862  * Ext JS Library 1.1.1
6863  * Copyright(c) 2006-2007, Ext JS, LLC.
6864  *
6865  * Originally Released Under LGPL - original licence link has changed is not relivant.
6866  *
6867  * Fork - LGPL
6868  * <script type="text/javascript">
6869  */
6870
6871  
6872 // was in Composite Element!??!?!
6873  
6874 (function(){
6875     var D = Roo.lib.Dom;
6876     var E = Roo.lib.Event;
6877     var A = Roo.lib.Anim;
6878
6879     // local style camelizing for speed
6880     var propCache = {};
6881     var camelRe = /(-[a-z])/gi;
6882     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6883     var view = document.defaultView;
6884
6885 /**
6886  * @class Roo.Element
6887  * Represents an Element in the DOM.<br><br>
6888  * Usage:<br>
6889 <pre><code>
6890 var el = Roo.get("my-div");
6891
6892 // or with getEl
6893 var el = getEl("my-div");
6894
6895 // or with a DOM element
6896 var el = Roo.get(myDivElement);
6897 </code></pre>
6898  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6899  * each call instead of constructing a new one.<br><br>
6900  * <b>Animations</b><br />
6901  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6902  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6903 <pre>
6904 Option    Default   Description
6905 --------- --------  ---------------------------------------------
6906 duration  .35       The duration of the animation in seconds
6907 easing    easeOut   The YUI easing method
6908 callback  none      A function to execute when the anim completes
6909 scope     this      The scope (this) of the callback function
6910 </pre>
6911 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6912 * manipulate the animation. Here's an example:
6913 <pre><code>
6914 var el = Roo.get("my-div");
6915
6916 // no animation
6917 el.setWidth(100);
6918
6919 // default animation
6920 el.setWidth(100, true);
6921
6922 // animation with some options set
6923 el.setWidth(100, {
6924     duration: 1,
6925     callback: this.foo,
6926     scope: this
6927 });
6928
6929 // using the "anim" property to get the Anim object
6930 var opt = {
6931     duration: 1,
6932     callback: this.foo,
6933     scope: this
6934 };
6935 el.setWidth(100, opt);
6936 ...
6937 if(opt.anim.isAnimated()){
6938     opt.anim.stop();
6939 }
6940 </code></pre>
6941 * <b> Composite (Collections of) Elements</b><br />
6942  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6943  * @constructor Create a new Element directly.
6944  * @param {String/HTMLElement} element
6945  * @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).
6946  */
6947     Roo.Element = function(element, forceNew){
6948         var dom = typeof element == "string" ?
6949                 document.getElementById(element) : element;
6950         if(!dom){ // invalid id/element
6951             return null;
6952         }
6953         var id = dom.id;
6954         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6955             return Roo.Element.cache[id];
6956         }
6957
6958         /**
6959          * The DOM element
6960          * @type HTMLElement
6961          */
6962         this.dom = dom;
6963
6964         /**
6965          * The DOM element ID
6966          * @type String
6967          */
6968         this.id = id || Roo.id(dom);
6969     };
6970
6971     var El = Roo.Element;
6972
6973     El.prototype = {
6974         /**
6975          * The element's default display mode  (defaults to "")
6976          * @type String
6977          */
6978         originalDisplay : "",
6979
6980         visibilityMode : 1,
6981         /**
6982          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6983          * @type String
6984          */
6985         defaultUnit : "px",
6986         /**
6987          * Sets the element's visibility mode. When setVisible() is called it
6988          * will use this to determine whether to set the visibility or the display property.
6989          * @param visMode Element.VISIBILITY or Element.DISPLAY
6990          * @return {Roo.Element} this
6991          */
6992         setVisibilityMode : function(visMode){
6993             this.visibilityMode = visMode;
6994             return this;
6995         },
6996         /**
6997          * Convenience method for setVisibilityMode(Element.DISPLAY)
6998          * @param {String} display (optional) What to set display to when visible
6999          * @return {Roo.Element} this
7000          */
7001         enableDisplayMode : function(display){
7002             this.setVisibilityMode(El.DISPLAY);
7003             if(typeof display != "undefined") this.originalDisplay = display;
7004             return this;
7005         },
7006
7007         /**
7008          * 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)
7009          * @param {String} selector The simple selector to test
7010          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7011                 search as a number or element (defaults to 10 || document.body)
7012          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7013          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7014          */
7015         findParent : function(simpleSelector, maxDepth, returnEl){
7016             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7017             maxDepth = maxDepth || 50;
7018             if(typeof maxDepth != "number"){
7019                 stopEl = Roo.getDom(maxDepth);
7020                 maxDepth = 10;
7021             }
7022             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7023                 if(dq.is(p, simpleSelector)){
7024                     return returnEl ? Roo.get(p) : p;
7025                 }
7026                 depth++;
7027                 p = p.parentNode;
7028             }
7029             return null;
7030         },
7031
7032
7033         /**
7034          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7035          * @param {String} selector The simple selector to test
7036          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7037                 search as a number or element (defaults to 10 || document.body)
7038          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7039          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7040          */
7041         findParentNode : function(simpleSelector, maxDepth, returnEl){
7042             var p = Roo.fly(this.dom.parentNode, '_internal');
7043             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7044         },
7045
7046         /**
7047          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7048          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7049          * @param {String} selector The simple selector to test
7050          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7051                 search as a number or element (defaults to 10 || document.body)
7052          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7053          */
7054         up : function(simpleSelector, maxDepth){
7055             return this.findParentNode(simpleSelector, maxDepth, true);
7056         },
7057
7058
7059
7060         /**
7061          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7062          * @param {String} selector The simple selector to test
7063          * @return {Boolean} True if this element matches the selector, else false
7064          */
7065         is : function(simpleSelector){
7066             return Roo.DomQuery.is(this.dom, simpleSelector);
7067         },
7068
7069         /**
7070          * Perform animation on this element.
7071          * @param {Object} args The YUI animation control args
7072          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7073          * @param {Function} onComplete (optional) Function to call when animation completes
7074          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7075          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7076          * @return {Roo.Element} this
7077          */
7078         animate : function(args, duration, onComplete, easing, animType){
7079             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7080             return this;
7081         },
7082
7083         /*
7084          * @private Internal animation call
7085          */
7086         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7087             animType = animType || 'run';
7088             opt = opt || {};
7089             var anim = Roo.lib.Anim[animType](
7090                 this.dom, args,
7091                 (opt.duration || defaultDur) || .35,
7092                 (opt.easing || defaultEase) || 'easeOut',
7093                 function(){
7094                     Roo.callback(cb, this);
7095                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7096                 },
7097                 this
7098             );
7099             opt.anim = anim;
7100             return anim;
7101         },
7102
7103         // private legacy anim prep
7104         preanim : function(a, i){
7105             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7106         },
7107
7108         /**
7109          * Removes worthless text nodes
7110          * @param {Boolean} forceReclean (optional) By default the element
7111          * keeps track if it has been cleaned already so
7112          * you can call this over and over. However, if you update the element and
7113          * need to force a reclean, you can pass true.
7114          */
7115         clean : function(forceReclean){
7116             if(this.isCleaned && forceReclean !== true){
7117                 return this;
7118             }
7119             var ns = /\S/;
7120             var d = this.dom, n = d.firstChild, ni = -1;
7121             while(n){
7122                 var nx = n.nextSibling;
7123                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7124                     d.removeChild(n);
7125                 }else{
7126                     n.nodeIndex = ++ni;
7127                 }
7128                 n = nx;
7129             }
7130             this.isCleaned = true;
7131             return this;
7132         },
7133
7134         // private
7135         calcOffsetsTo : function(el){
7136             el = Roo.get(el);
7137             var d = el.dom;
7138             var restorePos = false;
7139             if(el.getStyle('position') == 'static'){
7140                 el.position('relative');
7141                 restorePos = true;
7142             }
7143             var x = 0, y =0;
7144             var op = this.dom;
7145             while(op && op != d && op.tagName != 'HTML'){
7146                 x+= op.offsetLeft;
7147                 y+= op.offsetTop;
7148                 op = op.offsetParent;
7149             }
7150             if(restorePos){
7151                 el.position('static');
7152             }
7153             return [x, y];
7154         },
7155
7156         /**
7157          * Scrolls this element into view within the passed container.
7158          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7159          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7160          * @return {Roo.Element} this
7161          */
7162         scrollIntoView : function(container, hscroll){
7163             var c = Roo.getDom(container) || document.body;
7164             var el = this.dom;
7165
7166             var o = this.calcOffsetsTo(c),
7167                 l = o[0],
7168                 t = o[1],
7169                 b = t+el.offsetHeight,
7170                 r = l+el.offsetWidth;
7171
7172             var ch = c.clientHeight;
7173             var ct = parseInt(c.scrollTop, 10);
7174             var cl = parseInt(c.scrollLeft, 10);
7175             var cb = ct + ch;
7176             var cr = cl + c.clientWidth;
7177
7178             if(t < ct){
7179                 c.scrollTop = t;
7180             }else if(b > cb){
7181                 c.scrollTop = b-ch;
7182             }
7183
7184             if(hscroll !== false){
7185                 if(l < cl){
7186                     c.scrollLeft = l;
7187                 }else if(r > cr){
7188                     c.scrollLeft = r-c.clientWidth;
7189                 }
7190             }
7191             return this;
7192         },
7193
7194         // private
7195         scrollChildIntoView : function(child, hscroll){
7196             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7197         },
7198
7199         /**
7200          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7201          * the new height may not be available immediately.
7202          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7203          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7204          * @param {Function} onComplete (optional) Function to call when animation completes
7205          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7206          * @return {Roo.Element} this
7207          */
7208         autoHeight : function(animate, duration, onComplete, easing){
7209             var oldHeight = this.getHeight();
7210             this.clip();
7211             this.setHeight(1); // force clipping
7212             setTimeout(function(){
7213                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7214                 if(!animate){
7215                     this.setHeight(height);
7216                     this.unclip();
7217                     if(typeof onComplete == "function"){
7218                         onComplete();
7219                     }
7220                 }else{
7221                     this.setHeight(oldHeight); // restore original height
7222                     this.setHeight(height, animate, duration, function(){
7223                         this.unclip();
7224                         if(typeof onComplete == "function") onComplete();
7225                     }.createDelegate(this), easing);
7226                 }
7227             }.createDelegate(this), 0);
7228             return this;
7229         },
7230
7231         /**
7232          * Returns true if this element is an ancestor of the passed element
7233          * @param {HTMLElement/String} el The element to check
7234          * @return {Boolean} True if this element is an ancestor of el, else false
7235          */
7236         contains : function(el){
7237             if(!el){return false;}
7238             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7239         },
7240
7241         /**
7242          * Checks whether the element is currently visible using both visibility and display properties.
7243          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7244          * @return {Boolean} True if the element is currently visible, else false
7245          */
7246         isVisible : function(deep) {
7247             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7248             if(deep !== true || !vis){
7249                 return vis;
7250             }
7251             var p = this.dom.parentNode;
7252             while(p && p.tagName.toLowerCase() != "body"){
7253                 if(!Roo.fly(p, '_isVisible').isVisible()){
7254                     return false;
7255                 }
7256                 p = p.parentNode;
7257             }
7258             return true;
7259         },
7260
7261         /**
7262          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7263          * @param {String} selector The CSS selector
7264          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7265          * @return {CompositeElement/CompositeElementLite} The composite element
7266          */
7267         select : function(selector, unique){
7268             return El.select(selector, unique, this.dom);
7269         },
7270
7271         /**
7272          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7273          * @param {String} selector The CSS selector
7274          * @return {Array} An array of the matched nodes
7275          */
7276         query : function(selector, unique){
7277             return Roo.DomQuery.select(selector, this.dom);
7278         },
7279
7280         /**
7281          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7282          * @param {String} selector The CSS selector
7283          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7284          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7285          */
7286         child : function(selector, returnDom){
7287             var n = Roo.DomQuery.selectNode(selector, this.dom);
7288             return returnDom ? n : Roo.get(n);
7289         },
7290
7291         /**
7292          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7293          * @param {String} selector The CSS selector
7294          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7295          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7296          */
7297         down : function(selector, returnDom){
7298             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7299             return returnDom ? n : Roo.get(n);
7300         },
7301
7302         /**
7303          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7304          * @param {String} group The group the DD object is member of
7305          * @param {Object} config The DD config object
7306          * @param {Object} overrides An object containing methods to override/implement on the DD object
7307          * @return {Roo.dd.DD} The DD object
7308          */
7309         initDD : function(group, config, overrides){
7310             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7311             return Roo.apply(dd, overrides);
7312         },
7313
7314         /**
7315          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7316          * @param {String} group The group the DDProxy object is member of
7317          * @param {Object} config The DDProxy config object
7318          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7319          * @return {Roo.dd.DDProxy} The DDProxy object
7320          */
7321         initDDProxy : function(group, config, overrides){
7322             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7323             return Roo.apply(dd, overrides);
7324         },
7325
7326         /**
7327          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7328          * @param {String} group The group the DDTarget object is member of
7329          * @param {Object} config The DDTarget config object
7330          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7331          * @return {Roo.dd.DDTarget} The DDTarget object
7332          */
7333         initDDTarget : function(group, config, overrides){
7334             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7335             return Roo.apply(dd, overrides);
7336         },
7337
7338         /**
7339          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7340          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7341          * @param {Boolean} visible Whether the element is visible
7342          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7343          * @return {Roo.Element} this
7344          */
7345          setVisible : function(visible, animate){
7346             if(!animate || !A){
7347                 if(this.visibilityMode == El.DISPLAY){
7348                     this.setDisplayed(visible);
7349                 }else{
7350                     this.fixDisplay();
7351                     this.dom.style.visibility = visible ? "visible" : "hidden";
7352                 }
7353             }else{
7354                 // closure for composites
7355                 var dom = this.dom;
7356                 var visMode = this.visibilityMode;
7357                 if(visible){
7358                     this.setOpacity(.01);
7359                     this.setVisible(true);
7360                 }
7361                 this.anim({opacity: { to: (visible?1:0) }},
7362                       this.preanim(arguments, 1),
7363                       null, .35, 'easeIn', function(){
7364                          if(!visible){
7365                              if(visMode == El.DISPLAY){
7366                                  dom.style.display = "none";
7367                              }else{
7368                                  dom.style.visibility = "hidden";
7369                              }
7370                              Roo.get(dom).setOpacity(1);
7371                          }
7372                      });
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Returns true if display is not "none"
7379          * @return {Boolean}
7380          */
7381         isDisplayed : function() {
7382             return this.getStyle("display") != "none";
7383         },
7384
7385         /**
7386          * Toggles the element's visibility or display, depending on visibility mode.
7387          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7388          * @return {Roo.Element} this
7389          */
7390         toggle : function(animate){
7391             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7392             return this;
7393         },
7394
7395         /**
7396          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7397          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7398          * @return {Roo.Element} this
7399          */
7400         setDisplayed : function(value) {
7401             if(typeof value == "boolean"){
7402                value = value ? this.originalDisplay : "none";
7403             }
7404             this.setStyle("display", value);
7405             return this;
7406         },
7407
7408         /**
7409          * Tries to focus the element. Any exceptions are caught and ignored.
7410          * @return {Roo.Element} this
7411          */
7412         focus : function() {
7413             try{
7414                 this.dom.focus();
7415             }catch(e){}
7416             return this;
7417         },
7418
7419         /**
7420          * Tries to blur the element. Any exceptions are caught and ignored.
7421          * @return {Roo.Element} this
7422          */
7423         blur : function() {
7424             try{
7425                 this.dom.blur();
7426             }catch(e){}
7427             return this;
7428         },
7429
7430         /**
7431          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7432          * @param {String/Array} className The CSS class to add, or an array of classes
7433          * @return {Roo.Element} this
7434          */
7435         addClass : function(className){
7436             if(className instanceof Array){
7437                 for(var i = 0, len = className.length; i < len; i++) {
7438                     this.addClass(className[i]);
7439                 }
7440             }else{
7441                 if(className && !this.hasClass(className)){
7442                     this.dom.className = this.dom.className + " " + className;
7443                 }
7444             }
7445             return this;
7446         },
7447
7448         /**
7449          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7450          * @param {String/Array} className The CSS class to add, or an array of classes
7451          * @return {Roo.Element} this
7452          */
7453         radioClass : function(className){
7454             var siblings = this.dom.parentNode.childNodes;
7455             for(var i = 0; i < siblings.length; i++) {
7456                 var s = siblings[i];
7457                 if(s.nodeType == 1){
7458                     Roo.get(s).removeClass(className);
7459                 }
7460             }
7461             this.addClass(className);
7462             return this;
7463         },
7464
7465         /**
7466          * Removes one or more CSS classes from the element.
7467          * @param {String/Array} className The CSS class to remove, or an array of classes
7468          * @return {Roo.Element} this
7469          */
7470         removeClass : function(className){
7471             if(!className || !this.dom.className){
7472                 return this;
7473             }
7474             if(className instanceof Array){
7475                 for(var i = 0, len = className.length; i < len; i++) {
7476                     this.removeClass(className[i]);
7477                 }
7478             }else{
7479                 if(this.hasClass(className)){
7480                     var re = this.classReCache[className];
7481                     if (!re) {
7482                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7483                        this.classReCache[className] = re;
7484                     }
7485                     this.dom.className =
7486                         this.dom.className.replace(re, " ");
7487                 }
7488             }
7489             return this;
7490         },
7491
7492         // private
7493         classReCache: {},
7494
7495         /**
7496          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7497          * @param {String} className The CSS class to toggle
7498          * @return {Roo.Element} this
7499          */
7500         toggleClass : function(className){
7501             if(this.hasClass(className)){
7502                 this.removeClass(className);
7503             }else{
7504                 this.addClass(className);
7505             }
7506             return this;
7507         },
7508
7509         /**
7510          * Checks if the specified CSS class exists on this element's DOM node.
7511          * @param {String} className The CSS class to check for
7512          * @return {Boolean} True if the class exists, else false
7513          */
7514         hasClass : function(className){
7515             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7516         },
7517
7518         /**
7519          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7520          * @param {String} oldClassName The CSS class to replace
7521          * @param {String} newClassName The replacement CSS class
7522          * @return {Roo.Element} this
7523          */
7524         replaceClass : function(oldClassName, newClassName){
7525             this.removeClass(oldClassName);
7526             this.addClass(newClassName);
7527             return this;
7528         },
7529
7530         /**
7531          * Returns an object with properties matching the styles requested.
7532          * For example, el.getStyles('color', 'font-size', 'width') might return
7533          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7534          * @param {String} style1 A style name
7535          * @param {String} style2 A style name
7536          * @param {String} etc.
7537          * @return {Object} The style object
7538          */
7539         getStyles : function(){
7540             var a = arguments, len = a.length, r = {};
7541             for(var i = 0; i < len; i++){
7542                 r[a[i]] = this.getStyle(a[i]);
7543             }
7544             return r;
7545         },
7546
7547         /**
7548          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7549          * @param {String} property The style property whose value is returned.
7550          * @return {String} The current value of the style property for this element.
7551          */
7552         getStyle : function(){
7553             return view && view.getComputedStyle ?
7554                 function(prop){
7555                     var el = this.dom, v, cs, camel;
7556                     if(prop == 'float'){
7557                         prop = "cssFloat";
7558                     }
7559                     if(el.style && (v = el.style[prop])){
7560                         return v;
7561                     }
7562                     if(cs = view.getComputedStyle(el, "")){
7563                         if(!(camel = propCache[prop])){
7564                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7565                         }
7566                         return cs[camel];
7567                     }
7568                     return null;
7569                 } :
7570                 function(prop){
7571                     var el = this.dom, v, cs, camel;
7572                     if(prop == 'opacity'){
7573                         if(typeof el.style.filter == 'string'){
7574                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7575                             if(m){
7576                                 var fv = parseFloat(m[1]);
7577                                 if(!isNaN(fv)){
7578                                     return fv ? fv / 100 : 0;
7579                                 }
7580                             }
7581                         }
7582                         return 1;
7583                     }else if(prop == 'float'){
7584                         prop = "styleFloat";
7585                     }
7586                     if(!(camel = propCache[prop])){
7587                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7588                     }
7589                     if(v = el.style[camel]){
7590                         return v;
7591                     }
7592                     if(cs = el.currentStyle){
7593                         return cs[camel];
7594                     }
7595                     return null;
7596                 };
7597         }(),
7598
7599         /**
7600          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7601          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7602          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7603          * @return {Roo.Element} this
7604          */
7605         setStyle : function(prop, value){
7606             if(typeof prop == "string"){
7607                 
7608                 if (prop == 'float') {
7609                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7610                     return this;
7611                 }
7612                 
7613                 var camel;
7614                 if(!(camel = propCache[prop])){
7615                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7616                 }
7617                 
7618                 if(camel == 'opacity') {
7619                     this.setOpacity(value);
7620                 }else{
7621                     this.dom.style[camel] = value;
7622                 }
7623             }else{
7624                 for(var style in prop){
7625                     if(typeof prop[style] != "function"){
7626                        this.setStyle(style, prop[style]);
7627                     }
7628                 }
7629             }
7630             return this;
7631         },
7632
7633         /**
7634          * More flexible version of {@link #setStyle} for setting style properties.
7635          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7636          * a function which returns such a specification.
7637          * @return {Roo.Element} this
7638          */
7639         applyStyles : function(style){
7640             Roo.DomHelper.applyStyles(this.dom, style);
7641             return this;
7642         },
7643
7644         /**
7645           * 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).
7646           * @return {Number} The X position of the element
7647           */
7648         getX : function(){
7649             return D.getX(this.dom);
7650         },
7651
7652         /**
7653           * 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).
7654           * @return {Number} The Y position of the element
7655           */
7656         getY : function(){
7657             return D.getY(this.dom);
7658         },
7659
7660         /**
7661           * 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).
7662           * @return {Array} The XY position of the element
7663           */
7664         getXY : function(){
7665             return D.getXY(this.dom);
7666         },
7667
7668         /**
7669          * 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).
7670          * @param {Number} The X position of the element
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setX : function(x, animate){
7675             if(!animate || !A){
7676                 D.setX(this.dom, x);
7677             }else{
7678                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * 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).
7685          * @param {Number} The Y position of the element
7686          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7687          * @return {Roo.Element} this
7688          */
7689         setY : function(y, animate){
7690             if(!animate || !A){
7691                 D.setY(this.dom, y);
7692             }else{
7693                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7694             }
7695             return this;
7696         },
7697
7698         /**
7699          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7700          * @param {String} left The left CSS property value
7701          * @return {Roo.Element} this
7702          */
7703         setLeft : function(left){
7704             this.setStyle("left", this.addUnits(left));
7705             return this;
7706         },
7707
7708         /**
7709          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7710          * @param {String} top The top CSS property value
7711          * @return {Roo.Element} this
7712          */
7713         setTop : function(top){
7714             this.setStyle("top", this.addUnits(top));
7715             return this;
7716         },
7717
7718         /**
7719          * Sets the element's CSS right style.
7720          * @param {String} right The right CSS property value
7721          * @return {Roo.Element} this
7722          */
7723         setRight : function(right){
7724             this.setStyle("right", this.addUnits(right));
7725             return this;
7726         },
7727
7728         /**
7729          * Sets the element's CSS bottom style.
7730          * @param {String} bottom The bottom CSS property value
7731          * @return {Roo.Element} this
7732          */
7733         setBottom : function(bottom){
7734             this.setStyle("bottom", this.addUnits(bottom));
7735             return this;
7736         },
7737
7738         /**
7739          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7740          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7741          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7742          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7743          * @return {Roo.Element} this
7744          */
7745         setXY : function(pos, animate){
7746             if(!animate || !A){
7747                 D.setXY(this.dom, pos);
7748             }else{
7749                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7750             }
7751             return this;
7752         },
7753
7754         /**
7755          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7756          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7757          * @param {Number} x X value for new position (coordinates are page-based)
7758          * @param {Number} y Y value for new position (coordinates are page-based)
7759          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7760          * @return {Roo.Element} this
7761          */
7762         setLocation : function(x, y, animate){
7763             this.setXY([x, y], this.preanim(arguments, 2));
7764             return this;
7765         },
7766
7767         /**
7768          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7769          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7770          * @param {Number} x X value for new position (coordinates are page-based)
7771          * @param {Number} y Y value for new position (coordinates are page-based)
7772          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7773          * @return {Roo.Element} this
7774          */
7775         moveTo : function(x, y, animate){
7776             this.setXY([x, y], this.preanim(arguments, 2));
7777             return this;
7778         },
7779
7780         /**
7781          * Returns the region of the given element.
7782          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7783          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7784          */
7785         getRegion : function(){
7786             return D.getRegion(this.dom);
7787         },
7788
7789         /**
7790          * Returns the offset height of the element
7791          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7792          * @return {Number} The element's height
7793          */
7794         getHeight : function(contentHeight){
7795             var h = this.dom.offsetHeight || 0;
7796             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7797         },
7798
7799         /**
7800          * Returns the offset width of the element
7801          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7802          * @return {Number} The element's width
7803          */
7804         getWidth : function(contentWidth){
7805             var w = this.dom.offsetWidth || 0;
7806             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7807         },
7808
7809         /**
7810          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7811          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7812          * if a height has not been set using CSS.
7813          * @return {Number}
7814          */
7815         getComputedHeight : function(){
7816             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7817             if(!h){
7818                 h = parseInt(this.getStyle('height'), 10) || 0;
7819                 if(!this.isBorderBox()){
7820                     h += this.getFrameWidth('tb');
7821                 }
7822             }
7823             return h;
7824         },
7825
7826         /**
7827          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7828          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7829          * if a width has not been set using CSS.
7830          * @return {Number}
7831          */
7832         getComputedWidth : function(){
7833             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7834             if(!w){
7835                 w = parseInt(this.getStyle('width'), 10) || 0;
7836                 if(!this.isBorderBox()){
7837                     w += this.getFrameWidth('lr');
7838                 }
7839             }
7840             return w;
7841         },
7842
7843         /**
7844          * Returns the size of the element.
7845          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7846          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7847          */
7848         getSize : function(contentSize){
7849             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7850         },
7851
7852         /**
7853          * Returns the width and height of the viewport.
7854          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7855          */
7856         getViewSize : function(){
7857             var d = this.dom, doc = document, aw = 0, ah = 0;
7858             if(d == doc || d == doc.body){
7859                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7860             }else{
7861                 return {
7862                     width : d.clientWidth,
7863                     height: d.clientHeight
7864                 };
7865             }
7866         },
7867
7868         /**
7869          * Returns the value of the "value" attribute
7870          * @param {Boolean} asNumber true to parse the value as a number
7871          * @return {String/Number}
7872          */
7873         getValue : function(asNumber){
7874             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7875         },
7876
7877         // private
7878         adjustWidth : function(width){
7879             if(typeof width == "number"){
7880                 if(this.autoBoxAdjust && !this.isBorderBox()){
7881                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7882                 }
7883                 if(width < 0){
7884                     width = 0;
7885                 }
7886             }
7887             return width;
7888         },
7889
7890         // private
7891         adjustHeight : function(height){
7892             if(typeof height == "number"){
7893                if(this.autoBoxAdjust && !this.isBorderBox()){
7894                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7895                }
7896                if(height < 0){
7897                    height = 0;
7898                }
7899             }
7900             return height;
7901         },
7902
7903         /**
7904          * Set the width of the element
7905          * @param {Number} width The new width
7906          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7907          * @return {Roo.Element} this
7908          */
7909         setWidth : function(width, animate){
7910             width = this.adjustWidth(width);
7911             if(!animate || !A){
7912                 this.dom.style.width = this.addUnits(width);
7913             }else{
7914                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7915             }
7916             return this;
7917         },
7918
7919         /**
7920          * Set the height of the element
7921          * @param {Number} height The new height
7922          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7923          * @return {Roo.Element} this
7924          */
7925          setHeight : function(height, animate){
7926             height = this.adjustHeight(height);
7927             if(!animate || !A){
7928                 this.dom.style.height = this.addUnits(height);
7929             }else{
7930                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7931             }
7932             return this;
7933         },
7934
7935         /**
7936          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7937          * @param {Number} width The new width
7938          * @param {Number} height The new height
7939          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7940          * @return {Roo.Element} this
7941          */
7942          setSize : function(width, height, animate){
7943             if(typeof width == "object"){ // in case of object from getSize()
7944                 height = width.height; width = width.width;
7945             }
7946             width = this.adjustWidth(width); height = this.adjustHeight(height);
7947             if(!animate || !A){
7948                 this.dom.style.width = this.addUnits(width);
7949                 this.dom.style.height = this.addUnits(height);
7950             }else{
7951                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7952             }
7953             return this;
7954         },
7955
7956         /**
7957          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7958          * @param {Number} x X value for new position (coordinates are page-based)
7959          * @param {Number} y Y value for new position (coordinates are page-based)
7960          * @param {Number} width The new width
7961          * @param {Number} height The new height
7962          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7963          * @return {Roo.Element} this
7964          */
7965         setBounds : function(x, y, width, height, animate){
7966             if(!animate || !A){
7967                 this.setSize(width, height);
7968                 this.setLocation(x, y);
7969             }else{
7970                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7971                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7972                               this.preanim(arguments, 4), 'motion');
7973             }
7974             return this;
7975         },
7976
7977         /**
7978          * 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.
7979          * @param {Roo.lib.Region} region The region to fill
7980          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7981          * @return {Roo.Element} this
7982          */
7983         setRegion : function(region, animate){
7984             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7985             return this;
7986         },
7987
7988         /**
7989          * Appends an event handler
7990          *
7991          * @param {String}   eventName     The type of event to append
7992          * @param {Function} fn        The method the event invokes
7993          * @param {Object} scope       (optional) The scope (this object) of the fn
7994          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7995          */
7996         addListener : function(eventName, fn, scope, options){
7997             if (this.dom) {
7998                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7999             }
8000         },
8001
8002         /**
8003          * Removes an event handler from this element
8004          * @param {String} eventName the type of event to remove
8005          * @param {Function} fn the method the event invokes
8006          * @return {Roo.Element} this
8007          */
8008         removeListener : function(eventName, fn){
8009             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8010             return this;
8011         },
8012
8013         /**
8014          * Removes all previous added listeners from this element
8015          * @return {Roo.Element} this
8016          */
8017         removeAllListeners : function(){
8018             E.purgeElement(this.dom);
8019             return this;
8020         },
8021
8022         relayEvent : function(eventName, observable){
8023             this.on(eventName, function(e){
8024                 observable.fireEvent(eventName, e);
8025             });
8026         },
8027
8028         /**
8029          * Set the opacity of the element
8030          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8031          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8032          * @return {Roo.Element} this
8033          */
8034          setOpacity : function(opacity, animate){
8035             if(!animate || !A){
8036                 var s = this.dom.style;
8037                 if(Roo.isIE){
8038                     s.zoom = 1;
8039                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8040                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8041                 }else{
8042                     s.opacity = opacity;
8043                 }
8044             }else{
8045                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8046             }
8047             return this;
8048         },
8049
8050         /**
8051          * Gets the left X coordinate
8052          * @param {Boolean} local True to get the local css position instead of page coordinate
8053          * @return {Number}
8054          */
8055         getLeft : function(local){
8056             if(!local){
8057                 return this.getX();
8058             }else{
8059                 return parseInt(this.getStyle("left"), 10) || 0;
8060             }
8061         },
8062
8063         /**
8064          * Gets the right X coordinate of the element (element X position + element width)
8065          * @param {Boolean} local True to get the local css position instead of page coordinate
8066          * @return {Number}
8067          */
8068         getRight : function(local){
8069             if(!local){
8070                 return this.getX() + this.getWidth();
8071             }else{
8072                 return (this.getLeft(true) + this.getWidth()) || 0;
8073             }
8074         },
8075
8076         /**
8077          * Gets the top Y coordinate
8078          * @param {Boolean} local True to get the local css position instead of page coordinate
8079          * @return {Number}
8080          */
8081         getTop : function(local) {
8082             if(!local){
8083                 return this.getY();
8084             }else{
8085                 return parseInt(this.getStyle("top"), 10) || 0;
8086             }
8087         },
8088
8089         /**
8090          * Gets the bottom Y coordinate of the element (element Y position + element height)
8091          * @param {Boolean} local True to get the local css position instead of page coordinate
8092          * @return {Number}
8093          */
8094         getBottom : function(local){
8095             if(!local){
8096                 return this.getY() + this.getHeight();
8097             }else{
8098                 return (this.getTop(true) + this.getHeight()) || 0;
8099             }
8100         },
8101
8102         /**
8103         * Initializes positioning on this element. If a desired position is not passed, it will make the
8104         * the element positioned relative IF it is not already positioned.
8105         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8106         * @param {Number} zIndex (optional) The zIndex to apply
8107         * @param {Number} x (optional) Set the page X position
8108         * @param {Number} y (optional) Set the page Y position
8109         */
8110         position : function(pos, zIndex, x, y){
8111             if(!pos){
8112                if(this.getStyle('position') == 'static'){
8113                    this.setStyle('position', 'relative');
8114                }
8115             }else{
8116                 this.setStyle("position", pos);
8117             }
8118             if(zIndex){
8119                 this.setStyle("z-index", zIndex);
8120             }
8121             if(x !== undefined && y !== undefined){
8122                 this.setXY([x, y]);
8123             }else if(x !== undefined){
8124                 this.setX(x);
8125             }else if(y !== undefined){
8126                 this.setY(y);
8127             }
8128         },
8129
8130         /**
8131         * Clear positioning back to the default when the document was loaded
8132         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8133         * @return {Roo.Element} this
8134          */
8135         clearPositioning : function(value){
8136             value = value ||'';
8137             this.setStyle({
8138                 "left": value,
8139                 "right": value,
8140                 "top": value,
8141                 "bottom": value,
8142                 "z-index": "",
8143                 "position" : "static"
8144             });
8145             return this;
8146         },
8147
8148         /**
8149         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8150         * snapshot before performing an update and then restoring the element.
8151         * @return {Object}
8152         */
8153         getPositioning : function(){
8154             var l = this.getStyle("left");
8155             var t = this.getStyle("top");
8156             return {
8157                 "position" : this.getStyle("position"),
8158                 "left" : l,
8159                 "right" : l ? "" : this.getStyle("right"),
8160                 "top" : t,
8161                 "bottom" : t ? "" : this.getStyle("bottom"),
8162                 "z-index" : this.getStyle("z-index")
8163             };
8164         },
8165
8166         /**
8167          * Gets the width of the border(s) for the specified side(s)
8168          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8169          * passing lr would get the border (l)eft width + the border (r)ight width.
8170          * @return {Number} The width of the sides passed added together
8171          */
8172         getBorderWidth : function(side){
8173             return this.addStyles(side, El.borders);
8174         },
8175
8176         /**
8177          * Gets the width of the padding(s) for the specified side(s)
8178          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8179          * passing lr would get the padding (l)eft + the padding (r)ight.
8180          * @return {Number} The padding of the sides passed added together
8181          */
8182         getPadding : function(side){
8183             return this.addStyles(side, El.paddings);
8184         },
8185
8186         /**
8187         * Set positioning with an object returned by getPositioning().
8188         * @param {Object} posCfg
8189         * @return {Roo.Element} this
8190          */
8191         setPositioning : function(pc){
8192             this.applyStyles(pc);
8193             if(pc.right == "auto"){
8194                 this.dom.style.right = "";
8195             }
8196             if(pc.bottom == "auto"){
8197                 this.dom.style.bottom = "";
8198             }
8199             return this;
8200         },
8201
8202         // private
8203         fixDisplay : function(){
8204             if(this.getStyle("display") == "none"){
8205                 this.setStyle("visibility", "hidden");
8206                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8207                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8208                     this.setStyle("display", "block");
8209                 }
8210             }
8211         },
8212
8213         /**
8214          * Quick set left and top adding default units
8215          * @param {String} left The left CSS property value
8216          * @param {String} top The top CSS property value
8217          * @return {Roo.Element} this
8218          */
8219          setLeftTop : function(left, top){
8220             this.dom.style.left = this.addUnits(left);
8221             this.dom.style.top = this.addUnits(top);
8222             return this;
8223         },
8224
8225         /**
8226          * Move this element relative to its current position.
8227          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8228          * @param {Number} distance How far to move the element in pixels
8229          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8230          * @return {Roo.Element} this
8231          */
8232          move : function(direction, distance, animate){
8233             var xy = this.getXY();
8234             direction = direction.toLowerCase();
8235             switch(direction){
8236                 case "l":
8237                 case "left":
8238                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8239                     break;
8240                case "r":
8241                case "right":
8242                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8243                     break;
8244                case "t":
8245                case "top":
8246                case "up":
8247                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8248                     break;
8249                case "b":
8250                case "bottom":
8251                case "down":
8252                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8253                     break;
8254             }
8255             return this;
8256         },
8257
8258         /**
8259          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8260          * @return {Roo.Element} this
8261          */
8262         clip : function(){
8263             if(!this.isClipped){
8264                this.isClipped = true;
8265                this.originalClip = {
8266                    "o": this.getStyle("overflow"),
8267                    "x": this.getStyle("overflow-x"),
8268                    "y": this.getStyle("overflow-y")
8269                };
8270                this.setStyle("overflow", "hidden");
8271                this.setStyle("overflow-x", "hidden");
8272                this.setStyle("overflow-y", "hidden");
8273             }
8274             return this;
8275         },
8276
8277         /**
8278          *  Return clipping (overflow) to original clipping before clip() was called
8279          * @return {Roo.Element} this
8280          */
8281         unclip : function(){
8282             if(this.isClipped){
8283                 this.isClipped = false;
8284                 var o = this.originalClip;
8285                 if(o.o){this.setStyle("overflow", o.o);}
8286                 if(o.x){this.setStyle("overflow-x", o.x);}
8287                 if(o.y){this.setStyle("overflow-y", o.y);}
8288             }
8289             return this;
8290         },
8291
8292
8293         /**
8294          * Gets the x,y coordinates specified by the anchor position on the element.
8295          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8296          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8297          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8298          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8299          * @return {Array} [x, y] An array containing the element's x and y coordinates
8300          */
8301         getAnchorXY : function(anchor, local, s){
8302             //Passing a different size is useful for pre-calculating anchors,
8303             //especially for anchored animations that change the el size.
8304
8305             var w, h, vp = false;
8306             if(!s){
8307                 var d = this.dom;
8308                 if(d == document.body || d == document){
8309                     vp = true;
8310                     w = D.getViewWidth(); h = D.getViewHeight();
8311                 }else{
8312                     w = this.getWidth(); h = this.getHeight();
8313                 }
8314             }else{
8315                 w = s.width;  h = s.height;
8316             }
8317             var x = 0, y = 0, r = Math.round;
8318             switch((anchor || "tl").toLowerCase()){
8319                 case "c":
8320                     x = r(w*.5);
8321                     y = r(h*.5);
8322                 break;
8323                 case "t":
8324                     x = r(w*.5);
8325                     y = 0;
8326                 break;
8327                 case "l":
8328                     x = 0;
8329                     y = r(h*.5);
8330                 break;
8331                 case "r":
8332                     x = w;
8333                     y = r(h*.5);
8334                 break;
8335                 case "b":
8336                     x = r(w*.5);
8337                     y = h;
8338                 break;
8339                 case "tl":
8340                     x = 0;
8341                     y = 0;
8342                 break;
8343                 case "bl":
8344                     x = 0;
8345                     y = h;
8346                 break;
8347                 case "br":
8348                     x = w;
8349                     y = h;
8350                 break;
8351                 case "tr":
8352                     x = w;
8353                     y = 0;
8354                 break;
8355             }
8356             if(local === true){
8357                 return [x, y];
8358             }
8359             if(vp){
8360                 var sc = this.getScroll();
8361                 return [x + sc.left, y + sc.top];
8362             }
8363             //Add the element's offset xy
8364             var o = this.getXY();
8365             return [x+o[0], y+o[1]];
8366         },
8367
8368         /**
8369          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8370          * supported position values.
8371          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8372          * @param {String} position The position to align to.
8373          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8374          * @return {Array} [x, y]
8375          */
8376         getAlignToXY : function(el, p, o){
8377             el = Roo.get(el);
8378             var d = this.dom;
8379             if(!el.dom){
8380                 throw "Element.alignTo with an element that doesn't exist";
8381             }
8382             var c = false; //constrain to viewport
8383             var p1 = "", p2 = "";
8384             o = o || [0,0];
8385
8386             if(!p){
8387                 p = "tl-bl";
8388             }else if(p == "?"){
8389                 p = "tl-bl?";
8390             }else if(p.indexOf("-") == -1){
8391                 p = "tl-" + p;
8392             }
8393             p = p.toLowerCase();
8394             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8395             if(!m){
8396                throw "Element.alignTo with an invalid alignment " + p;
8397             }
8398             p1 = m[1]; p2 = m[2]; c = !!m[3];
8399
8400             //Subtract the aligned el's internal xy from the target's offset xy
8401             //plus custom offset to get the aligned el's new offset xy
8402             var a1 = this.getAnchorXY(p1, true);
8403             var a2 = el.getAnchorXY(p2, false);
8404             var x = a2[0] - a1[0] + o[0];
8405             var y = a2[1] - a1[1] + o[1];
8406             if(c){
8407                 //constrain the aligned el to viewport if necessary
8408                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8409                 // 5px of margin for ie
8410                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8411
8412                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8413                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8414                 //otherwise swap the aligned el to the opposite border of the target.
8415                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8416                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8417                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8418                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8419
8420                var doc = document;
8421                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8422                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8423
8424                if((x+w) > dw + scrollX){
8425                     x = swapX ? r.left-w : dw+scrollX-w;
8426                 }
8427                if(x < scrollX){
8428                    x = swapX ? r.right : scrollX;
8429                }
8430                if((y+h) > dh + scrollY){
8431                     y = swapY ? r.top-h : dh+scrollY-h;
8432                 }
8433                if (y < scrollY){
8434                    y = swapY ? r.bottom : scrollY;
8435                }
8436             }
8437             return [x,y];
8438         },
8439
8440         // private
8441         getConstrainToXY : function(){
8442             var os = {top:0, left:0, bottom:0, right: 0};
8443
8444             return function(el, local, offsets, proposedXY){
8445                 el = Roo.get(el);
8446                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8447
8448                 var vw, vh, vx = 0, vy = 0;
8449                 if(el.dom == document.body || el.dom == document){
8450                     vw = Roo.lib.Dom.getViewWidth();
8451                     vh = Roo.lib.Dom.getViewHeight();
8452                 }else{
8453                     vw = el.dom.clientWidth;
8454                     vh = el.dom.clientHeight;
8455                     if(!local){
8456                         var vxy = el.getXY();
8457                         vx = vxy[0];
8458                         vy = vxy[1];
8459                     }
8460                 }
8461
8462                 var s = el.getScroll();
8463
8464                 vx += offsets.left + s.left;
8465                 vy += offsets.top + s.top;
8466
8467                 vw -= offsets.right;
8468                 vh -= offsets.bottom;
8469
8470                 var vr = vx+vw;
8471                 var vb = vy+vh;
8472
8473                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8474                 var x = xy[0], y = xy[1];
8475                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8476
8477                 // only move it if it needs it
8478                 var moved = false;
8479
8480                 // first validate right/bottom
8481                 if((x + w) > vr){
8482                     x = vr - w;
8483                     moved = true;
8484                 }
8485                 if((y + h) > vb){
8486                     y = vb - h;
8487                     moved = true;
8488                 }
8489                 // then make sure top/left isn't negative
8490                 if(x < vx){
8491                     x = vx;
8492                     moved = true;
8493                 }
8494                 if(y < vy){
8495                     y = vy;
8496                     moved = true;
8497                 }
8498                 return moved ? [x, y] : false;
8499             };
8500         }(),
8501
8502         // private
8503         adjustForConstraints : function(xy, parent, offsets){
8504             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8505         },
8506
8507         /**
8508          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8509          * document it aligns it to the viewport.
8510          * The position parameter is optional, and can be specified in any one of the following formats:
8511          * <ul>
8512          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8513          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8514          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8515          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8516          *   <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
8517          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8518          * </ul>
8519          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8520          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8521          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8522          * that specified in order to enforce the viewport constraints.
8523          * Following are all of the supported anchor positions:
8524     <pre>
8525     Value  Description
8526     -----  -----------------------------
8527     tl     The top left corner (default)
8528     t      The center of the top edge
8529     tr     The top right corner
8530     l      The center of the left edge
8531     c      In the center of the element
8532     r      The center of the right edge
8533     bl     The bottom left corner
8534     b      The center of the bottom edge
8535     br     The bottom right corner
8536     </pre>
8537     Example Usage:
8538     <pre><code>
8539     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8540     el.alignTo("other-el");
8541
8542     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8543     el.alignTo("other-el", "tr?");
8544
8545     // align the bottom right corner of el with the center left edge of other-el
8546     el.alignTo("other-el", "br-l?");
8547
8548     // align the center of el with the bottom left corner of other-el and
8549     // adjust the x position by -6 pixels (and the y position by 0)
8550     el.alignTo("other-el", "c-bl", [-6, 0]);
8551     </code></pre>
8552          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8553          * @param {String} position The position to align to.
8554          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8555          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8556          * @return {Roo.Element} this
8557          */
8558         alignTo : function(element, position, offsets, animate){
8559             var xy = this.getAlignToXY(element, position, offsets);
8560             this.setXY(xy, this.preanim(arguments, 3));
8561             return this;
8562         },
8563
8564         /**
8565          * Anchors an element to another element and realigns it when the window is resized.
8566          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8567          * @param {String} position The position to align to.
8568          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8569          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8570          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8571          * is a number, it is used as the buffer delay (defaults to 50ms).
8572          * @param {Function} callback The function to call after the animation finishes
8573          * @return {Roo.Element} this
8574          */
8575         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8576             var action = function(){
8577                 this.alignTo(el, alignment, offsets, animate);
8578                 Roo.callback(callback, this);
8579             };
8580             Roo.EventManager.onWindowResize(action, this);
8581             var tm = typeof monitorScroll;
8582             if(tm != 'undefined'){
8583                 Roo.EventManager.on(window, 'scroll', action, this,
8584                     {buffer: tm == 'number' ? monitorScroll : 50});
8585             }
8586             action.call(this); // align immediately
8587             return this;
8588         },
8589         /**
8590          * Clears any opacity settings from this element. Required in some cases for IE.
8591          * @return {Roo.Element} this
8592          */
8593         clearOpacity : function(){
8594             if (window.ActiveXObject) {
8595                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8596                     this.dom.style.filter = "";
8597                 }
8598             } else {
8599                 this.dom.style.opacity = "";
8600                 this.dom.style["-moz-opacity"] = "";
8601                 this.dom.style["-khtml-opacity"] = "";
8602             }
8603             return this;
8604         },
8605
8606         /**
8607          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8608          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8609          * @return {Roo.Element} this
8610          */
8611         hide : function(animate){
8612             this.setVisible(false, this.preanim(arguments, 0));
8613             return this;
8614         },
8615
8616         /**
8617         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8618         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8619          * @return {Roo.Element} this
8620          */
8621         show : function(animate){
8622             this.setVisible(true, this.preanim(arguments, 0));
8623             return this;
8624         },
8625
8626         /**
8627          * @private Test if size has a unit, otherwise appends the default
8628          */
8629         addUnits : function(size){
8630             return Roo.Element.addUnits(size, this.defaultUnit);
8631         },
8632
8633         /**
8634          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8635          * @return {Roo.Element} this
8636          */
8637         beginMeasure : function(){
8638             var el = this.dom;
8639             if(el.offsetWidth || el.offsetHeight){
8640                 return this; // offsets work already
8641             }
8642             var changed = [];
8643             var p = this.dom, b = document.body; // start with this element
8644             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8645                 var pe = Roo.get(p);
8646                 if(pe.getStyle('display') == 'none'){
8647                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8648                     p.style.visibility = "hidden";
8649                     p.style.display = "block";
8650                 }
8651                 p = p.parentNode;
8652             }
8653             this._measureChanged = changed;
8654             return this;
8655
8656         },
8657
8658         /**
8659          * Restores displays to before beginMeasure was called
8660          * @return {Roo.Element} this
8661          */
8662         endMeasure : function(){
8663             var changed = this._measureChanged;
8664             if(changed){
8665                 for(var i = 0, len = changed.length; i < len; i++) {
8666                     var r = changed[i];
8667                     r.el.style.visibility = r.visibility;
8668                     r.el.style.display = "none";
8669                 }
8670                 this._measureChanged = null;
8671             }
8672             return this;
8673         },
8674
8675         /**
8676         * Update the innerHTML of this element, optionally searching for and processing scripts
8677         * @param {String} html The new HTML
8678         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8679         * @param {Function} callback For async script loading you can be noticed when the update completes
8680         * @return {Roo.Element} this
8681          */
8682         update : function(html, loadScripts, callback){
8683             if(typeof html == "undefined"){
8684                 html = "";
8685             }
8686             if(loadScripts !== true){
8687                 this.dom.innerHTML = html;
8688                 if(typeof callback == "function"){
8689                     callback();
8690                 }
8691                 return this;
8692             }
8693             var id = Roo.id();
8694             var dom = this.dom;
8695
8696             html += '<span id="' + id + '"></span>';
8697
8698             E.onAvailable(id, function(){
8699                 var hd = document.getElementsByTagName("head")[0];
8700                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8701                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8702                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8703
8704                 var match;
8705                 while(match = re.exec(html)){
8706                     var attrs = match[1];
8707                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8708                     if(srcMatch && srcMatch[2]){
8709                        var s = document.createElement("script");
8710                        s.src = srcMatch[2];
8711                        var typeMatch = attrs.match(typeRe);
8712                        if(typeMatch && typeMatch[2]){
8713                            s.type = typeMatch[2];
8714                        }
8715                        hd.appendChild(s);
8716                     }else if(match[2] && match[2].length > 0){
8717                         if(window.execScript) {
8718                            window.execScript(match[2]);
8719                         } else {
8720                             /**
8721                              * eval:var:id
8722                              * eval:var:dom
8723                              * eval:var:html
8724                              * 
8725                              */
8726                            window.eval(match[2]);
8727                         }
8728                     }
8729                 }
8730                 var el = document.getElementById(id);
8731                 if(el){el.parentNode.removeChild(el);}
8732                 if(typeof callback == "function"){
8733                     callback();
8734                 }
8735             });
8736             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8737             return this;
8738         },
8739
8740         /**
8741          * Direct access to the UpdateManager update() method (takes the same parameters).
8742          * @param {String/Function} url The url for this request or a function to call to get the url
8743          * @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}
8744          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8745          * @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.
8746          * @return {Roo.Element} this
8747          */
8748         load : function(){
8749             var um = this.getUpdateManager();
8750             um.update.apply(um, arguments);
8751             return this;
8752         },
8753
8754         /**
8755         * Gets this element's UpdateManager
8756         * @return {Roo.UpdateManager} The UpdateManager
8757         */
8758         getUpdateManager : function(){
8759             if(!this.updateManager){
8760                 this.updateManager = new Roo.UpdateManager(this);
8761             }
8762             return this.updateManager;
8763         },
8764
8765         /**
8766          * Disables text selection for this element (normalized across browsers)
8767          * @return {Roo.Element} this
8768          */
8769         unselectable : function(){
8770             this.dom.unselectable = "on";
8771             this.swallowEvent("selectstart", true);
8772             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8773             this.addClass("x-unselectable");
8774             return this;
8775         },
8776
8777         /**
8778         * Calculates the x, y to center this element on the screen
8779         * @return {Array} The x, y values [x, y]
8780         */
8781         getCenterXY : function(){
8782             return this.getAlignToXY(document, 'c-c');
8783         },
8784
8785         /**
8786         * Centers the Element in either the viewport, or another Element.
8787         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8788         */
8789         center : function(centerIn){
8790             this.alignTo(centerIn || document, 'c-c');
8791             return this;
8792         },
8793
8794         /**
8795          * Tests various css rules/browsers to determine if this element uses a border box
8796          * @return {Boolean}
8797          */
8798         isBorderBox : function(){
8799             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8800         },
8801
8802         /**
8803          * Return a box {x, y, width, height} that can be used to set another elements
8804          * size/location to match this element.
8805          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8806          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8807          * @return {Object} box An object in the format {x, y, width, height}
8808          */
8809         getBox : function(contentBox, local){
8810             var xy;
8811             if(!local){
8812                 xy = this.getXY();
8813             }else{
8814                 var left = parseInt(this.getStyle("left"), 10) || 0;
8815                 var top = parseInt(this.getStyle("top"), 10) || 0;
8816                 xy = [left, top];
8817             }
8818             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8819             if(!contentBox){
8820                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8821             }else{
8822                 var l = this.getBorderWidth("l")+this.getPadding("l");
8823                 var r = this.getBorderWidth("r")+this.getPadding("r");
8824                 var t = this.getBorderWidth("t")+this.getPadding("t");
8825                 var b = this.getBorderWidth("b")+this.getPadding("b");
8826                 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)};
8827             }
8828             bx.right = bx.x + bx.width;
8829             bx.bottom = bx.y + bx.height;
8830             return bx;
8831         },
8832
8833         /**
8834          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8835          for more information about the sides.
8836          * @param {String} sides
8837          * @return {Number}
8838          */
8839         getFrameWidth : function(sides, onlyContentBox){
8840             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8841         },
8842
8843         /**
8844          * 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.
8845          * @param {Object} box The box to fill {x, y, width, height}
8846          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8847          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8848          * @return {Roo.Element} this
8849          */
8850         setBox : function(box, adjust, animate){
8851             var w = box.width, h = box.height;
8852             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8853                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8854                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8855             }
8856             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8857             return this;
8858         },
8859
8860         /**
8861          * Forces the browser to repaint this element
8862          * @return {Roo.Element} this
8863          */
8864          repaint : function(){
8865             var dom = this.dom;
8866             this.addClass("x-repaint");
8867             setTimeout(function(){
8868                 Roo.get(dom).removeClass("x-repaint");
8869             }, 1);
8870             return this;
8871         },
8872
8873         /**
8874          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8875          * then it returns the calculated width of the sides (see getPadding)
8876          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8877          * @return {Object/Number}
8878          */
8879         getMargins : function(side){
8880             if(!side){
8881                 return {
8882                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8883                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8884                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8885                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8886                 };
8887             }else{
8888                 return this.addStyles(side, El.margins);
8889              }
8890         },
8891
8892         // private
8893         addStyles : function(sides, styles){
8894             var val = 0, v, w;
8895             for(var i = 0, len = sides.length; i < len; i++){
8896                 v = this.getStyle(styles[sides.charAt(i)]);
8897                 if(v){
8898                      w = parseInt(v, 10);
8899                      if(w){ val += w; }
8900                 }
8901             }
8902             return val;
8903         },
8904
8905         /**
8906          * Creates a proxy element of this element
8907          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8908          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8909          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8910          * @return {Roo.Element} The new proxy element
8911          */
8912         createProxy : function(config, renderTo, matchBox){
8913             if(renderTo){
8914                 renderTo = Roo.getDom(renderTo);
8915             }else{
8916                 renderTo = document.body;
8917             }
8918             config = typeof config == "object" ?
8919                 config : {tag : "div", cls: config};
8920             var proxy = Roo.DomHelper.append(renderTo, config, true);
8921             if(matchBox){
8922                proxy.setBox(this.getBox());
8923             }
8924             return proxy;
8925         },
8926
8927         /**
8928          * Puts a mask over this element to disable user interaction. Requires core.css.
8929          * This method can only be applied to elements which accept child nodes.
8930          * @param {String} msg (optional) A message to display in the mask
8931          * @param {String} msgCls (optional) A css class to apply to the msg element
8932          * @return {Element} The mask  element
8933          */
8934         mask : function(msg, msgCls)
8935         {
8936             if(this.getStyle("position") == "static"){
8937                 this.setStyle("position", "relative");
8938             }
8939             if(!this._mask){
8940                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8941             }
8942             this.addClass("x-masked");
8943             this._mask.setDisplayed(true);
8944             
8945             // we wander
8946             var z = 0;
8947             var dom = this.dom
8948             while (dom && dom.style) {
8949                 if (!isNaN(parseInt(dom.style.zIndex))) {
8950                     z = Math.max(z, parseInt(dom.style.zIndex));
8951                 }
8952                 dom = dom.parentNode;
8953             }
8954             // if we are masking the body - then it hides everything..
8955             if (this.dom == document.body) {
8956                 z = 1000000;
8957                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8958                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8959             }
8960            
8961             if(typeof msg == 'string'){
8962                 if(!this._maskMsg){
8963                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8964                 }
8965                 var mm = this._maskMsg;
8966                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8967                 mm.dom.firstChild.innerHTML = msg;
8968                 mm.setDisplayed(true);
8969                 mm.center(this);
8970                 mm.setStyle('z-index', z + 102);
8971             }
8972             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8973                 this._mask.setHeight(this.getHeight());
8974             }
8975             this._mask.setStyle('z-index', z + 100);
8976             
8977             return this._mask;
8978         },
8979
8980         /**
8981          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8982          * it is cached for reuse.
8983          */
8984         unmask : function(removeEl){
8985             if(this._mask){
8986                 if(removeEl === true){
8987                     this._mask.remove();
8988                     delete this._mask;
8989                     if(this._maskMsg){
8990                         this._maskMsg.remove();
8991                         delete this._maskMsg;
8992                     }
8993                 }else{
8994                     this._mask.setDisplayed(false);
8995                     if(this._maskMsg){
8996                         this._maskMsg.setDisplayed(false);
8997                     }
8998                 }
8999             }
9000             this.removeClass("x-masked");
9001         },
9002
9003         /**
9004          * Returns true if this element is masked
9005          * @return {Boolean}
9006          */
9007         isMasked : function(){
9008             return this._mask && this._mask.isVisible();
9009         },
9010
9011         /**
9012          * Creates an iframe shim for this element to keep selects and other windowed objects from
9013          * showing through.
9014          * @return {Roo.Element} The new shim element
9015          */
9016         createShim : function(){
9017             var el = document.createElement('iframe');
9018             el.frameBorder = 'no';
9019             el.className = 'roo-shim';
9020             if(Roo.isIE && Roo.isSecure){
9021                 el.src = Roo.SSL_SECURE_URL;
9022             }
9023             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9024             shim.autoBoxAdjust = false;
9025             return shim;
9026         },
9027
9028         /**
9029          * Removes this element from the DOM and deletes it from the cache
9030          */
9031         remove : function(){
9032             if(this.dom.parentNode){
9033                 this.dom.parentNode.removeChild(this.dom);
9034             }
9035             delete El.cache[this.dom.id];
9036         },
9037
9038         /**
9039          * Sets up event handlers to add and remove a css class when the mouse is over this element
9040          * @param {String} className
9041          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9042          * mouseout events for children elements
9043          * @return {Roo.Element} this
9044          */
9045         addClassOnOver : function(className, preventFlicker){
9046             this.on("mouseover", function(){
9047                 Roo.fly(this, '_internal').addClass(className);
9048             }, this.dom);
9049             var removeFn = function(e){
9050                 if(preventFlicker !== true || !e.within(this, true)){
9051                     Roo.fly(this, '_internal').removeClass(className);
9052                 }
9053             };
9054             this.on("mouseout", removeFn, this.dom);
9055             return this;
9056         },
9057
9058         /**
9059          * Sets up event handlers to add and remove a css class when this element has the focus
9060          * @param {String} className
9061          * @return {Roo.Element} this
9062          */
9063         addClassOnFocus : function(className){
9064             this.on("focus", function(){
9065                 Roo.fly(this, '_internal').addClass(className);
9066             }, this.dom);
9067             this.on("blur", function(){
9068                 Roo.fly(this, '_internal').removeClass(className);
9069             }, this.dom);
9070             return this;
9071         },
9072         /**
9073          * 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)
9074          * @param {String} className
9075          * @return {Roo.Element} this
9076          */
9077         addClassOnClick : function(className){
9078             var dom = this.dom;
9079             this.on("mousedown", function(){
9080                 Roo.fly(dom, '_internal').addClass(className);
9081                 var d = Roo.get(document);
9082                 var fn = function(){
9083                     Roo.fly(dom, '_internal').removeClass(className);
9084                     d.removeListener("mouseup", fn);
9085                 };
9086                 d.on("mouseup", fn);
9087             });
9088             return this;
9089         },
9090
9091         /**
9092          * Stops the specified event from bubbling and optionally prevents the default action
9093          * @param {String} eventName
9094          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9095          * @return {Roo.Element} this
9096          */
9097         swallowEvent : function(eventName, preventDefault){
9098             var fn = function(e){
9099                 e.stopPropagation();
9100                 if(preventDefault){
9101                     e.preventDefault();
9102                 }
9103             };
9104             if(eventName instanceof Array){
9105                 for(var i = 0, len = eventName.length; i < len; i++){
9106                      this.on(eventName[i], fn);
9107                 }
9108                 return this;
9109             }
9110             this.on(eventName, fn);
9111             return this;
9112         },
9113
9114         /**
9115          * @private
9116          */
9117       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9118
9119         /**
9120          * Sizes this element to its parent element's dimensions performing
9121          * neccessary box adjustments.
9122          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9123          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9124          * @return {Roo.Element} this
9125          */
9126         fitToParent : function(monitorResize, targetParent) {
9127           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9128           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9129           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9130             return;
9131           }
9132           var p = Roo.get(targetParent || this.dom.parentNode);
9133           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9134           if (monitorResize === true) {
9135             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9136             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9137           }
9138           return this;
9139         },
9140
9141         /**
9142          * Gets the next sibling, skipping text nodes
9143          * @return {HTMLElement} The next sibling or null
9144          */
9145         getNextSibling : function(){
9146             var n = this.dom.nextSibling;
9147             while(n && n.nodeType != 1){
9148                 n = n.nextSibling;
9149             }
9150             return n;
9151         },
9152
9153         /**
9154          * Gets the previous sibling, skipping text nodes
9155          * @return {HTMLElement} The previous sibling or null
9156          */
9157         getPrevSibling : function(){
9158             var n = this.dom.previousSibling;
9159             while(n && n.nodeType != 1){
9160                 n = n.previousSibling;
9161             }
9162             return n;
9163         },
9164
9165
9166         /**
9167          * Appends the passed element(s) to this element
9168          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9169          * @return {Roo.Element} this
9170          */
9171         appendChild: function(el){
9172             el = Roo.get(el);
9173             el.appendTo(this);
9174             return this;
9175         },
9176
9177         /**
9178          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9179          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9180          * automatically generated with the specified attributes.
9181          * @param {HTMLElement} insertBefore (optional) a child element of this element
9182          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9183          * @return {Roo.Element} The new child element
9184          */
9185         createChild: function(config, insertBefore, returnDom){
9186             config = config || {tag:'div'};
9187             if(insertBefore){
9188                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9189             }
9190             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9191         },
9192
9193         /**
9194          * Appends this element to the passed element
9195          * @param {String/HTMLElement/Element} el The new parent element
9196          * @return {Roo.Element} this
9197          */
9198         appendTo: function(el){
9199             el = Roo.getDom(el);
9200             el.appendChild(this.dom);
9201             return this;
9202         },
9203
9204         /**
9205          * Inserts this element before the passed element in the DOM
9206          * @param {String/HTMLElement/Element} el The element to insert before
9207          * @return {Roo.Element} this
9208          */
9209         insertBefore: function(el){
9210             el = Roo.getDom(el);
9211             el.parentNode.insertBefore(this.dom, el);
9212             return this;
9213         },
9214
9215         /**
9216          * Inserts this element after the passed element in the DOM
9217          * @param {String/HTMLElement/Element} el The element to insert after
9218          * @return {Roo.Element} this
9219          */
9220         insertAfter: function(el){
9221             el = Roo.getDom(el);
9222             el.parentNode.insertBefore(this.dom, el.nextSibling);
9223             return this;
9224         },
9225
9226         /**
9227          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9228          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9229          * @return {Roo.Element} The new child
9230          */
9231         insertFirst: function(el, returnDom){
9232             el = el || {};
9233             if(typeof el == 'object' && !el.nodeType){ // dh config
9234                 return this.createChild(el, this.dom.firstChild, returnDom);
9235             }else{
9236                 el = Roo.getDom(el);
9237                 this.dom.insertBefore(el, this.dom.firstChild);
9238                 return !returnDom ? Roo.get(el) : el;
9239             }
9240         },
9241
9242         /**
9243          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9244          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9245          * @param {String} where (optional) 'before' or 'after' defaults to before
9246          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9247          * @return {Roo.Element} the inserted Element
9248          */
9249         insertSibling: function(el, where, returnDom){
9250             where = where ? where.toLowerCase() : 'before';
9251             el = el || {};
9252             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9253
9254             if(typeof el == 'object' && !el.nodeType){ // dh config
9255                 if(where == 'after' && !this.dom.nextSibling){
9256                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9257                 }else{
9258                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9259                 }
9260
9261             }else{
9262                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9263                             where == 'before' ? this.dom : this.dom.nextSibling);
9264                 if(!returnDom){
9265                     rt = Roo.get(rt);
9266                 }
9267             }
9268             return rt;
9269         },
9270
9271         /**
9272          * Creates and wraps this element with another element
9273          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9274          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9275          * @return {HTMLElement/Element} The newly created wrapper element
9276          */
9277         wrap: function(config, returnDom){
9278             if(!config){
9279                 config = {tag: "div"};
9280             }
9281             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9282             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9283             return newEl;
9284         },
9285
9286         /**
9287          * Replaces the passed element with this element
9288          * @param {String/HTMLElement/Element} el The element to replace
9289          * @return {Roo.Element} this
9290          */
9291         replace: function(el){
9292             el = Roo.get(el);
9293             this.insertBefore(el);
9294             el.remove();
9295             return this;
9296         },
9297
9298         /**
9299          * Inserts an html fragment into this element
9300          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9301          * @param {String} html The HTML fragment
9302          * @param {Boolean} returnEl True to return an Roo.Element
9303          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9304          */
9305         insertHtml : function(where, html, returnEl){
9306             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9307             return returnEl ? Roo.get(el) : el;
9308         },
9309
9310         /**
9311          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9312          * @param {Object} o The object with the attributes
9313          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9314          * @return {Roo.Element} this
9315          */
9316         set : function(o, useSet){
9317             var el = this.dom;
9318             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9319             for(var attr in o){
9320                 if(attr == "style" || typeof o[attr] == "function") continue;
9321                 if(attr=="cls"){
9322                     el.className = o["cls"];
9323                 }else{
9324                     if(useSet) el.setAttribute(attr, o[attr]);
9325                     else el[attr] = o[attr];
9326                 }
9327             }
9328             if(o.style){
9329                 Roo.DomHelper.applyStyles(el, o.style);
9330             }
9331             return this;
9332         },
9333
9334         /**
9335          * Convenience method for constructing a KeyMap
9336          * @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:
9337          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9338          * @param {Function} fn The function to call
9339          * @param {Object} scope (optional) The scope of the function
9340          * @return {Roo.KeyMap} The KeyMap created
9341          */
9342         addKeyListener : function(key, fn, scope){
9343             var config;
9344             if(typeof key != "object" || key instanceof Array){
9345                 config = {
9346                     key: key,
9347                     fn: fn,
9348                     scope: scope
9349                 };
9350             }else{
9351                 config = {
9352                     key : key.key,
9353                     shift : key.shift,
9354                     ctrl : key.ctrl,
9355                     alt : key.alt,
9356                     fn: fn,
9357                     scope: scope
9358                 };
9359             }
9360             return new Roo.KeyMap(this, config);
9361         },
9362
9363         /**
9364          * Creates a KeyMap for this element
9365          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9366          * @return {Roo.KeyMap} The KeyMap created
9367          */
9368         addKeyMap : function(config){
9369             return new Roo.KeyMap(this, config);
9370         },
9371
9372         /**
9373          * Returns true if this element is scrollable.
9374          * @return {Boolean}
9375          */
9376          isScrollable : function(){
9377             var dom = this.dom;
9378             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9379         },
9380
9381         /**
9382          * 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().
9383          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9384          * @param {Number} value The new scroll value
9385          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9386          * @return {Element} this
9387          */
9388
9389         scrollTo : function(side, value, animate){
9390             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9391             if(!animate || !A){
9392                 this.dom[prop] = value;
9393             }else{
9394                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9395                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9396             }
9397             return this;
9398         },
9399
9400         /**
9401          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9402          * within this element's scrollable range.
9403          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9404          * @param {Number} distance How far to scroll the element in pixels
9405          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9406          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9407          * was scrolled as far as it could go.
9408          */
9409          scroll : function(direction, distance, animate){
9410              if(!this.isScrollable()){
9411                  return;
9412              }
9413              var el = this.dom;
9414              var l = el.scrollLeft, t = el.scrollTop;
9415              var w = el.scrollWidth, h = el.scrollHeight;
9416              var cw = el.clientWidth, ch = el.clientHeight;
9417              direction = direction.toLowerCase();
9418              var scrolled = false;
9419              var a = this.preanim(arguments, 2);
9420              switch(direction){
9421                  case "l":
9422                  case "left":
9423                      if(w - l > cw){
9424                          var v = Math.min(l + distance, w-cw);
9425                          this.scrollTo("left", v, a);
9426                          scrolled = true;
9427                      }
9428                      break;
9429                 case "r":
9430                 case "right":
9431                      if(l > 0){
9432                          var v = Math.max(l - distance, 0);
9433                          this.scrollTo("left", v, a);
9434                          scrolled = true;
9435                      }
9436                      break;
9437                 case "t":
9438                 case "top":
9439                 case "up":
9440                      if(t > 0){
9441                          var v = Math.max(t - distance, 0);
9442                          this.scrollTo("top", v, a);
9443                          scrolled = true;
9444                      }
9445                      break;
9446                 case "b":
9447                 case "bottom":
9448                 case "down":
9449                      if(h - t > ch){
9450                          var v = Math.min(t + distance, h-ch);
9451                          this.scrollTo("top", v, a);
9452                          scrolled = true;
9453                      }
9454                      break;
9455              }
9456              return scrolled;
9457         },
9458
9459         /**
9460          * Translates the passed page coordinates into left/top css values for this element
9461          * @param {Number/Array} x The page x or an array containing [x, y]
9462          * @param {Number} y The page y
9463          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9464          */
9465         translatePoints : function(x, y){
9466             if(typeof x == 'object' || x instanceof Array){
9467                 y = x[1]; x = x[0];
9468             }
9469             var p = this.getStyle('position');
9470             var o = this.getXY();
9471
9472             var l = parseInt(this.getStyle('left'), 10);
9473             var t = parseInt(this.getStyle('top'), 10);
9474
9475             if(isNaN(l)){
9476                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9477             }
9478             if(isNaN(t)){
9479                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9480             }
9481
9482             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9483         },
9484
9485         /**
9486          * Returns the current scroll position of the element.
9487          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9488          */
9489         getScroll : function(){
9490             var d = this.dom, doc = document;
9491             if(d == doc || d == doc.body){
9492                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9493                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9494                 return {left: l, top: t};
9495             }else{
9496                 return {left: d.scrollLeft, top: d.scrollTop};
9497             }
9498         },
9499
9500         /**
9501          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9502          * are convert to standard 6 digit hex color.
9503          * @param {String} attr The css attribute
9504          * @param {String} defaultValue The default value to use when a valid color isn't found
9505          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9506          * YUI color anims.
9507          */
9508         getColor : function(attr, defaultValue, prefix){
9509             var v = this.getStyle(attr);
9510             if(!v || v == "transparent" || v == "inherit") {
9511                 return defaultValue;
9512             }
9513             var color = typeof prefix == "undefined" ? "#" : prefix;
9514             if(v.substr(0, 4) == "rgb("){
9515                 var rvs = v.slice(4, v.length -1).split(",");
9516                 for(var i = 0; i < 3; i++){
9517                     var h = parseInt(rvs[i]).toString(16);
9518                     if(h < 16){
9519                         h = "0" + h;
9520                     }
9521                     color += h;
9522                 }
9523             } else {
9524                 if(v.substr(0, 1) == "#"){
9525                     if(v.length == 4) {
9526                         for(var i = 1; i < 4; i++){
9527                             var c = v.charAt(i);
9528                             color +=  c + c;
9529                         }
9530                     }else if(v.length == 7){
9531                         color += v.substr(1);
9532                     }
9533                 }
9534             }
9535             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9536         },
9537
9538         /**
9539          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9540          * gradient background, rounded corners and a 4-way shadow.
9541          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9542          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9543          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9544          * @return {Roo.Element} this
9545          */
9546         boxWrap : function(cls){
9547             cls = cls || 'x-box';
9548             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9549             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9550             return el;
9551         },
9552
9553         /**
9554          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9555          * @param {String} namespace The namespace in which to look for the attribute
9556          * @param {String} name The attribute name
9557          * @return {String} The attribute value
9558          */
9559         getAttributeNS : Roo.isIE ? function(ns, name){
9560             var d = this.dom;
9561             var type = typeof d[ns+":"+name];
9562             if(type != 'undefined' && type != 'unknown'){
9563                 return d[ns+":"+name];
9564             }
9565             return d[name];
9566         } : function(ns, name){
9567             var d = this.dom;
9568             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9569         },
9570         
9571         
9572         /**
9573          * Sets or Returns the value the dom attribute value
9574          * @param {String} name The attribute name
9575          * @param {String} value (optional) The value to set the attribute to
9576          * @return {String} The attribute value
9577          */
9578         attr : function(name){
9579             if (arguments.length > 1) {
9580                 this.dom.setAttribute(name, arguments[1]);
9581                 return arguments[1];
9582             }
9583             if (!this.dom.hasAttribute(name)) {
9584                 return undefined;
9585             }
9586             return this.dom.getAttribute(name);
9587         }
9588         
9589         
9590         
9591     };
9592
9593     var ep = El.prototype;
9594
9595     /**
9596      * Appends an event handler (Shorthand for addListener)
9597      * @param {String}   eventName     The type of event to append
9598      * @param {Function} fn        The method the event invokes
9599      * @param {Object} scope       (optional) The scope (this object) of the fn
9600      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9601      * @method
9602      */
9603     ep.on = ep.addListener;
9604         // backwards compat
9605     ep.mon = ep.addListener;
9606
9607     /**
9608      * Removes an event handler from this element (shorthand for removeListener)
9609      * @param {String} eventName the type of event to remove
9610      * @param {Function} fn the method the event invokes
9611      * @return {Roo.Element} this
9612      * @method
9613      */
9614     ep.un = ep.removeListener;
9615
9616     /**
9617      * true to automatically adjust width and height settings for box-model issues (default to true)
9618      */
9619     ep.autoBoxAdjust = true;
9620
9621     // private
9622     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9623
9624     // private
9625     El.addUnits = function(v, defaultUnit){
9626         if(v === "" || v == "auto"){
9627             return v;
9628         }
9629         if(v === undefined){
9630             return '';
9631         }
9632         if(typeof v == "number" || !El.unitPattern.test(v)){
9633             return v + (defaultUnit || 'px');
9634         }
9635         return v;
9636     };
9637
9638     // special markup used throughout Roo when box wrapping elements
9639     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>';
9640     /**
9641      * Visibility mode constant - Use visibility to hide element
9642      * @static
9643      * @type Number
9644      */
9645     El.VISIBILITY = 1;
9646     /**
9647      * Visibility mode constant - Use display to hide element
9648      * @static
9649      * @type Number
9650      */
9651     El.DISPLAY = 2;
9652
9653     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9654     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9655     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9656
9657
9658
9659     /**
9660      * @private
9661      */
9662     El.cache = {};
9663
9664     var docEl;
9665
9666     /**
9667      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9668      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9669      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9670      * @return {Element} The Element object
9671      * @static
9672      */
9673     El.get = function(el){
9674         var ex, elm, id;
9675         if(!el){ return null; }
9676         if(typeof el == "string"){ // element id
9677             if(!(elm = document.getElementById(el))){
9678                 return null;
9679             }
9680             if(ex = El.cache[el]){
9681                 ex.dom = elm;
9682             }else{
9683                 ex = El.cache[el] = new El(elm);
9684             }
9685             return ex;
9686         }else if(el.tagName){ // dom element
9687             if(!(id = el.id)){
9688                 id = Roo.id(el);
9689             }
9690             if(ex = El.cache[id]){
9691                 ex.dom = el;
9692             }else{
9693                 ex = El.cache[id] = new El(el);
9694             }
9695             return ex;
9696         }else if(el instanceof El){
9697             if(el != docEl){
9698                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9699                                                               // catch case where it hasn't been appended
9700                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9701             }
9702             return el;
9703         }else if(el.isComposite){
9704             return el;
9705         }else if(el instanceof Array){
9706             return El.select(el);
9707         }else if(el == document){
9708             // create a bogus element object representing the document object
9709             if(!docEl){
9710                 var f = function(){};
9711                 f.prototype = El.prototype;
9712                 docEl = new f();
9713                 docEl.dom = document;
9714             }
9715             return docEl;
9716         }
9717         return null;
9718     };
9719
9720     // private
9721     El.uncache = function(el){
9722         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9723             if(a[i]){
9724                 delete El.cache[a[i].id || a[i]];
9725             }
9726         }
9727     };
9728
9729     // private
9730     // Garbage collection - uncache elements/purge listeners on orphaned elements
9731     // so we don't hold a reference and cause the browser to retain them
9732     El.garbageCollect = function(){
9733         if(!Roo.enableGarbageCollector){
9734             clearInterval(El.collectorThread);
9735             return;
9736         }
9737         for(var eid in El.cache){
9738             var el = El.cache[eid], d = el.dom;
9739             // -------------------------------------------------------
9740             // Determining what is garbage:
9741             // -------------------------------------------------------
9742             // !d
9743             // dom node is null, definitely garbage
9744             // -------------------------------------------------------
9745             // !d.parentNode
9746             // no parentNode == direct orphan, definitely garbage
9747             // -------------------------------------------------------
9748             // !d.offsetParent && !document.getElementById(eid)
9749             // display none elements have no offsetParent so we will
9750             // also try to look it up by it's id. However, check
9751             // offsetParent first so we don't do unneeded lookups.
9752             // This enables collection of elements that are not orphans
9753             // directly, but somewhere up the line they have an orphan
9754             // parent.
9755             // -------------------------------------------------------
9756             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9757                 delete El.cache[eid];
9758                 if(d && Roo.enableListenerCollection){
9759                     E.purgeElement(d);
9760                 }
9761             }
9762         }
9763     }
9764     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9765
9766
9767     // dom is optional
9768     El.Flyweight = function(dom){
9769         this.dom = dom;
9770     };
9771     El.Flyweight.prototype = El.prototype;
9772
9773     El._flyweights = {};
9774     /**
9775      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9776      * the dom node can be overwritten by other code.
9777      * @param {String/HTMLElement} el The dom node or id
9778      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9779      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9780      * @static
9781      * @return {Element} The shared Element object
9782      */
9783     El.fly = function(el, named){
9784         named = named || '_global';
9785         el = Roo.getDom(el);
9786         if(!el){
9787             return null;
9788         }
9789         if(!El._flyweights[named]){
9790             El._flyweights[named] = new El.Flyweight();
9791         }
9792         El._flyweights[named].dom = el;
9793         return El._flyweights[named];
9794     };
9795
9796     /**
9797      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9798      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9799      * Shorthand of {@link Roo.Element#get}
9800      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9801      * @return {Element} The Element object
9802      * @member Roo
9803      * @method get
9804      */
9805     Roo.get = El.get;
9806     /**
9807      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9808      * the dom node can be overwritten by other code.
9809      * Shorthand of {@link Roo.Element#fly}
9810      * @param {String/HTMLElement} el The dom node or id
9811      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9812      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9813      * @static
9814      * @return {Element} The shared Element object
9815      * @member Roo
9816      * @method fly
9817      */
9818     Roo.fly = El.fly;
9819
9820     // speedy lookup for elements never to box adjust
9821     var noBoxAdjust = Roo.isStrict ? {
9822         select:1
9823     } : {
9824         input:1, select:1, textarea:1
9825     };
9826     if(Roo.isIE || Roo.isGecko){
9827         noBoxAdjust['button'] = 1;
9828     }
9829
9830
9831     Roo.EventManager.on(window, 'unload', function(){
9832         delete El.cache;
9833         delete El._flyweights;
9834     });
9835 })();
9836
9837
9838
9839
9840 if(Roo.DomQuery){
9841     Roo.Element.selectorFunction = Roo.DomQuery.select;
9842 }
9843
9844 Roo.Element.select = function(selector, unique, root){
9845     var els;
9846     if(typeof selector == "string"){
9847         els = Roo.Element.selectorFunction(selector, root);
9848     }else if(selector.length !== undefined){
9849         els = selector;
9850     }else{
9851         throw "Invalid selector";
9852     }
9853     if(unique === true){
9854         return new Roo.CompositeElement(els);
9855     }else{
9856         return new Roo.CompositeElementLite(els);
9857     }
9858 };
9859 /**
9860  * Selects elements based on the passed CSS selector to enable working on them as 1.
9861  * @param {String/Array} selector The CSS selector or an array of elements
9862  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9863  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9864  * @return {CompositeElementLite/CompositeElement}
9865  * @member Roo
9866  * @method select
9867  */
9868 Roo.select = Roo.Element.select;
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881
9882
9883 /*
9884  * Based on:
9885  * Ext JS Library 1.1.1
9886  * Copyright(c) 2006-2007, Ext JS, LLC.
9887  *
9888  * Originally Released Under LGPL - original licence link has changed is not relivant.
9889  *
9890  * Fork - LGPL
9891  * <script type="text/javascript">
9892  */
9893
9894
9895
9896 //Notifies Element that fx methods are available
9897 Roo.enableFx = true;
9898
9899 /**
9900  * @class Roo.Fx
9901  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9902  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9903  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9904  * Element effects to work.</p><br/>
9905  *
9906  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9907  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9908  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9909  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9910  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9911  * expected results and should be done with care.</p><br/>
9912  *
9913  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9914  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9915 <pre>
9916 Value  Description
9917 -----  -----------------------------
9918 tl     The top left corner
9919 t      The center of the top edge
9920 tr     The top right corner
9921 l      The center of the left edge
9922 r      The center of the right edge
9923 bl     The bottom left corner
9924 b      The center of the bottom edge
9925 br     The bottom right corner
9926 </pre>
9927  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9928  * below are common options that can be passed to any Fx method.</b>
9929  * @cfg {Function} callback A function called when the effect is finished
9930  * @cfg {Object} scope The scope of the effect function
9931  * @cfg {String} easing A valid Easing value for the effect
9932  * @cfg {String} afterCls A css class to apply after the effect
9933  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9934  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9935  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9936  * effects that end with the element being visually hidden, ignored otherwise)
9937  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9938  * a function which returns such a specification that will be applied to the Element after the effect finishes
9939  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9940  * @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
9941  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9942  */
9943 Roo.Fx = {
9944         /**
9945          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9946          * origin for the slide effect.  This function automatically handles wrapping the element with
9947          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9948          * Usage:
9949          *<pre><code>
9950 // default: slide the element in from the top
9951 el.slideIn();
9952
9953 // custom: slide the element in from the right with a 2-second duration
9954 el.slideIn('r', { duration: 2 });
9955
9956 // common config options shown with default values
9957 el.slideIn('t', {
9958     easing: 'easeOut',
9959     duration: .5
9960 });
9961 </code></pre>
9962          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9963          * @param {Object} options (optional) Object literal with any of the Fx config options
9964          * @return {Roo.Element} The Element
9965          */
9966     slideIn : function(anchor, o){
9967         var el = this.getFxEl();
9968         o = o || {};
9969
9970         el.queueFx(o, function(){
9971
9972             anchor = anchor || "t";
9973
9974             // fix display to visibility
9975             this.fixDisplay();
9976
9977             // restore values after effect
9978             var r = this.getFxRestore();
9979             var b = this.getBox();
9980             // fixed size for slide
9981             this.setSize(b);
9982
9983             // wrap if needed
9984             var wrap = this.fxWrap(r.pos, o, "hidden");
9985
9986             var st = this.dom.style;
9987             st.visibility = "visible";
9988             st.position = "absolute";
9989
9990             // clear out temp styles after slide and unwrap
9991             var after = function(){
9992                 el.fxUnwrap(wrap, r.pos, o);
9993                 st.width = r.width;
9994                 st.height = r.height;
9995                 el.afterFx(o);
9996             };
9997             // time to calc the positions
9998             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9999
10000             switch(anchor.toLowerCase()){
10001                 case "t":
10002                     wrap.setSize(b.width, 0);
10003                     st.left = st.bottom = "0";
10004                     a = {height: bh};
10005                 break;
10006                 case "l":
10007                     wrap.setSize(0, b.height);
10008                     st.right = st.top = "0";
10009                     a = {width: bw};
10010                 break;
10011                 case "r":
10012                     wrap.setSize(0, b.height);
10013                     wrap.setX(b.right);
10014                     st.left = st.top = "0";
10015                     a = {width: bw, points: pt};
10016                 break;
10017                 case "b":
10018                     wrap.setSize(b.width, 0);
10019                     wrap.setY(b.bottom);
10020                     st.left = st.top = "0";
10021                     a = {height: bh, points: pt};
10022                 break;
10023                 case "tl":
10024                     wrap.setSize(0, 0);
10025                     st.right = st.bottom = "0";
10026                     a = {width: bw, height: bh};
10027                 break;
10028                 case "bl":
10029                     wrap.setSize(0, 0);
10030                     wrap.setY(b.y+b.height);
10031                     st.right = st.top = "0";
10032                     a = {width: bw, height: bh, points: pt};
10033                 break;
10034                 case "br":
10035                     wrap.setSize(0, 0);
10036                     wrap.setXY([b.right, b.bottom]);
10037                     st.left = st.top = "0";
10038                     a = {width: bw, height: bh, points: pt};
10039                 break;
10040                 case "tr":
10041                     wrap.setSize(0, 0);
10042                     wrap.setX(b.x+b.width);
10043                     st.left = st.bottom = "0";
10044                     a = {width: bw, height: bh, points: pt};
10045                 break;
10046             }
10047             this.dom.style.visibility = "visible";
10048             wrap.show();
10049
10050             arguments.callee.anim = wrap.fxanim(a,
10051                 o,
10052                 'motion',
10053                 .5,
10054                 'easeOut', after);
10055         });
10056         return this;
10057     },
10058     
10059         /**
10060          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10061          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10062          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10063          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10064          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10065          * Usage:
10066          *<pre><code>
10067 // default: slide the element out to the top
10068 el.slideOut();
10069
10070 // custom: slide the element out to the right with a 2-second duration
10071 el.slideOut('r', { duration: 2 });
10072
10073 // common config options shown with default values
10074 el.slideOut('t', {
10075     easing: 'easeOut',
10076     duration: .5,
10077     remove: false,
10078     useDisplay: false
10079 });
10080 </code></pre>
10081          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10082          * @param {Object} options (optional) Object literal with any of the Fx config options
10083          * @return {Roo.Element} The Element
10084          */
10085     slideOut : function(anchor, o){
10086         var el = this.getFxEl();
10087         o = o || {};
10088
10089         el.queueFx(o, function(){
10090
10091             anchor = anchor || "t";
10092
10093             // restore values after effect
10094             var r = this.getFxRestore();
10095             
10096             var b = this.getBox();
10097             // fixed size for slide
10098             this.setSize(b);
10099
10100             // wrap if needed
10101             var wrap = this.fxWrap(r.pos, o, "visible");
10102
10103             var st = this.dom.style;
10104             st.visibility = "visible";
10105             st.position = "absolute";
10106
10107             wrap.setSize(b);
10108
10109             var after = function(){
10110                 if(o.useDisplay){
10111                     el.setDisplayed(false);
10112                 }else{
10113                     el.hide();
10114                 }
10115
10116                 el.fxUnwrap(wrap, r.pos, o);
10117
10118                 st.width = r.width;
10119                 st.height = r.height;
10120
10121                 el.afterFx(o);
10122             };
10123
10124             var a, zero = {to: 0};
10125             switch(anchor.toLowerCase()){
10126                 case "t":
10127                     st.left = st.bottom = "0";
10128                     a = {height: zero};
10129                 break;
10130                 case "l":
10131                     st.right = st.top = "0";
10132                     a = {width: zero};
10133                 break;
10134                 case "r":
10135                     st.left = st.top = "0";
10136                     a = {width: zero, points: {to:[b.right, b.y]}};
10137                 break;
10138                 case "b":
10139                     st.left = st.top = "0";
10140                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10141                 break;
10142                 case "tl":
10143                     st.right = st.bottom = "0";
10144                     a = {width: zero, height: zero};
10145                 break;
10146                 case "bl":
10147                     st.right = st.top = "0";
10148                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10149                 break;
10150                 case "br":
10151                     st.left = st.top = "0";
10152                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10153                 break;
10154                 case "tr":
10155                     st.left = st.bottom = "0";
10156                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10157                 break;
10158             }
10159
10160             arguments.callee.anim = wrap.fxanim(a,
10161                 o,
10162                 'motion',
10163                 .5,
10164                 "easeOut", after);
10165         });
10166         return this;
10167     },
10168
10169         /**
10170          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10171          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10172          * The element must be removed from the DOM using the 'remove' config option if desired.
10173          * Usage:
10174          *<pre><code>
10175 // default
10176 el.puff();
10177
10178 // common config options shown with default values
10179 el.puff({
10180     easing: 'easeOut',
10181     duration: .5,
10182     remove: false,
10183     useDisplay: false
10184 });
10185 </code></pre>
10186          * @param {Object} options (optional) Object literal with any of the Fx config options
10187          * @return {Roo.Element} The Element
10188          */
10189     puff : function(o){
10190         var el = this.getFxEl();
10191         o = o || {};
10192
10193         el.queueFx(o, function(){
10194             this.clearOpacity();
10195             this.show();
10196
10197             // restore values after effect
10198             var r = this.getFxRestore();
10199             var st = this.dom.style;
10200
10201             var after = function(){
10202                 if(o.useDisplay){
10203                     el.setDisplayed(false);
10204                 }else{
10205                     el.hide();
10206                 }
10207
10208                 el.clearOpacity();
10209
10210                 el.setPositioning(r.pos);
10211                 st.width = r.width;
10212                 st.height = r.height;
10213                 st.fontSize = '';
10214                 el.afterFx(o);
10215             };
10216
10217             var width = this.getWidth();
10218             var height = this.getHeight();
10219
10220             arguments.callee.anim = this.fxanim({
10221                     width : {to: this.adjustWidth(width * 2)},
10222                     height : {to: this.adjustHeight(height * 2)},
10223                     points : {by: [-(width * .5), -(height * .5)]},
10224                     opacity : {to: 0},
10225                     fontSize: {to:200, unit: "%"}
10226                 },
10227                 o,
10228                 'motion',
10229                 .5,
10230                 "easeOut", after);
10231         });
10232         return this;
10233     },
10234
10235         /**
10236          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10237          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10238          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10239          * Usage:
10240          *<pre><code>
10241 // default
10242 el.switchOff();
10243
10244 // all config options shown with default values
10245 el.switchOff({
10246     easing: 'easeIn',
10247     duration: .3,
10248     remove: false,
10249     useDisplay: false
10250 });
10251 </code></pre>
10252          * @param {Object} options (optional) Object literal with any of the Fx config options
10253          * @return {Roo.Element} The Element
10254          */
10255     switchOff : function(o){
10256         var el = this.getFxEl();
10257         o = o || {};
10258
10259         el.queueFx(o, function(){
10260             this.clearOpacity();
10261             this.clip();
10262
10263             // restore values after effect
10264             var r = this.getFxRestore();
10265             var st = this.dom.style;
10266
10267             var after = function(){
10268                 if(o.useDisplay){
10269                     el.setDisplayed(false);
10270                 }else{
10271                     el.hide();
10272                 }
10273
10274                 el.clearOpacity();
10275                 el.setPositioning(r.pos);
10276                 st.width = r.width;
10277                 st.height = r.height;
10278
10279                 el.afterFx(o);
10280             };
10281
10282             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10283                 this.clearOpacity();
10284                 (function(){
10285                     this.fxanim({
10286                         height:{to:1},
10287                         points:{by:[0, this.getHeight() * .5]}
10288                     }, o, 'motion', 0.3, 'easeIn', after);
10289                 }).defer(100, this);
10290             });
10291         });
10292         return this;
10293     },
10294
10295     /**
10296      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10297      * changed using the "attr" config option) and then fading back to the original color. If no original
10298      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10299      * Usage:
10300 <pre><code>
10301 // default: highlight background to yellow
10302 el.highlight();
10303
10304 // custom: highlight foreground text to blue for 2 seconds
10305 el.highlight("0000ff", { attr: 'color', duration: 2 });
10306
10307 // common config options shown with default values
10308 el.highlight("ffff9c", {
10309     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10310     endColor: (current color) or "ffffff",
10311     easing: 'easeIn',
10312     duration: 1
10313 });
10314 </code></pre>
10315      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10316      * @param {Object} options (optional) Object literal with any of the Fx config options
10317      * @return {Roo.Element} The Element
10318      */ 
10319     highlight : function(color, o){
10320         var el = this.getFxEl();
10321         o = o || {};
10322
10323         el.queueFx(o, function(){
10324             color = color || "ffff9c";
10325             attr = o.attr || "backgroundColor";
10326
10327             this.clearOpacity();
10328             this.show();
10329
10330             var origColor = this.getColor(attr);
10331             var restoreColor = this.dom.style[attr];
10332             endColor = (o.endColor || origColor) || "ffffff";
10333
10334             var after = function(){
10335                 el.dom.style[attr] = restoreColor;
10336                 el.afterFx(o);
10337             };
10338
10339             var a = {};
10340             a[attr] = {from: color, to: endColor};
10341             arguments.callee.anim = this.fxanim(a,
10342                 o,
10343                 'color',
10344                 1,
10345                 'easeIn', after);
10346         });
10347         return this;
10348     },
10349
10350    /**
10351     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10352     * Usage:
10353 <pre><code>
10354 // default: a single light blue ripple
10355 el.frame();
10356
10357 // custom: 3 red ripples lasting 3 seconds total
10358 el.frame("ff0000", 3, { duration: 3 });
10359
10360 // common config options shown with default values
10361 el.frame("C3DAF9", 1, {
10362     duration: 1 //duration of entire animation (not each individual ripple)
10363     // Note: Easing is not configurable and will be ignored if included
10364 });
10365 </code></pre>
10366     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10367     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10368     * @param {Object} options (optional) Object literal with any of the Fx config options
10369     * @return {Roo.Element} The Element
10370     */
10371     frame : function(color, count, o){
10372         var el = this.getFxEl();
10373         o = o || {};
10374
10375         el.queueFx(o, function(){
10376             color = color || "#C3DAF9";
10377             if(color.length == 6){
10378                 color = "#" + color;
10379             }
10380             count = count || 1;
10381             duration = o.duration || 1;
10382             this.show();
10383
10384             var b = this.getBox();
10385             var animFn = function(){
10386                 var proxy = this.createProxy({
10387
10388                      style:{
10389                         visbility:"hidden",
10390                         position:"absolute",
10391                         "z-index":"35000", // yee haw
10392                         border:"0px solid " + color
10393                      }
10394                   });
10395                 var scale = Roo.isBorderBox ? 2 : 1;
10396                 proxy.animate({
10397                     top:{from:b.y, to:b.y - 20},
10398                     left:{from:b.x, to:b.x - 20},
10399                     borderWidth:{from:0, to:10},
10400                     opacity:{from:1, to:0},
10401                     height:{from:b.height, to:(b.height + (20*scale))},
10402                     width:{from:b.width, to:(b.width + (20*scale))}
10403                 }, duration, function(){
10404                     proxy.remove();
10405                 });
10406                 if(--count > 0){
10407                      animFn.defer((duration/2)*1000, this);
10408                 }else{
10409                     el.afterFx(o);
10410                 }
10411             };
10412             animFn.call(this);
10413         });
10414         return this;
10415     },
10416
10417    /**
10418     * Creates a pause before any subsequent queued effects begin.  If there are
10419     * no effects queued after the pause it will have no effect.
10420     * Usage:
10421 <pre><code>
10422 el.pause(1);
10423 </code></pre>
10424     * @param {Number} seconds The length of time to pause (in seconds)
10425     * @return {Roo.Element} The Element
10426     */
10427     pause : function(seconds){
10428         var el = this.getFxEl();
10429         var o = {};
10430
10431         el.queueFx(o, function(){
10432             setTimeout(function(){
10433                 el.afterFx(o);
10434             }, seconds * 1000);
10435         });
10436         return this;
10437     },
10438
10439    /**
10440     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10441     * using the "endOpacity" config option.
10442     * Usage:
10443 <pre><code>
10444 // default: fade in from opacity 0 to 100%
10445 el.fadeIn();
10446
10447 // custom: fade in from opacity 0 to 75% over 2 seconds
10448 el.fadeIn({ endOpacity: .75, duration: 2});
10449
10450 // common config options shown with default values
10451 el.fadeIn({
10452     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10453     easing: 'easeOut',
10454     duration: .5
10455 });
10456 </code></pre>
10457     * @param {Object} options (optional) Object literal with any of the Fx config options
10458     * @return {Roo.Element} The Element
10459     */
10460     fadeIn : function(o){
10461         var el = this.getFxEl();
10462         o = o || {};
10463         el.queueFx(o, function(){
10464             this.setOpacity(0);
10465             this.fixDisplay();
10466             this.dom.style.visibility = 'visible';
10467             var to = o.endOpacity || 1;
10468             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10469                 o, null, .5, "easeOut", function(){
10470                 if(to == 1){
10471                     this.clearOpacity();
10472                 }
10473                 el.afterFx(o);
10474             });
10475         });
10476         return this;
10477     },
10478
10479    /**
10480     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10481     * using the "endOpacity" config option.
10482     * Usage:
10483 <pre><code>
10484 // default: fade out from the element's current opacity to 0
10485 el.fadeOut();
10486
10487 // custom: fade out from the element's current opacity to 25% over 2 seconds
10488 el.fadeOut({ endOpacity: .25, duration: 2});
10489
10490 // common config options shown with default values
10491 el.fadeOut({
10492     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10493     easing: 'easeOut',
10494     duration: .5
10495     remove: false,
10496     useDisplay: false
10497 });
10498 </code></pre>
10499     * @param {Object} options (optional) Object literal with any of the Fx config options
10500     * @return {Roo.Element} The Element
10501     */
10502     fadeOut : function(o){
10503         var el = this.getFxEl();
10504         o = o || {};
10505         el.queueFx(o, function(){
10506             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10507                 o, null, .5, "easeOut", function(){
10508                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10509                      this.dom.style.display = "none";
10510                 }else{
10511                      this.dom.style.visibility = "hidden";
10512                 }
10513                 this.clearOpacity();
10514                 el.afterFx(o);
10515             });
10516         });
10517         return this;
10518     },
10519
10520    /**
10521     * Animates the transition of an element's dimensions from a starting height/width
10522     * to an ending height/width.
10523     * Usage:
10524 <pre><code>
10525 // change height and width to 100x100 pixels
10526 el.scale(100, 100);
10527
10528 // common config options shown with default values.  The height and width will default to
10529 // the element's existing values if passed as null.
10530 el.scale(
10531     [element's width],
10532     [element's height], {
10533     easing: 'easeOut',
10534     duration: .35
10535 });
10536 </code></pre>
10537     * @param {Number} width  The new width (pass undefined to keep the original width)
10538     * @param {Number} height  The new height (pass undefined to keep the original height)
10539     * @param {Object} options (optional) Object literal with any of the Fx config options
10540     * @return {Roo.Element} The Element
10541     */
10542     scale : function(w, h, o){
10543         this.shift(Roo.apply({}, o, {
10544             width: w,
10545             height: h
10546         }));
10547         return this;
10548     },
10549
10550    /**
10551     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10552     * Any of these properties not specified in the config object will not be changed.  This effect 
10553     * requires that at least one new dimension, position or opacity setting must be passed in on
10554     * the config object in order for the function to have any effect.
10555     * Usage:
10556 <pre><code>
10557 // slide the element horizontally to x position 200 while changing the height and opacity
10558 el.shift({ x: 200, height: 50, opacity: .8 });
10559
10560 // common config options shown with default values.
10561 el.shift({
10562     width: [element's width],
10563     height: [element's height],
10564     x: [element's x position],
10565     y: [element's y position],
10566     opacity: [element's opacity],
10567     easing: 'easeOut',
10568     duration: .35
10569 });
10570 </code></pre>
10571     * @param {Object} options  Object literal with any of the Fx config options
10572     * @return {Roo.Element} The Element
10573     */
10574     shift : function(o){
10575         var el = this.getFxEl();
10576         o = o || {};
10577         el.queueFx(o, function(){
10578             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10579             if(w !== undefined){
10580                 a.width = {to: this.adjustWidth(w)};
10581             }
10582             if(h !== undefined){
10583                 a.height = {to: this.adjustHeight(h)};
10584             }
10585             if(x !== undefined || y !== undefined){
10586                 a.points = {to: [
10587                     x !== undefined ? x : this.getX(),
10588                     y !== undefined ? y : this.getY()
10589                 ]};
10590             }
10591             if(op !== undefined){
10592                 a.opacity = {to: op};
10593             }
10594             if(o.xy !== undefined){
10595                 a.points = {to: o.xy};
10596             }
10597             arguments.callee.anim = this.fxanim(a,
10598                 o, 'motion', .35, "easeOut", function(){
10599                 el.afterFx(o);
10600             });
10601         });
10602         return this;
10603     },
10604
10605         /**
10606          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10607          * ending point of the effect.
10608          * Usage:
10609          *<pre><code>
10610 // default: slide the element downward while fading out
10611 el.ghost();
10612
10613 // custom: slide the element out to the right with a 2-second duration
10614 el.ghost('r', { duration: 2 });
10615
10616 // common config options shown with default values
10617 el.ghost('b', {
10618     easing: 'easeOut',
10619     duration: .5
10620     remove: false,
10621     useDisplay: false
10622 });
10623 </code></pre>
10624          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10625          * @param {Object} options (optional) Object literal with any of the Fx config options
10626          * @return {Roo.Element} The Element
10627          */
10628     ghost : function(anchor, o){
10629         var el = this.getFxEl();
10630         o = o || {};
10631
10632         el.queueFx(o, function(){
10633             anchor = anchor || "b";
10634
10635             // restore values after effect
10636             var r = this.getFxRestore();
10637             var w = this.getWidth(),
10638                 h = this.getHeight();
10639
10640             var st = this.dom.style;
10641
10642             var after = function(){
10643                 if(o.useDisplay){
10644                     el.setDisplayed(false);
10645                 }else{
10646                     el.hide();
10647                 }
10648
10649                 el.clearOpacity();
10650                 el.setPositioning(r.pos);
10651                 st.width = r.width;
10652                 st.height = r.height;
10653
10654                 el.afterFx(o);
10655             };
10656
10657             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10658             switch(anchor.toLowerCase()){
10659                 case "t":
10660                     pt.by = [0, -h];
10661                 break;
10662                 case "l":
10663                     pt.by = [-w, 0];
10664                 break;
10665                 case "r":
10666                     pt.by = [w, 0];
10667                 break;
10668                 case "b":
10669                     pt.by = [0, h];
10670                 break;
10671                 case "tl":
10672                     pt.by = [-w, -h];
10673                 break;
10674                 case "bl":
10675                     pt.by = [-w, h];
10676                 break;
10677                 case "br":
10678                     pt.by = [w, h];
10679                 break;
10680                 case "tr":
10681                     pt.by = [w, -h];
10682                 break;
10683             }
10684
10685             arguments.callee.anim = this.fxanim(a,
10686                 o,
10687                 'motion',
10688                 .5,
10689                 "easeOut", after);
10690         });
10691         return this;
10692     },
10693
10694         /**
10695          * Ensures that all effects queued after syncFx is called on the element are
10696          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10697          * @return {Roo.Element} The Element
10698          */
10699     syncFx : function(){
10700         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10701             block : false,
10702             concurrent : true,
10703             stopFx : false
10704         });
10705         return this;
10706     },
10707
10708         /**
10709          * Ensures that all effects queued after sequenceFx is called on the element are
10710          * run in sequence.  This is the opposite of {@link #syncFx}.
10711          * @return {Roo.Element} The Element
10712          */
10713     sequenceFx : function(){
10714         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10715             block : false,
10716             concurrent : false,
10717             stopFx : false
10718         });
10719         return this;
10720     },
10721
10722         /* @private */
10723     nextFx : function(){
10724         var ef = this.fxQueue[0];
10725         if(ef){
10726             ef.call(this);
10727         }
10728     },
10729
10730         /**
10731          * Returns true if the element has any effects actively running or queued, else returns false.
10732          * @return {Boolean} True if element has active effects, else false
10733          */
10734     hasActiveFx : function(){
10735         return this.fxQueue && this.fxQueue[0];
10736     },
10737
10738         /**
10739          * Stops any running effects and clears the element's internal effects queue if it contains
10740          * any additional effects that haven't started yet.
10741          * @return {Roo.Element} The Element
10742          */
10743     stopFx : function(){
10744         if(this.hasActiveFx()){
10745             var cur = this.fxQueue[0];
10746             if(cur && cur.anim && cur.anim.isAnimated()){
10747                 this.fxQueue = [cur]; // clear out others
10748                 cur.anim.stop(true);
10749             }
10750         }
10751         return this;
10752     },
10753
10754         /* @private */
10755     beforeFx : function(o){
10756         if(this.hasActiveFx() && !o.concurrent){
10757            if(o.stopFx){
10758                this.stopFx();
10759                return true;
10760            }
10761            return false;
10762         }
10763         return true;
10764     },
10765
10766         /**
10767          * Returns true if the element is currently blocking so that no other effect can be queued
10768          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10769          * used to ensure that an effect initiated by a user action runs to completion prior to the
10770          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10771          * @return {Boolean} True if blocking, else false
10772          */
10773     hasFxBlock : function(){
10774         var q = this.fxQueue;
10775         return q && q[0] && q[0].block;
10776     },
10777
10778         /* @private */
10779     queueFx : function(o, fn){
10780         if(!this.fxQueue){
10781             this.fxQueue = [];
10782         }
10783         if(!this.hasFxBlock()){
10784             Roo.applyIf(o, this.fxDefaults);
10785             if(!o.concurrent){
10786                 var run = this.beforeFx(o);
10787                 fn.block = o.block;
10788                 this.fxQueue.push(fn);
10789                 if(run){
10790                     this.nextFx();
10791                 }
10792             }else{
10793                 fn.call(this);
10794             }
10795         }
10796         return this;
10797     },
10798
10799         /* @private */
10800     fxWrap : function(pos, o, vis){
10801         var wrap;
10802         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10803             var wrapXY;
10804             if(o.fixPosition){
10805                 wrapXY = this.getXY();
10806             }
10807             var div = document.createElement("div");
10808             div.style.visibility = vis;
10809             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10810             wrap.setPositioning(pos);
10811             if(wrap.getStyle("position") == "static"){
10812                 wrap.position("relative");
10813             }
10814             this.clearPositioning('auto');
10815             wrap.clip();
10816             wrap.dom.appendChild(this.dom);
10817             if(wrapXY){
10818                 wrap.setXY(wrapXY);
10819             }
10820         }
10821         return wrap;
10822     },
10823
10824         /* @private */
10825     fxUnwrap : function(wrap, pos, o){
10826         this.clearPositioning();
10827         this.setPositioning(pos);
10828         if(!o.wrap){
10829             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10830             wrap.remove();
10831         }
10832     },
10833
10834         /* @private */
10835     getFxRestore : function(){
10836         var st = this.dom.style;
10837         return {pos: this.getPositioning(), width: st.width, height : st.height};
10838     },
10839
10840         /* @private */
10841     afterFx : function(o){
10842         if(o.afterStyle){
10843             this.applyStyles(o.afterStyle);
10844         }
10845         if(o.afterCls){
10846             this.addClass(o.afterCls);
10847         }
10848         if(o.remove === true){
10849             this.remove();
10850         }
10851         Roo.callback(o.callback, o.scope, [this]);
10852         if(!o.concurrent){
10853             this.fxQueue.shift();
10854             this.nextFx();
10855         }
10856     },
10857
10858         /* @private */
10859     getFxEl : function(){ // support for composite element fx
10860         return Roo.get(this.dom);
10861     },
10862
10863         /* @private */
10864     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10865         animType = animType || 'run';
10866         opt = opt || {};
10867         var anim = Roo.lib.Anim[animType](
10868             this.dom, args,
10869             (opt.duration || defaultDur) || .35,
10870             (opt.easing || defaultEase) || 'easeOut',
10871             function(){
10872                 Roo.callback(cb, this);
10873             },
10874             this
10875         );
10876         opt.anim = anim;
10877         return anim;
10878     }
10879 };
10880
10881 // backwords compat
10882 Roo.Fx.resize = Roo.Fx.scale;
10883
10884 //When included, Roo.Fx is automatically applied to Element so that all basic
10885 //effects are available directly via the Element API
10886 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10887  * Based on:
10888  * Ext JS Library 1.1.1
10889  * Copyright(c) 2006-2007, Ext JS, LLC.
10890  *
10891  * Originally Released Under LGPL - original licence link has changed is not relivant.
10892  *
10893  * Fork - LGPL
10894  * <script type="text/javascript">
10895  */
10896
10897
10898 /**
10899  * @class Roo.CompositeElement
10900  * Standard composite class. Creates a Roo.Element for every element in the collection.
10901  * <br><br>
10902  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10903  * actions will be performed on all the elements in this collection.</b>
10904  * <br><br>
10905  * All methods return <i>this</i> and can be chained.
10906  <pre><code>
10907  var els = Roo.select("#some-el div.some-class", true);
10908  // or select directly from an existing element
10909  var el = Roo.get('some-el');
10910  el.select('div.some-class', true);
10911
10912  els.setWidth(100); // all elements become 100 width
10913  els.hide(true); // all elements fade out and hide
10914  // or
10915  els.setWidth(100).hide(true);
10916  </code></pre>
10917  */
10918 Roo.CompositeElement = function(els){
10919     this.elements = [];
10920     this.addElements(els);
10921 };
10922 Roo.CompositeElement.prototype = {
10923     isComposite: true,
10924     addElements : function(els){
10925         if(!els) return this;
10926         if(typeof els == "string"){
10927             els = Roo.Element.selectorFunction(els);
10928         }
10929         var yels = this.elements;
10930         var index = yels.length-1;
10931         for(var i = 0, len = els.length; i < len; i++) {
10932                 yels[++index] = Roo.get(els[i]);
10933         }
10934         return this;
10935     },
10936
10937     /**
10938     * Clears this composite and adds the elements returned by the passed selector.
10939     * @param {String/Array} els A string CSS selector, an array of elements or an element
10940     * @return {CompositeElement} this
10941     */
10942     fill : function(els){
10943         this.elements = [];
10944         this.add(els);
10945         return this;
10946     },
10947
10948     /**
10949     * Filters this composite to only elements that match the passed selector.
10950     * @param {String} selector A string CSS selector
10951     * @return {CompositeElement} this
10952     */
10953     filter : function(selector){
10954         var els = [];
10955         this.each(function(el){
10956             if(el.is(selector)){
10957                 els[els.length] = el.dom;
10958             }
10959         });
10960         this.fill(els);
10961         return this;
10962     },
10963
10964     invoke : function(fn, args){
10965         var els = this.elements;
10966         for(var i = 0, len = els.length; i < len; i++) {
10967                 Roo.Element.prototype[fn].apply(els[i], args);
10968         }
10969         return this;
10970     },
10971     /**
10972     * Adds elements to this composite.
10973     * @param {String/Array} els A string CSS selector, an array of elements or an element
10974     * @return {CompositeElement} this
10975     */
10976     add : function(els){
10977         if(typeof els == "string"){
10978             this.addElements(Roo.Element.selectorFunction(els));
10979         }else if(els.length !== undefined){
10980             this.addElements(els);
10981         }else{
10982             this.addElements([els]);
10983         }
10984         return this;
10985     },
10986     /**
10987     * Calls the passed function passing (el, this, index) for each element in this composite.
10988     * @param {Function} fn The function to call
10989     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10990     * @return {CompositeElement} this
10991     */
10992     each : function(fn, scope){
10993         var els = this.elements;
10994         for(var i = 0, len = els.length; i < len; i++){
10995             if(fn.call(scope || els[i], els[i], this, i) === false) {
10996                 break;
10997             }
10998         }
10999         return this;
11000     },
11001
11002     /**
11003      * Returns the Element object at the specified index
11004      * @param {Number} index
11005      * @return {Roo.Element}
11006      */
11007     item : function(index){
11008         return this.elements[index] || null;
11009     },
11010
11011     /**
11012      * Returns the first Element
11013      * @return {Roo.Element}
11014      */
11015     first : function(){
11016         return this.item(0);
11017     },
11018
11019     /**
11020      * Returns the last Element
11021      * @return {Roo.Element}
11022      */
11023     last : function(){
11024         return this.item(this.elements.length-1);
11025     },
11026
11027     /**
11028      * Returns the number of elements in this composite
11029      * @return Number
11030      */
11031     getCount : function(){
11032         return this.elements.length;
11033     },
11034
11035     /**
11036      * Returns true if this composite contains the passed element
11037      * @return Boolean
11038      */
11039     contains : function(el){
11040         return this.indexOf(el) !== -1;
11041     },
11042
11043     /**
11044      * Returns true if this composite contains the passed element
11045      * @return Boolean
11046      */
11047     indexOf : function(el){
11048         return this.elements.indexOf(Roo.get(el));
11049     },
11050
11051
11052     /**
11053     * Removes the specified element(s).
11054     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11055     * or an array of any of those.
11056     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11057     * @return {CompositeElement} this
11058     */
11059     removeElement : function(el, removeDom){
11060         if(el instanceof Array){
11061             for(var i = 0, len = el.length; i < len; i++){
11062                 this.removeElement(el[i]);
11063             }
11064             return this;
11065         }
11066         var index = typeof el == 'number' ? el : this.indexOf(el);
11067         if(index !== -1){
11068             if(removeDom){
11069                 var d = this.elements[index];
11070                 if(d.dom){
11071                     d.remove();
11072                 }else{
11073                     d.parentNode.removeChild(d);
11074                 }
11075             }
11076             this.elements.splice(index, 1);
11077         }
11078         return this;
11079     },
11080
11081     /**
11082     * Replaces the specified element with the passed element.
11083     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11084     * to replace.
11085     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11086     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11087     * @return {CompositeElement} this
11088     */
11089     replaceElement : function(el, replacement, domReplace){
11090         var index = typeof el == 'number' ? el : this.indexOf(el);
11091         if(index !== -1){
11092             if(domReplace){
11093                 this.elements[index].replaceWith(replacement);
11094             }else{
11095                 this.elements.splice(index, 1, Roo.get(replacement))
11096             }
11097         }
11098         return this;
11099     },
11100
11101     /**
11102      * Removes all elements.
11103      */
11104     clear : function(){
11105         this.elements = [];
11106     }
11107 };
11108 (function(){
11109     Roo.CompositeElement.createCall = function(proto, fnName){
11110         if(!proto[fnName]){
11111             proto[fnName] = function(){
11112                 return this.invoke(fnName, arguments);
11113             };
11114         }
11115     };
11116     for(var fnName in Roo.Element.prototype){
11117         if(typeof Roo.Element.prototype[fnName] == "function"){
11118             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11119         }
11120     };
11121 })();
11122 /*
11123  * Based on:
11124  * Ext JS Library 1.1.1
11125  * Copyright(c) 2006-2007, Ext JS, LLC.
11126  *
11127  * Originally Released Under LGPL - original licence link has changed is not relivant.
11128  *
11129  * Fork - LGPL
11130  * <script type="text/javascript">
11131  */
11132
11133 /**
11134  * @class Roo.CompositeElementLite
11135  * @extends Roo.CompositeElement
11136  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11137  <pre><code>
11138  var els = Roo.select("#some-el div.some-class");
11139  // or select directly from an existing element
11140  var el = Roo.get('some-el');
11141  el.select('div.some-class');
11142
11143  els.setWidth(100); // all elements become 100 width
11144  els.hide(true); // all elements fade out and hide
11145  // or
11146  els.setWidth(100).hide(true);
11147  </code></pre><br><br>
11148  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11149  * actions will be performed on all the elements in this collection.</b>
11150  */
11151 Roo.CompositeElementLite = function(els){
11152     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11153     this.el = new Roo.Element.Flyweight();
11154 };
11155 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11156     addElements : function(els){
11157         if(els){
11158             if(els instanceof Array){
11159                 this.elements = this.elements.concat(els);
11160             }else{
11161                 var yels = this.elements;
11162                 var index = yels.length-1;
11163                 for(var i = 0, len = els.length; i < len; i++) {
11164                     yels[++index] = els[i];
11165                 }
11166             }
11167         }
11168         return this;
11169     },
11170     invoke : function(fn, args){
11171         var els = this.elements;
11172         var el = this.el;
11173         for(var i = 0, len = els.length; i < len; i++) {
11174             el.dom = els[i];
11175                 Roo.Element.prototype[fn].apply(el, args);
11176         }
11177         return this;
11178     },
11179     /**
11180      * Returns a flyweight Element of the dom element object at the specified index
11181      * @param {Number} index
11182      * @return {Roo.Element}
11183      */
11184     item : function(index){
11185         if(!this.elements[index]){
11186             return null;
11187         }
11188         this.el.dom = this.elements[index];
11189         return this.el;
11190     },
11191
11192     // fixes scope with flyweight
11193     addListener : function(eventName, handler, scope, opt){
11194         var els = this.elements;
11195         for(var i = 0, len = els.length; i < len; i++) {
11196             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11197         }
11198         return this;
11199     },
11200
11201     /**
11202     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11203     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11204     * a reference to the dom node, use el.dom.</b>
11205     * @param {Function} fn The function to call
11206     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11207     * @return {CompositeElement} this
11208     */
11209     each : function(fn, scope){
11210         var els = this.elements;
11211         var el = this.el;
11212         for(var i = 0, len = els.length; i < len; i++){
11213             el.dom = els[i];
11214                 if(fn.call(scope || el, el, this, i) === false){
11215                 break;
11216             }
11217         }
11218         return this;
11219     },
11220
11221     indexOf : function(el){
11222         return this.elements.indexOf(Roo.getDom(el));
11223     },
11224
11225     replaceElement : function(el, replacement, domReplace){
11226         var index = typeof el == 'number' ? el : this.indexOf(el);
11227         if(index !== -1){
11228             replacement = Roo.getDom(replacement);
11229             if(domReplace){
11230                 var d = this.elements[index];
11231                 d.parentNode.insertBefore(replacement, d);
11232                 d.parentNode.removeChild(d);
11233             }
11234             this.elements.splice(index, 1, replacement);
11235         }
11236         return this;
11237     }
11238 });
11239 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11240
11241 /*
11242  * Based on:
11243  * Ext JS Library 1.1.1
11244  * Copyright(c) 2006-2007, Ext JS, LLC.
11245  *
11246  * Originally Released Under LGPL - original licence link has changed is not relivant.
11247  *
11248  * Fork - LGPL
11249  * <script type="text/javascript">
11250  */
11251
11252  
11253
11254 /**
11255  * @class Roo.data.Connection
11256  * @extends Roo.util.Observable
11257  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11258  * either to a configured URL, or to a URL specified at request time.<br><br>
11259  * <p>
11260  * Requests made by this class are asynchronous, and will return immediately. No data from
11261  * the server will be available to the statement immediately following the {@link #request} call.
11262  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11263  * <p>
11264  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11265  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11266  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11267  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11268  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11269  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11270  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11271  * standard DOM methods.
11272  * @constructor
11273  * @param {Object} config a configuration object.
11274  */
11275 Roo.data.Connection = function(config){
11276     Roo.apply(this, config);
11277     this.addEvents({
11278         /**
11279          * @event beforerequest
11280          * Fires before a network request is made to retrieve a data object.
11281          * @param {Connection} conn This Connection object.
11282          * @param {Object} options The options config object passed to the {@link #request} method.
11283          */
11284         "beforerequest" : true,
11285         /**
11286          * @event requestcomplete
11287          * Fires if the request was successfully completed.
11288          * @param {Connection} conn This Connection object.
11289          * @param {Object} response The XHR object containing the response data.
11290          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11291          * @param {Object} options The options config object passed to the {@link #request} method.
11292          */
11293         "requestcomplete" : true,
11294         /**
11295          * @event requestexception
11296          * Fires if an error HTTP status was returned from the server.
11297          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11298          * @param {Connection} conn This Connection object.
11299          * @param {Object} response The XHR object containing the response data.
11300          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11301          * @param {Object} options The options config object passed to the {@link #request} method.
11302          */
11303         "requestexception" : true
11304     });
11305     Roo.data.Connection.superclass.constructor.call(this);
11306 };
11307
11308 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11309     /**
11310      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11311      */
11312     /**
11313      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11314      * extra parameters to each request made by this object. (defaults to undefined)
11315      */
11316     /**
11317      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11318      *  to each request made by this object. (defaults to undefined)
11319      */
11320     /**
11321      * @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)
11322      */
11323     /**
11324      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11325      */
11326     timeout : 30000,
11327     /**
11328      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11329      * @type Boolean
11330      */
11331     autoAbort:false,
11332
11333     /**
11334      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11335      * @type Boolean
11336      */
11337     disableCaching: true,
11338
11339     /**
11340      * Sends an HTTP request to a remote server.
11341      * @param {Object} options An object which may contain the following properties:<ul>
11342      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11343      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11344      * request, a url encoded string or a function to call to get either.</li>
11345      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11346      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11347      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11348      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11349      * <li>options {Object} The parameter to the request call.</li>
11350      * <li>success {Boolean} True if the request succeeded.</li>
11351      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11352      * </ul></li>
11353      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11354      * The callback is passed the following parameters:<ul>
11355      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11356      * <li>options {Object} The parameter to the request call.</li>
11357      * </ul></li>
11358      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11359      * The callback is passed the following parameters:<ul>
11360      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11361      * <li>options {Object} The parameter to the request call.</li>
11362      * </ul></li>
11363      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11364      * for the callback function. Defaults to the browser window.</li>
11365      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11366      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11367      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11368      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11369      * params for the post data. Any params will be appended to the URL.</li>
11370      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11371      * </ul>
11372      * @return {Number} transactionId
11373      */
11374     request : function(o){
11375         if(this.fireEvent("beforerequest", this, o) !== false){
11376             var p = o.params;
11377
11378             if(typeof p == "function"){
11379                 p = p.call(o.scope||window, o);
11380             }
11381             if(typeof p == "object"){
11382                 p = Roo.urlEncode(o.params);
11383             }
11384             if(this.extraParams){
11385                 var extras = Roo.urlEncode(this.extraParams);
11386                 p = p ? (p + '&' + extras) : extras;
11387             }
11388
11389             var url = o.url || this.url;
11390             if(typeof url == 'function'){
11391                 url = url.call(o.scope||window, o);
11392             }
11393
11394             if(o.form){
11395                 var form = Roo.getDom(o.form);
11396                 url = url || form.action;
11397
11398                 var enctype = form.getAttribute("enctype");
11399                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11400                     return this.doFormUpload(o, p, url);
11401                 }
11402                 var f = Roo.lib.Ajax.serializeForm(form);
11403                 p = p ? (p + '&' + f) : f;
11404             }
11405
11406             var hs = o.headers;
11407             if(this.defaultHeaders){
11408                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11409                 if(!o.headers){
11410                     o.headers = hs;
11411                 }
11412             }
11413
11414             var cb = {
11415                 success: this.handleResponse,
11416                 failure: this.handleFailure,
11417                 scope: this,
11418                 argument: {options: o},
11419                 timeout : o.timeout || this.timeout
11420             };
11421
11422             var method = o.method||this.method||(p ? "POST" : "GET");
11423
11424             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11425                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11426             }
11427
11428             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11429                 if(o.autoAbort){
11430                     this.abort();
11431                 }
11432             }else if(this.autoAbort !== false){
11433                 this.abort();
11434             }
11435
11436             if((method == 'GET' && p) || o.xmlData){
11437                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11438                 p = '';
11439             }
11440             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11441             return this.transId;
11442         }else{
11443             Roo.callback(o.callback, o.scope, [o, null, null]);
11444             return null;
11445         }
11446     },
11447
11448     /**
11449      * Determine whether this object has a request outstanding.
11450      * @param {Number} transactionId (Optional) defaults to the last transaction
11451      * @return {Boolean} True if there is an outstanding request.
11452      */
11453     isLoading : function(transId){
11454         if(transId){
11455             return Roo.lib.Ajax.isCallInProgress(transId);
11456         }else{
11457             return this.transId ? true : false;
11458         }
11459     },
11460
11461     /**
11462      * Aborts any outstanding request.
11463      * @param {Number} transactionId (Optional) defaults to the last transaction
11464      */
11465     abort : function(transId){
11466         if(transId || this.isLoading()){
11467             Roo.lib.Ajax.abort(transId || this.transId);
11468         }
11469     },
11470
11471     // private
11472     handleResponse : function(response){
11473         this.transId = false;
11474         var options = response.argument.options;
11475         response.argument = options ? options.argument : null;
11476         this.fireEvent("requestcomplete", this, response, options);
11477         Roo.callback(options.success, options.scope, [response, options]);
11478         Roo.callback(options.callback, options.scope, [options, true, response]);
11479     },
11480
11481     // private
11482     handleFailure : function(response, e){
11483         this.transId = false;
11484         var options = response.argument.options;
11485         response.argument = options ? options.argument : null;
11486         this.fireEvent("requestexception", this, response, options, e);
11487         Roo.callback(options.failure, options.scope, [response, options]);
11488         Roo.callback(options.callback, options.scope, [options, false, response]);
11489     },
11490
11491     // private
11492     doFormUpload : function(o, ps, url){
11493         var id = Roo.id();
11494         var frame = document.createElement('iframe');
11495         frame.id = id;
11496         frame.name = id;
11497         frame.className = 'x-hidden';
11498         if(Roo.isIE){
11499             frame.src = Roo.SSL_SECURE_URL;
11500         }
11501         document.body.appendChild(frame);
11502
11503         if(Roo.isIE){
11504            document.frames[id].name = id;
11505         }
11506
11507         var form = Roo.getDom(o.form);
11508         form.target = id;
11509         form.method = 'POST';
11510         form.enctype = form.encoding = 'multipart/form-data';
11511         if(url){
11512             form.action = url;
11513         }
11514
11515         var hiddens, hd;
11516         if(ps){ // add dynamic params
11517             hiddens = [];
11518             ps = Roo.urlDecode(ps, false);
11519             for(var k in ps){
11520                 if(ps.hasOwnProperty(k)){
11521                     hd = document.createElement('input');
11522                     hd.type = 'hidden';
11523                     hd.name = k;
11524                     hd.value = ps[k];
11525                     form.appendChild(hd);
11526                     hiddens.push(hd);
11527                 }
11528             }
11529         }
11530
11531         function cb(){
11532             var r = {  // bogus response object
11533                 responseText : '',
11534                 responseXML : null
11535             };
11536
11537             r.argument = o ? o.argument : null;
11538
11539             try { //
11540                 var doc;
11541                 if(Roo.isIE){
11542                     doc = frame.contentWindow.document;
11543                 }else {
11544                     doc = (frame.contentDocument || window.frames[id].document);
11545                 }
11546                 if(doc && doc.body){
11547                     r.responseText = doc.body.innerHTML;
11548                 }
11549                 if(doc && doc.XMLDocument){
11550                     r.responseXML = doc.XMLDocument;
11551                 }else {
11552                     r.responseXML = doc;
11553                 }
11554             }
11555             catch(e) {
11556                 // ignore
11557             }
11558
11559             Roo.EventManager.removeListener(frame, 'load', cb, this);
11560
11561             this.fireEvent("requestcomplete", this, r, o);
11562             Roo.callback(o.success, o.scope, [r, o]);
11563             Roo.callback(o.callback, o.scope, [o, true, r]);
11564
11565             setTimeout(function(){document.body.removeChild(frame);}, 100);
11566         }
11567
11568         Roo.EventManager.on(frame, 'load', cb, this);
11569         form.submit();
11570
11571         if(hiddens){ // remove dynamic params
11572             for(var i = 0, len = hiddens.length; i < len; i++){
11573                 form.removeChild(hiddens[i]);
11574             }
11575         }
11576     }
11577 });
11578 /*
11579  * Based on:
11580  * Ext JS Library 1.1.1
11581  * Copyright(c) 2006-2007, Ext JS, LLC.
11582  *
11583  * Originally Released Under LGPL - original licence link has changed is not relivant.
11584  *
11585  * Fork - LGPL
11586  * <script type="text/javascript">
11587  */
11588  
11589 /**
11590  * Global Ajax request class.
11591  * 
11592  * @class Roo.Ajax
11593  * @extends Roo.data.Connection
11594  * @static
11595  * 
11596  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11597  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11598  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11599  * @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)
11600  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11601  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11602  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11603  */
11604 Roo.Ajax = new Roo.data.Connection({
11605     // fix up the docs
11606     /**
11607      * @scope Roo.Ajax
11608      * @type {Boolear} 
11609      */
11610     autoAbort : false,
11611
11612     /**
11613      * Serialize the passed form into a url encoded string
11614      * @scope Roo.Ajax
11615      * @param {String/HTMLElement} form
11616      * @return {String}
11617      */
11618     serializeForm : function(form){
11619         return Roo.lib.Ajax.serializeForm(form);
11620     }
11621 });/*
11622  * Based on:
11623  * Ext JS Library 1.1.1
11624  * Copyright(c) 2006-2007, Ext JS, LLC.
11625  *
11626  * Originally Released Under LGPL - original licence link has changed is not relivant.
11627  *
11628  * Fork - LGPL
11629  * <script type="text/javascript">
11630  */
11631
11632  
11633 /**
11634  * @class Roo.UpdateManager
11635  * @extends Roo.util.Observable
11636  * Provides AJAX-style update for Element object.<br><br>
11637  * Usage:<br>
11638  * <pre><code>
11639  * // Get it from a Roo.Element object
11640  * var el = Roo.get("foo");
11641  * var mgr = el.getUpdateManager();
11642  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11643  * ...
11644  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11645  * <br>
11646  * // or directly (returns the same UpdateManager instance)
11647  * var mgr = new Roo.UpdateManager("myElementId");
11648  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11649  * mgr.on("update", myFcnNeedsToKnow);
11650  * <br>
11651    // short handed call directly from the element object
11652    Roo.get("foo").load({
11653         url: "bar.php",
11654         scripts:true,
11655         params: "for=bar",
11656         text: "Loading Foo..."
11657    });
11658  * </code></pre>
11659  * @constructor
11660  * Create new UpdateManager directly.
11661  * @param {String/HTMLElement/Roo.Element} el The element to update
11662  * @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).
11663  */
11664 Roo.UpdateManager = function(el, forceNew){
11665     el = Roo.get(el);
11666     if(!forceNew && el.updateManager){
11667         return el.updateManager;
11668     }
11669     /**
11670      * The Element object
11671      * @type Roo.Element
11672      */
11673     this.el = el;
11674     /**
11675      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11676      * @type String
11677      */
11678     this.defaultUrl = null;
11679
11680     this.addEvents({
11681         /**
11682          * @event beforeupdate
11683          * Fired before an update is made, return false from your handler and the update is cancelled.
11684          * @param {Roo.Element} el
11685          * @param {String/Object/Function} url
11686          * @param {String/Object} params
11687          */
11688         "beforeupdate": true,
11689         /**
11690          * @event update
11691          * Fired after successful update is made.
11692          * @param {Roo.Element} el
11693          * @param {Object} oResponseObject The response Object
11694          */
11695         "update": true,
11696         /**
11697          * @event failure
11698          * Fired on update failure.
11699          * @param {Roo.Element} el
11700          * @param {Object} oResponseObject The response Object
11701          */
11702         "failure": true
11703     });
11704     var d = Roo.UpdateManager.defaults;
11705     /**
11706      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11707      * @type String
11708      */
11709     this.sslBlankUrl = d.sslBlankUrl;
11710     /**
11711      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11712      * @type Boolean
11713      */
11714     this.disableCaching = d.disableCaching;
11715     /**
11716      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11717      * @type String
11718      */
11719     this.indicatorText = d.indicatorText;
11720     /**
11721      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11722      * @type String
11723      */
11724     this.showLoadIndicator = d.showLoadIndicator;
11725     /**
11726      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11727      * @type Number
11728      */
11729     this.timeout = d.timeout;
11730
11731     /**
11732      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11733      * @type Boolean
11734      */
11735     this.loadScripts = d.loadScripts;
11736
11737     /**
11738      * Transaction object of current executing transaction
11739      */
11740     this.transaction = null;
11741
11742     /**
11743      * @private
11744      */
11745     this.autoRefreshProcId = null;
11746     /**
11747      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11748      * @type Function
11749      */
11750     this.refreshDelegate = this.refresh.createDelegate(this);
11751     /**
11752      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11753      * @type Function
11754      */
11755     this.updateDelegate = this.update.createDelegate(this);
11756     /**
11757      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11758      * @type Function
11759      */
11760     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11761     /**
11762      * @private
11763      */
11764     this.successDelegate = this.processSuccess.createDelegate(this);
11765     /**
11766      * @private
11767      */
11768     this.failureDelegate = this.processFailure.createDelegate(this);
11769
11770     if(!this.renderer){
11771      /**
11772       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11773       */
11774     this.renderer = new Roo.UpdateManager.BasicRenderer();
11775     }
11776     
11777     Roo.UpdateManager.superclass.constructor.call(this);
11778 };
11779
11780 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11781     /**
11782      * Get the Element this UpdateManager is bound to
11783      * @return {Roo.Element} The element
11784      */
11785     getEl : function(){
11786         return this.el;
11787     },
11788     /**
11789      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11790      * @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:
11791 <pre><code>
11792 um.update({<br/>
11793     url: "your-url.php",<br/>
11794     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11795     callback: yourFunction,<br/>
11796     scope: yourObject, //(optional scope)  <br/>
11797     discardUrl: false, <br/>
11798     nocache: false,<br/>
11799     text: "Loading...",<br/>
11800     timeout: 30,<br/>
11801     scripts: false<br/>
11802 });
11803 </code></pre>
11804      * The only required property is url. The optional properties nocache, text and scripts
11805      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11806      * @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}
11807      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11808      * @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.
11809      */
11810     update : function(url, params, callback, discardUrl){
11811         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11812             var method = this.method,
11813                 cfg;
11814             if(typeof url == "object"){ // must be config object
11815                 cfg = url;
11816                 url = cfg.url;
11817                 params = params || cfg.params;
11818                 callback = callback || cfg.callback;
11819                 discardUrl = discardUrl || cfg.discardUrl;
11820                 if(callback && cfg.scope){
11821                     callback = callback.createDelegate(cfg.scope);
11822                 }
11823                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11824                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11825                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11826                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11827                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11828             }
11829             this.showLoading();
11830             if(!discardUrl){
11831                 this.defaultUrl = url;
11832             }
11833             if(typeof url == "function"){
11834                 url = url.call(this);
11835             }
11836
11837             method = method || (params ? "POST" : "GET");
11838             if(method == "GET"){
11839                 url = this.prepareUrl(url);
11840             }
11841
11842             var o = Roo.apply(cfg ||{}, {
11843                 url : url,
11844                 params: params,
11845                 success: this.successDelegate,
11846                 failure: this.failureDelegate,
11847                 callback: undefined,
11848                 timeout: (this.timeout*1000),
11849                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11850             });
11851             Roo.log("updated manager called with timeout of " + o.timeout);
11852             this.transaction = Roo.Ajax.request(o);
11853         }
11854     },
11855
11856     /**
11857      * 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.
11858      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11859      * @param {String/HTMLElement} form The form Id or form element
11860      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11861      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11862      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11863      */
11864     formUpdate : function(form, url, reset, callback){
11865         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11866             if(typeof url == "function"){
11867                 url = url.call(this);
11868             }
11869             form = Roo.getDom(form);
11870             this.transaction = Roo.Ajax.request({
11871                 form: form,
11872                 url:url,
11873                 success: this.successDelegate,
11874                 failure: this.failureDelegate,
11875                 timeout: (this.timeout*1000),
11876                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11877             });
11878             this.showLoading.defer(1, this);
11879         }
11880     },
11881
11882     /**
11883      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11884      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11885      */
11886     refresh : function(callback){
11887         if(this.defaultUrl == null){
11888             return;
11889         }
11890         this.update(this.defaultUrl, null, callback, true);
11891     },
11892
11893     /**
11894      * Set this element to auto refresh.
11895      * @param {Number} interval How often to update (in seconds).
11896      * @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)
11897      * @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}
11898      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11899      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11900      */
11901     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11902         if(refreshNow){
11903             this.update(url || this.defaultUrl, params, callback, true);
11904         }
11905         if(this.autoRefreshProcId){
11906             clearInterval(this.autoRefreshProcId);
11907         }
11908         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11909     },
11910
11911     /**
11912      * Stop auto refresh on this element.
11913      */
11914      stopAutoRefresh : function(){
11915         if(this.autoRefreshProcId){
11916             clearInterval(this.autoRefreshProcId);
11917             delete this.autoRefreshProcId;
11918         }
11919     },
11920
11921     isAutoRefreshing : function(){
11922        return this.autoRefreshProcId ? true : false;
11923     },
11924     /**
11925      * Called to update the element to "Loading" state. Override to perform custom action.
11926      */
11927     showLoading : function(){
11928         if(this.showLoadIndicator){
11929             this.el.update(this.indicatorText);
11930         }
11931     },
11932
11933     /**
11934      * Adds unique parameter to query string if disableCaching = true
11935      * @private
11936      */
11937     prepareUrl : function(url){
11938         if(this.disableCaching){
11939             var append = "_dc=" + (new Date().getTime());
11940             if(url.indexOf("?") !== -1){
11941                 url += "&" + append;
11942             }else{
11943                 url += "?" + append;
11944             }
11945         }
11946         return url;
11947     },
11948
11949     /**
11950      * @private
11951      */
11952     processSuccess : function(response){
11953         this.transaction = null;
11954         if(response.argument.form && response.argument.reset){
11955             try{ // put in try/catch since some older FF releases had problems with this
11956                 response.argument.form.reset();
11957             }catch(e){}
11958         }
11959         if(this.loadScripts){
11960             this.renderer.render(this.el, response, this,
11961                 this.updateComplete.createDelegate(this, [response]));
11962         }else{
11963             this.renderer.render(this.el, response, this);
11964             this.updateComplete(response);
11965         }
11966     },
11967
11968     updateComplete : function(response){
11969         this.fireEvent("update", this.el, response);
11970         if(typeof response.argument.callback == "function"){
11971             response.argument.callback(this.el, true, response);
11972         }
11973     },
11974
11975     /**
11976      * @private
11977      */
11978     processFailure : function(response){
11979         this.transaction = null;
11980         this.fireEvent("failure", this.el, response);
11981         if(typeof response.argument.callback == "function"){
11982             response.argument.callback(this.el, false, response);
11983         }
11984     },
11985
11986     /**
11987      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11988      * @param {Object} renderer The object implementing the render() method
11989      */
11990     setRenderer : function(renderer){
11991         this.renderer = renderer;
11992     },
11993
11994     getRenderer : function(){
11995        return this.renderer;
11996     },
11997
11998     /**
11999      * Set the defaultUrl used for updates
12000      * @param {String/Function} defaultUrl The url or a function to call to get the url
12001      */
12002     setDefaultUrl : function(defaultUrl){
12003         this.defaultUrl = defaultUrl;
12004     },
12005
12006     /**
12007      * Aborts the executing transaction
12008      */
12009     abort : function(){
12010         if(this.transaction){
12011             Roo.Ajax.abort(this.transaction);
12012         }
12013     },
12014
12015     /**
12016      * Returns true if an update is in progress
12017      * @return {Boolean}
12018      */
12019     isUpdating : function(){
12020         if(this.transaction){
12021             return Roo.Ajax.isLoading(this.transaction);
12022         }
12023         return false;
12024     }
12025 });
12026
12027 /**
12028  * @class Roo.UpdateManager.defaults
12029  * @static (not really - but it helps the doc tool)
12030  * The defaults collection enables customizing the default properties of UpdateManager
12031  */
12032    Roo.UpdateManager.defaults = {
12033        /**
12034          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12035          * @type Number
12036          */
12037          timeout : 30,
12038
12039          /**
12040          * True to process scripts by default (Defaults to false).
12041          * @type Boolean
12042          */
12043         loadScripts : false,
12044
12045         /**
12046         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12047         * @type String
12048         */
12049         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12050         /**
12051          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12052          * @type Boolean
12053          */
12054         disableCaching : false,
12055         /**
12056          * Whether to show indicatorText when loading (Defaults to true).
12057          * @type Boolean
12058          */
12059         showLoadIndicator : true,
12060         /**
12061          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12062          * @type String
12063          */
12064         indicatorText : '<div class="loading-indicator">Loading...</div>'
12065    };
12066
12067 /**
12068  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12069  *Usage:
12070  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12071  * @param {String/HTMLElement/Roo.Element} el The element to update
12072  * @param {String} url The url
12073  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12074  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12075  * @static
12076  * @deprecated
12077  * @member Roo.UpdateManager
12078  */
12079 Roo.UpdateManager.updateElement = function(el, url, params, options){
12080     var um = Roo.get(el, true).getUpdateManager();
12081     Roo.apply(um, options);
12082     um.update(url, params, options ? options.callback : null);
12083 };
12084 // alias for backwards compat
12085 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12086 /**
12087  * @class Roo.UpdateManager.BasicRenderer
12088  * Default Content renderer. Updates the elements innerHTML with the responseText.
12089  */
12090 Roo.UpdateManager.BasicRenderer = function(){};
12091
12092 Roo.UpdateManager.BasicRenderer.prototype = {
12093     /**
12094      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12095      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12096      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12097      * @param {Roo.Element} el The element being rendered
12098      * @param {Object} response The YUI Connect response object
12099      * @param {UpdateManager} updateManager The calling update manager
12100      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12101      */
12102      render : function(el, response, updateManager, callback){
12103         el.update(response.responseText, updateManager.loadScripts, callback);
12104     }
12105 };
12106 /*
12107  * Based on:
12108  * Roo JS
12109  * (c)) Alan Knowles
12110  * Licence : LGPL
12111  */
12112
12113
12114 /**
12115  * @class Roo.DomTemplate
12116  * @extends Roo.Template
12117  * An effort at a dom based template engine..
12118  *
12119  * Similar to XTemplate, except it uses dom parsing to create the template..
12120  *
12121  * Supported features:
12122  *
12123  *  Tags:
12124
12125 <pre><code>
12126       {a_variable} - output encoded.
12127       {a_variable.format:("Y-m-d")} - call a method on the variable
12128       {a_variable:raw} - unencoded output
12129       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12130       {a_variable:this.method_on_template(...)} - call a method on the template object.
12131  
12132 </code></pre>
12133  *  The tpl tag:
12134 <pre><code>
12135         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12136         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12137         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12138         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12139   
12140 </code></pre>
12141  *      
12142  */
12143 Roo.DomTemplate = function()
12144 {
12145      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12146      if (this.html) {
12147         this.compile();
12148      }
12149 };
12150
12151
12152 Roo.extend(Roo.DomTemplate, Roo.Template, {
12153     /**
12154      * id counter for sub templates.
12155      */
12156     id : 0,
12157     /**
12158      * flag to indicate if dom parser is inside a pre,
12159      * it will strip whitespace if not.
12160      */
12161     inPre : false,
12162     
12163     /**
12164      * The various sub templates
12165      */
12166     tpls : false,
12167     
12168     
12169     
12170     /**
12171      *
12172      * basic tag replacing syntax
12173      * WORD:WORD()
12174      *
12175      * // you can fake an object call by doing this
12176      *  x.t:(test,tesT) 
12177      * 
12178      */
12179     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12180     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12181     
12182     iterChild : function (node, method) {
12183         
12184         var oldPre = this.inPre;
12185         if (node.tagName == 'PRE') {
12186             this.inPre = true;
12187         }
12188         for( var i = 0; i < node.childNodes.length; i++) {
12189             method.call(this, node.childNodes[i]);
12190         }
12191         this.inPre = oldPre;
12192     },
12193     
12194     
12195     
12196     /**
12197      * compile the template
12198      *
12199      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12200      *
12201      */
12202     compile: function()
12203     {
12204         var s = this.html;
12205         
12206         // covert the html into DOM...
12207         var doc = false;
12208         var div =false;
12209         try {
12210             doc = document.implementation.createHTMLDocument("");
12211             doc.documentElement.innerHTML =   this.html  ;
12212             div = doc.documentElement;
12213         } catch (e) {
12214             // old IE... - nasty -- it causes all sorts of issues.. with
12215             // images getting pulled from server..
12216             div = document.createElement('div');
12217             div.innerHTML = this.html;
12218         }
12219         //doc.documentElement.innerHTML = htmlBody
12220          
12221         
12222         
12223         this.tpls = [];
12224         var _t = this;
12225         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12226         
12227         var tpls = this.tpls;
12228         
12229         // create a top level template from the snippet..
12230         
12231         //Roo.log(div.innerHTML);
12232         
12233         var tpl = {
12234             uid : 'master',
12235             id : this.id++,
12236             attr : false,
12237             value : false,
12238             body : div.innerHTML,
12239             
12240             forCall : false,
12241             execCall : false,
12242             dom : div,
12243             isTop : true
12244             
12245         };
12246         tpls.unshift(tpl);
12247         
12248         
12249         // compile them...
12250         this.tpls = [];
12251         Roo.each(tpls, function(tp){
12252             this.compileTpl(tp);
12253             this.tpls[tp.id] = tp;
12254         }, this);
12255         
12256         this.master = tpls[0];
12257         return this;
12258         
12259         
12260     },
12261     
12262     compileNode : function(node, istop) {
12263         // test for
12264         //Roo.log(node);
12265         
12266         
12267         // skip anything not a tag..
12268         if (node.nodeType != 1) {
12269             if (node.nodeType == 3 && !this.inPre) {
12270                 // reduce white space..
12271                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12272                 
12273             }
12274             return;
12275         }
12276         
12277         var tpl = {
12278             uid : false,
12279             id : false,
12280             attr : false,
12281             value : false,
12282             body : '',
12283             
12284             forCall : false,
12285             execCall : false,
12286             dom : false,
12287             isTop : istop
12288             
12289             
12290         };
12291         
12292         
12293         switch(true) {
12294             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12295             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12296             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12297             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12298             // no default..
12299         }
12300         
12301         
12302         if (!tpl.attr) {
12303             // just itterate children..
12304             this.iterChild(node,this.compileNode);
12305             return;
12306         }
12307         tpl.uid = this.id++;
12308         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12309         node.removeAttribute('roo-'+ tpl.attr);
12310         if (tpl.attr != 'name') {
12311             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12312             node.parentNode.replaceChild(placeholder,  node);
12313         } else {
12314             
12315             var placeholder =  document.createElement('span');
12316             placeholder.className = 'roo-tpl-' + tpl.value;
12317             node.parentNode.replaceChild(placeholder,  node);
12318         }
12319         
12320         // parent now sees '{domtplXXXX}
12321         this.iterChild(node,this.compileNode);
12322         
12323         // we should now have node body...
12324         var div = document.createElement('div');
12325         div.appendChild(node);
12326         tpl.dom = node;
12327         // this has the unfortunate side effect of converting tagged attributes
12328         // eg. href="{...}" into %7C...%7D
12329         // this has been fixed by searching for those combo's although it's a bit hacky..
12330         
12331         
12332         tpl.body = div.innerHTML;
12333         
12334         
12335          
12336         tpl.id = tpl.uid;
12337         switch(tpl.attr) {
12338             case 'for' :
12339                 switch (tpl.value) {
12340                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12341                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12342                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12343                 }
12344                 break;
12345             
12346             case 'exec':
12347                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12348                 break;
12349             
12350             case 'if':     
12351                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12352                 break;
12353             
12354             case 'name':
12355                 tpl.id  = tpl.value; // replace non characters???
12356                 break;
12357             
12358         }
12359         
12360         
12361         this.tpls.push(tpl);
12362         
12363         
12364         
12365     },
12366     
12367     
12368     
12369     
12370     /**
12371      * Compile a segment of the template into a 'sub-template'
12372      *
12373      * 
12374      * 
12375      *
12376      */
12377     compileTpl : function(tpl)
12378     {
12379         var fm = Roo.util.Format;
12380         var useF = this.disableFormats !== true;
12381         
12382         var sep = Roo.isGecko ? "+\n" : ",\n";
12383         
12384         var undef = function(str) {
12385             Roo.debug && Roo.log("Property not found :"  + str);
12386             return '';
12387         };
12388           
12389         //Roo.log(tpl.body);
12390         
12391         
12392         
12393         var fn = function(m, lbrace, name, format, args)
12394         {
12395             //Roo.log("ARGS");
12396             //Roo.log(arguments);
12397             args = args ? args.replace(/\\'/g,"'") : args;
12398             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12399             if (typeof(format) == 'undefined') {
12400                 format =  'htmlEncode'; 
12401             }
12402             if (format == 'raw' ) {
12403                 format = false;
12404             }
12405             
12406             if(name.substr(0, 6) == 'domtpl'){
12407                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12408             }
12409             
12410             // build an array of options to determine if value is undefined..
12411             
12412             // basically get 'xxxx.yyyy' then do
12413             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12414             //    (function () { Roo.log("Property not found"); return ''; })() :
12415             //    ......
12416             
12417             var udef_ar = [];
12418             var lookfor = '';
12419             Roo.each(name.split('.'), function(st) {
12420                 lookfor += (lookfor.length ? '.': '') + st;
12421                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12422             });
12423             
12424             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12425             
12426             
12427             if(format && useF){
12428                 
12429                 args = args ? ',' + args : "";
12430                  
12431                 if(format.substr(0, 5) != "this."){
12432                     format = "fm." + format + '(';
12433                 }else{
12434                     format = 'this.call("'+ format.substr(5) + '", ';
12435                     args = ", values";
12436                 }
12437                 
12438                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12439             }
12440              
12441             if (args && args.length) {
12442                 // called with xxyx.yuu:(test,test)
12443                 // change to ()
12444                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12445             }
12446             // raw.. - :raw modifier..
12447             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12448             
12449         };
12450         var body;
12451         // branched to use + in gecko and [].join() in others
12452         if(Roo.isGecko){
12453             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12454                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12455                     "';};};";
12456         }else{
12457             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12458             body.push(tpl.body.replace(/(\r\n|\n)/g,
12459                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12460             body.push("'].join('');};};");
12461             body = body.join('');
12462         }
12463         
12464         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12465        
12466         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12467         eval(body);
12468         
12469         return this;
12470     },
12471      
12472     /**
12473      * same as applyTemplate, except it's done to one of the subTemplates
12474      * when using named templates, you can do:
12475      *
12476      * var str = pl.applySubTemplate('your-name', values);
12477      *
12478      * 
12479      * @param {Number} id of the template
12480      * @param {Object} values to apply to template
12481      * @param {Object} parent (normaly the instance of this object)
12482      */
12483     applySubTemplate : function(id, values, parent)
12484     {
12485         
12486         
12487         var t = this.tpls[id];
12488         
12489         
12490         try { 
12491             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12492                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12493                 return '';
12494             }
12495         } catch(e) {
12496             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12497             Roo.log(values);
12498           
12499             return '';
12500         }
12501         try { 
12502             
12503             if(t.execCall && t.execCall.call(this, values, parent)){
12504                 return '';
12505             }
12506         } catch(e) {
12507             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12508             Roo.log(values);
12509             return '';
12510         }
12511         
12512         try {
12513             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12514             parent = t.target ? values : parent;
12515             if(t.forCall && vs instanceof Array){
12516                 var buf = [];
12517                 for(var i = 0, len = vs.length; i < len; i++){
12518                     try {
12519                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12520                     } catch (e) {
12521                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12522                         Roo.log(e.body);
12523                         //Roo.log(t.compiled);
12524                         Roo.log(vs[i]);
12525                     }   
12526                 }
12527                 return buf.join('');
12528             }
12529         } catch (e) {
12530             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12531             Roo.log(values);
12532             return '';
12533         }
12534         try {
12535             return t.compiled.call(this, vs, parent);
12536         } catch (e) {
12537             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12538             Roo.log(e.body);
12539             //Roo.log(t.compiled);
12540             Roo.log(values);
12541             return '';
12542         }
12543     },
12544
12545    
12546
12547     applyTemplate : function(values){
12548         return this.master.compiled.call(this, values, {});
12549         //var s = this.subs;
12550     },
12551
12552     apply : function(){
12553         return this.applyTemplate.apply(this, arguments);
12554     }
12555
12556  });
12557
12558 Roo.DomTemplate.from = function(el){
12559     el = Roo.getDom(el);
12560     return new Roo.Domtemplate(el.value || el.innerHTML);
12561 };/*
12562  * Based on:
12563  * Ext JS Library 1.1.1
12564  * Copyright(c) 2006-2007, Ext JS, LLC.
12565  *
12566  * Originally Released Under LGPL - original licence link has changed is not relivant.
12567  *
12568  * Fork - LGPL
12569  * <script type="text/javascript">
12570  */
12571
12572 /**
12573  * @class Roo.util.DelayedTask
12574  * Provides a convenient method of performing setTimeout where a new
12575  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12576  * You can use this class to buffer
12577  * the keypress events for a certain number of milliseconds, and perform only if they stop
12578  * for that amount of time.
12579  * @constructor The parameters to this constructor serve as defaults and are not required.
12580  * @param {Function} fn (optional) The default function to timeout
12581  * @param {Object} scope (optional) The default scope of that timeout
12582  * @param {Array} args (optional) The default Array of arguments
12583  */
12584 Roo.util.DelayedTask = function(fn, scope, args){
12585     var id = null, d, t;
12586
12587     var call = function(){
12588         var now = new Date().getTime();
12589         if(now - t >= d){
12590             clearInterval(id);
12591             id = null;
12592             fn.apply(scope, args || []);
12593         }
12594     };
12595     /**
12596      * Cancels any pending timeout and queues a new one
12597      * @param {Number} delay The milliseconds to delay
12598      * @param {Function} newFn (optional) Overrides function passed to constructor
12599      * @param {Object} newScope (optional) Overrides scope passed to constructor
12600      * @param {Array} newArgs (optional) Overrides args passed to constructor
12601      */
12602     this.delay = function(delay, newFn, newScope, newArgs){
12603         if(id && delay != d){
12604             this.cancel();
12605         }
12606         d = delay;
12607         t = new Date().getTime();
12608         fn = newFn || fn;
12609         scope = newScope || scope;
12610         args = newArgs || args;
12611         if(!id){
12612             id = setInterval(call, d);
12613         }
12614     };
12615
12616     /**
12617      * Cancel the last queued timeout
12618      */
12619     this.cancel = function(){
12620         if(id){
12621             clearInterval(id);
12622             id = null;
12623         }
12624     };
12625 };/*
12626  * Based on:
12627  * Ext JS Library 1.1.1
12628  * Copyright(c) 2006-2007, Ext JS, LLC.
12629  *
12630  * Originally Released Under LGPL - original licence link has changed is not relivant.
12631  *
12632  * Fork - LGPL
12633  * <script type="text/javascript">
12634  */
12635  
12636  
12637 Roo.util.TaskRunner = function(interval){
12638     interval = interval || 10;
12639     var tasks = [], removeQueue = [];
12640     var id = 0;
12641     var running = false;
12642
12643     var stopThread = function(){
12644         running = false;
12645         clearInterval(id);
12646         id = 0;
12647     };
12648
12649     var startThread = function(){
12650         if(!running){
12651             running = true;
12652             id = setInterval(runTasks, interval);
12653         }
12654     };
12655
12656     var removeTask = function(task){
12657         removeQueue.push(task);
12658         if(task.onStop){
12659             task.onStop();
12660         }
12661     };
12662
12663     var runTasks = function(){
12664         if(removeQueue.length > 0){
12665             for(var i = 0, len = removeQueue.length; i < len; i++){
12666                 tasks.remove(removeQueue[i]);
12667             }
12668             removeQueue = [];
12669             if(tasks.length < 1){
12670                 stopThread();
12671                 return;
12672             }
12673         }
12674         var now = new Date().getTime();
12675         for(var i = 0, len = tasks.length; i < len; ++i){
12676             var t = tasks[i];
12677             var itime = now - t.taskRunTime;
12678             if(t.interval <= itime){
12679                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12680                 t.taskRunTime = now;
12681                 if(rt === false || t.taskRunCount === t.repeat){
12682                     removeTask(t);
12683                     return;
12684                 }
12685             }
12686             if(t.duration && t.duration <= (now - t.taskStartTime)){
12687                 removeTask(t);
12688             }
12689         }
12690     };
12691
12692     /**
12693      * Queues a new task.
12694      * @param {Object} task
12695      */
12696     this.start = function(task){
12697         tasks.push(task);
12698         task.taskStartTime = new Date().getTime();
12699         task.taskRunTime = 0;
12700         task.taskRunCount = 0;
12701         startThread();
12702         return task;
12703     };
12704
12705     this.stop = function(task){
12706         removeTask(task);
12707         return task;
12708     };
12709
12710     this.stopAll = function(){
12711         stopThread();
12712         for(var i = 0, len = tasks.length; i < len; i++){
12713             if(tasks[i].onStop){
12714                 tasks[i].onStop();
12715             }
12716         }
12717         tasks = [];
12718         removeQueue = [];
12719     };
12720 };
12721
12722 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12723  * Based on:
12724  * Ext JS Library 1.1.1
12725  * Copyright(c) 2006-2007, Ext JS, LLC.
12726  *
12727  * Originally Released Under LGPL - original licence link has changed is not relivant.
12728  *
12729  * Fork - LGPL
12730  * <script type="text/javascript">
12731  */
12732
12733  
12734 /**
12735  * @class Roo.util.MixedCollection
12736  * @extends Roo.util.Observable
12737  * A Collection class that maintains both numeric indexes and keys and exposes events.
12738  * @constructor
12739  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12740  * collection (defaults to false)
12741  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12742  * and return the key value for that item.  This is used when available to look up the key on items that
12743  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12744  * equivalent to providing an implementation for the {@link #getKey} method.
12745  */
12746 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12747     this.items = [];
12748     this.map = {};
12749     this.keys = [];
12750     this.length = 0;
12751     this.addEvents({
12752         /**
12753          * @event clear
12754          * Fires when the collection is cleared.
12755          */
12756         "clear" : true,
12757         /**
12758          * @event add
12759          * Fires when an item is added to the collection.
12760          * @param {Number} index The index at which the item was added.
12761          * @param {Object} o The item added.
12762          * @param {String} key The key associated with the added item.
12763          */
12764         "add" : true,
12765         /**
12766          * @event replace
12767          * Fires when an item is replaced in the collection.
12768          * @param {String} key he key associated with the new added.
12769          * @param {Object} old The item being replaced.
12770          * @param {Object} new The new item.
12771          */
12772         "replace" : true,
12773         /**
12774          * @event remove
12775          * Fires when an item is removed from the collection.
12776          * @param {Object} o The item being removed.
12777          * @param {String} key (optional) The key associated with the removed item.
12778          */
12779         "remove" : true,
12780         "sort" : true
12781     });
12782     this.allowFunctions = allowFunctions === true;
12783     if(keyFn){
12784         this.getKey = keyFn;
12785     }
12786     Roo.util.MixedCollection.superclass.constructor.call(this);
12787 };
12788
12789 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12790     allowFunctions : false,
12791     
12792 /**
12793  * Adds an item to the collection.
12794  * @param {String} key The key to associate with the item
12795  * @param {Object} o The item to add.
12796  * @return {Object} The item added.
12797  */
12798     add : function(key, o){
12799         if(arguments.length == 1){
12800             o = arguments[0];
12801             key = this.getKey(o);
12802         }
12803         if(typeof key == "undefined" || key === null){
12804             this.length++;
12805             this.items.push(o);
12806             this.keys.push(null);
12807         }else{
12808             var old = this.map[key];
12809             if(old){
12810                 return this.replace(key, o);
12811             }
12812             this.length++;
12813             this.items.push(o);
12814             this.map[key] = o;
12815             this.keys.push(key);
12816         }
12817         this.fireEvent("add", this.length-1, o, key);
12818         return o;
12819     },
12820        
12821 /**
12822   * MixedCollection has a generic way to fetch keys if you implement getKey.
12823 <pre><code>
12824 // normal way
12825 var mc = new Roo.util.MixedCollection();
12826 mc.add(someEl.dom.id, someEl);
12827 mc.add(otherEl.dom.id, otherEl);
12828 //and so on
12829
12830 // using getKey
12831 var mc = new Roo.util.MixedCollection();
12832 mc.getKey = function(el){
12833    return el.dom.id;
12834 };
12835 mc.add(someEl);
12836 mc.add(otherEl);
12837
12838 // or via the constructor
12839 var mc = new Roo.util.MixedCollection(false, function(el){
12840    return el.dom.id;
12841 });
12842 mc.add(someEl);
12843 mc.add(otherEl);
12844 </code></pre>
12845  * @param o {Object} The item for which to find the key.
12846  * @return {Object} The key for the passed item.
12847  */
12848     getKey : function(o){
12849          return o.id; 
12850     },
12851    
12852 /**
12853  * Replaces an item in the collection.
12854  * @param {String} key The key associated with the item to replace, or the item to replace.
12855  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12856  * @return {Object}  The new item.
12857  */
12858     replace : function(key, o){
12859         if(arguments.length == 1){
12860             o = arguments[0];
12861             key = this.getKey(o);
12862         }
12863         var old = this.item(key);
12864         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12865              return this.add(key, o);
12866         }
12867         var index = this.indexOfKey(key);
12868         this.items[index] = o;
12869         this.map[key] = o;
12870         this.fireEvent("replace", key, old, o);
12871         return o;
12872     },
12873    
12874 /**
12875  * Adds all elements of an Array or an Object to the collection.
12876  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12877  * an Array of values, each of which are added to the collection.
12878  */
12879     addAll : function(objs){
12880         if(arguments.length > 1 || objs instanceof Array){
12881             var args = arguments.length > 1 ? arguments : objs;
12882             for(var i = 0, len = args.length; i < len; i++){
12883                 this.add(args[i]);
12884             }
12885         }else{
12886             for(var key in objs){
12887                 if(this.allowFunctions || typeof objs[key] != "function"){
12888                     this.add(key, objs[key]);
12889                 }
12890             }
12891         }
12892     },
12893    
12894 /**
12895  * Executes the specified function once for every item in the collection, passing each
12896  * item as the first and only parameter. returning false from the function will stop the iteration.
12897  * @param {Function} fn The function to execute for each item.
12898  * @param {Object} scope (optional) The scope in which to execute the function.
12899  */
12900     each : function(fn, scope){
12901         var items = [].concat(this.items); // each safe for removal
12902         for(var i = 0, len = items.length; i < len; i++){
12903             if(fn.call(scope || items[i], items[i], i, len) === false){
12904                 break;
12905             }
12906         }
12907     },
12908    
12909 /**
12910  * Executes the specified function once for every key in the collection, passing each
12911  * key, and its associated item as the first two parameters.
12912  * @param {Function} fn The function to execute for each item.
12913  * @param {Object} scope (optional) The scope in which to execute the function.
12914  */
12915     eachKey : function(fn, scope){
12916         for(var i = 0, len = this.keys.length; i < len; i++){
12917             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12918         }
12919     },
12920    
12921 /**
12922  * Returns the first item in the collection which elicits a true return value from the
12923  * passed selection function.
12924  * @param {Function} fn The selection function to execute for each item.
12925  * @param {Object} scope (optional) The scope in which to execute the function.
12926  * @return {Object} The first item in the collection which returned true from the selection function.
12927  */
12928     find : function(fn, scope){
12929         for(var i = 0, len = this.items.length; i < len; i++){
12930             if(fn.call(scope || window, this.items[i], this.keys[i])){
12931                 return this.items[i];
12932             }
12933         }
12934         return null;
12935     },
12936    
12937 /**
12938  * Inserts an item at the specified index in the collection.
12939  * @param {Number} index The index to insert the item at.
12940  * @param {String} key The key to associate with the new item, or the item itself.
12941  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12942  * @return {Object} The item inserted.
12943  */
12944     insert : function(index, key, o){
12945         if(arguments.length == 2){
12946             o = arguments[1];
12947             key = this.getKey(o);
12948         }
12949         if(index >= this.length){
12950             return this.add(key, o);
12951         }
12952         this.length++;
12953         this.items.splice(index, 0, o);
12954         if(typeof key != "undefined" && key != null){
12955             this.map[key] = o;
12956         }
12957         this.keys.splice(index, 0, key);
12958         this.fireEvent("add", index, o, key);
12959         return o;
12960     },
12961    
12962 /**
12963  * Removed an item from the collection.
12964  * @param {Object} o The item to remove.
12965  * @return {Object} The item removed.
12966  */
12967     remove : function(o){
12968         return this.removeAt(this.indexOf(o));
12969     },
12970    
12971 /**
12972  * Remove an item from a specified index in the collection.
12973  * @param {Number} index The index within the collection of the item to remove.
12974  */
12975     removeAt : function(index){
12976         if(index < this.length && index >= 0){
12977             this.length--;
12978             var o = this.items[index];
12979             this.items.splice(index, 1);
12980             var key = this.keys[index];
12981             if(typeof key != "undefined"){
12982                 delete this.map[key];
12983             }
12984             this.keys.splice(index, 1);
12985             this.fireEvent("remove", o, key);
12986         }
12987     },
12988    
12989 /**
12990  * Removed an item associated with the passed key fom the collection.
12991  * @param {String} key The key of the item to remove.
12992  */
12993     removeKey : function(key){
12994         return this.removeAt(this.indexOfKey(key));
12995     },
12996    
12997 /**
12998  * Returns the number of items in the collection.
12999  * @return {Number} the number of items in the collection.
13000  */
13001     getCount : function(){
13002         return this.length; 
13003     },
13004    
13005 /**
13006  * Returns index within the collection of the passed Object.
13007  * @param {Object} o The item to find the index of.
13008  * @return {Number} index of the item.
13009  */
13010     indexOf : function(o){
13011         if(!this.items.indexOf){
13012             for(var i = 0, len = this.items.length; i < len; i++){
13013                 if(this.items[i] == o) return i;
13014             }
13015             return -1;
13016         }else{
13017             return this.items.indexOf(o);
13018         }
13019     },
13020    
13021 /**
13022  * Returns index within the collection of the passed key.
13023  * @param {String} key The key to find the index of.
13024  * @return {Number} index of the key.
13025  */
13026     indexOfKey : function(key){
13027         if(!this.keys.indexOf){
13028             for(var i = 0, len = this.keys.length; i < len; i++){
13029                 if(this.keys[i] == key) return i;
13030             }
13031             return -1;
13032         }else{
13033             return this.keys.indexOf(key);
13034         }
13035     },
13036    
13037 /**
13038  * Returns the item associated with the passed key OR index. Key has priority over index.
13039  * @param {String/Number} key The key or index of the item.
13040  * @return {Object} The item associated with the passed key.
13041  */
13042     item : function(key){
13043         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13044         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13045     },
13046     
13047 /**
13048  * Returns the item at the specified index.
13049  * @param {Number} index The index of the item.
13050  * @return {Object}
13051  */
13052     itemAt : function(index){
13053         return this.items[index];
13054     },
13055     
13056 /**
13057  * Returns the item associated with the passed key.
13058  * @param {String/Number} key The key of the item.
13059  * @return {Object} The item associated with the passed key.
13060  */
13061     key : function(key){
13062         return this.map[key];
13063     },
13064    
13065 /**
13066  * Returns true if the collection contains the passed Object as an item.
13067  * @param {Object} o  The Object to look for in the collection.
13068  * @return {Boolean} True if the collection contains the Object as an item.
13069  */
13070     contains : function(o){
13071         return this.indexOf(o) != -1;
13072     },
13073    
13074 /**
13075  * Returns true if the collection contains the passed Object as a key.
13076  * @param {String} key The key to look for in the collection.
13077  * @return {Boolean} True if the collection contains the Object as a key.
13078  */
13079     containsKey : function(key){
13080         return typeof this.map[key] != "undefined";
13081     },
13082    
13083 /**
13084  * Removes all items from the collection.
13085  */
13086     clear : function(){
13087         this.length = 0;
13088         this.items = [];
13089         this.keys = [];
13090         this.map = {};
13091         this.fireEvent("clear");
13092     },
13093    
13094 /**
13095  * Returns the first item in the collection.
13096  * @return {Object} the first item in the collection..
13097  */
13098     first : function(){
13099         return this.items[0]; 
13100     },
13101    
13102 /**
13103  * Returns the last item in the collection.
13104  * @return {Object} the last item in the collection..
13105  */
13106     last : function(){
13107         return this.items[this.length-1];   
13108     },
13109     
13110     _sort : function(property, dir, fn){
13111         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13112         fn = fn || function(a, b){
13113             return a-b;
13114         };
13115         var c = [], k = this.keys, items = this.items;
13116         for(var i = 0, len = items.length; i < len; i++){
13117             c[c.length] = {key: k[i], value: items[i], index: i};
13118         }
13119         c.sort(function(a, b){
13120             var v = fn(a[property], b[property]) * dsc;
13121             if(v == 0){
13122                 v = (a.index < b.index ? -1 : 1);
13123             }
13124             return v;
13125         });
13126         for(var i = 0, len = c.length; i < len; i++){
13127             items[i] = c[i].value;
13128             k[i] = c[i].key;
13129         }
13130         this.fireEvent("sort", this);
13131     },
13132     
13133     /**
13134      * Sorts this collection with the passed comparison function
13135      * @param {String} direction (optional) "ASC" or "DESC"
13136      * @param {Function} fn (optional) comparison function
13137      */
13138     sort : function(dir, fn){
13139         this._sort("value", dir, fn);
13140     },
13141     
13142     /**
13143      * Sorts this collection by keys
13144      * @param {String} direction (optional) "ASC" or "DESC"
13145      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13146      */
13147     keySort : function(dir, fn){
13148         this._sort("key", dir, fn || function(a, b){
13149             return String(a).toUpperCase()-String(b).toUpperCase();
13150         });
13151     },
13152     
13153     /**
13154      * Returns a range of items in this collection
13155      * @param {Number} startIndex (optional) defaults to 0
13156      * @param {Number} endIndex (optional) default to the last item
13157      * @return {Array} An array of items
13158      */
13159     getRange : function(start, end){
13160         var items = this.items;
13161         if(items.length < 1){
13162             return [];
13163         }
13164         start = start || 0;
13165         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13166         var r = [];
13167         if(start <= end){
13168             for(var i = start; i <= end; i++) {
13169                     r[r.length] = items[i];
13170             }
13171         }else{
13172             for(var i = start; i >= end; i--) {
13173                     r[r.length] = items[i];
13174             }
13175         }
13176         return r;
13177     },
13178         
13179     /**
13180      * Filter the <i>objects</i> in this collection by a specific property. 
13181      * Returns a new collection that has been filtered.
13182      * @param {String} property A property on your objects
13183      * @param {String/RegExp} value Either string that the property values 
13184      * should start with or a RegExp to test against the property
13185      * @return {MixedCollection} The new filtered collection
13186      */
13187     filter : function(property, value){
13188         if(!value.exec){ // not a regex
13189             value = String(value);
13190             if(value.length == 0){
13191                 return this.clone();
13192             }
13193             value = new RegExp("^" + Roo.escapeRe(value), "i");
13194         }
13195         return this.filterBy(function(o){
13196             return o && value.test(o[property]);
13197         });
13198         },
13199     
13200     /**
13201      * Filter by a function. * Returns a new collection that has been filtered.
13202      * The passed function will be called with each 
13203      * object in the collection. If the function returns true, the value is included 
13204      * otherwise it is filtered.
13205      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13206      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13207      * @return {MixedCollection} The new filtered collection
13208      */
13209     filterBy : function(fn, scope){
13210         var r = new Roo.util.MixedCollection();
13211         r.getKey = this.getKey;
13212         var k = this.keys, it = this.items;
13213         for(var i = 0, len = it.length; i < len; i++){
13214             if(fn.call(scope||this, it[i], k[i])){
13215                                 r.add(k[i], it[i]);
13216                         }
13217         }
13218         return r;
13219     },
13220     
13221     /**
13222      * Creates a duplicate of this collection
13223      * @return {MixedCollection}
13224      */
13225     clone : function(){
13226         var r = new Roo.util.MixedCollection();
13227         var k = this.keys, it = this.items;
13228         for(var i = 0, len = it.length; i < len; i++){
13229             r.add(k[i], it[i]);
13230         }
13231         r.getKey = this.getKey;
13232         return r;
13233     }
13234 });
13235 /**
13236  * Returns the item associated with the passed key or index.
13237  * @method
13238  * @param {String/Number} key The key or index of the item.
13239  * @return {Object} The item associated with the passed key.
13240  */
13241 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13242  * Based on:
13243  * Ext JS Library 1.1.1
13244  * Copyright(c) 2006-2007, Ext JS, LLC.
13245  *
13246  * Originally Released Under LGPL - original licence link has changed is not relivant.
13247  *
13248  * Fork - LGPL
13249  * <script type="text/javascript">
13250  */
13251 /**
13252  * @class Roo.util.JSON
13253  * Modified version of Douglas Crockford"s json.js that doesn"t
13254  * mess with the Object prototype 
13255  * http://www.json.org/js.html
13256  * @singleton
13257  */
13258 Roo.util.JSON = new (function(){
13259     var useHasOwn = {}.hasOwnProperty ? true : false;
13260     
13261     // crashes Safari in some instances
13262     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13263     
13264     var pad = function(n) {
13265         return n < 10 ? "0" + n : n;
13266     };
13267     
13268     var m = {
13269         "\b": '\\b',
13270         "\t": '\\t',
13271         "\n": '\\n',
13272         "\f": '\\f',
13273         "\r": '\\r',
13274         '"' : '\\"',
13275         "\\": '\\\\'
13276     };
13277
13278     var encodeString = function(s){
13279         if (/["\\\x00-\x1f]/.test(s)) {
13280             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13281                 var c = m[b];
13282                 if(c){
13283                     return c;
13284                 }
13285                 c = b.charCodeAt();
13286                 return "\\u00" +
13287                     Math.floor(c / 16).toString(16) +
13288                     (c % 16).toString(16);
13289             }) + '"';
13290         }
13291         return '"' + s + '"';
13292     };
13293     
13294     var encodeArray = function(o){
13295         var a = ["["], b, i, l = o.length, v;
13296             for (i = 0; i < l; i += 1) {
13297                 v = o[i];
13298                 switch (typeof v) {
13299                     case "undefined":
13300                     case "function":
13301                     case "unknown":
13302                         break;
13303                     default:
13304                         if (b) {
13305                             a.push(',');
13306                         }
13307                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13308                         b = true;
13309                 }
13310             }
13311             a.push("]");
13312             return a.join("");
13313     };
13314     
13315     var encodeDate = function(o){
13316         return '"' + o.getFullYear() + "-" +
13317                 pad(o.getMonth() + 1) + "-" +
13318                 pad(o.getDate()) + "T" +
13319                 pad(o.getHours()) + ":" +
13320                 pad(o.getMinutes()) + ":" +
13321                 pad(o.getSeconds()) + '"';
13322     };
13323     
13324     /**
13325      * Encodes an Object, Array or other value
13326      * @param {Mixed} o The variable to encode
13327      * @return {String} The JSON string
13328      */
13329     this.encode = function(o)
13330     {
13331         // should this be extended to fully wrap stringify..
13332         
13333         if(typeof o == "undefined" || o === null){
13334             return "null";
13335         }else if(o instanceof Array){
13336             return encodeArray(o);
13337         }else if(o instanceof Date){
13338             return encodeDate(o);
13339         }else if(typeof o == "string"){
13340             return encodeString(o);
13341         }else if(typeof o == "number"){
13342             return isFinite(o) ? String(o) : "null";
13343         }else if(typeof o == "boolean"){
13344             return String(o);
13345         }else {
13346             var a = ["{"], b, i, v;
13347             for (i in o) {
13348                 if(!useHasOwn || o.hasOwnProperty(i)) {
13349                     v = o[i];
13350                     switch (typeof v) {
13351                     case "undefined":
13352                     case "function":
13353                     case "unknown":
13354                         break;
13355                     default:
13356                         if(b){
13357                             a.push(',');
13358                         }
13359                         a.push(this.encode(i), ":",
13360                                 v === null ? "null" : this.encode(v));
13361                         b = true;
13362                     }
13363                 }
13364             }
13365             a.push("}");
13366             return a.join("");
13367         }
13368     };
13369     
13370     /**
13371      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13372      * @param {String} json The JSON string
13373      * @return {Object} The resulting object
13374      */
13375     this.decode = function(json){
13376         
13377         return  /** eval:var:json */ eval("(" + json + ')');
13378     };
13379 })();
13380 /** 
13381  * Shorthand for {@link Roo.util.JSON#encode}
13382  * @member Roo encode 
13383  * @method */
13384 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13385 /** 
13386  * Shorthand for {@link Roo.util.JSON#decode}
13387  * @member Roo decode 
13388  * @method */
13389 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13390 /*
13391  * Based on:
13392  * Ext JS Library 1.1.1
13393  * Copyright(c) 2006-2007, Ext JS, LLC.
13394  *
13395  * Originally Released Under LGPL - original licence link has changed is not relivant.
13396  *
13397  * Fork - LGPL
13398  * <script type="text/javascript">
13399  */
13400  
13401 /**
13402  * @class Roo.util.Format
13403  * Reusable data formatting functions
13404  * @singleton
13405  */
13406 Roo.util.Format = function(){
13407     var trimRe = /^\s+|\s+$/g;
13408     return {
13409         /**
13410          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13411          * @param {String} value The string to truncate
13412          * @param {Number} length The maximum length to allow before truncating
13413          * @return {String} The converted text
13414          */
13415         ellipsis : function(value, len){
13416             if(value && value.length > len){
13417                 return value.substr(0, len-3)+"...";
13418             }
13419             return value;
13420         },
13421
13422         /**
13423          * Checks a reference and converts it to empty string if it is undefined
13424          * @param {Mixed} value Reference to check
13425          * @return {Mixed} Empty string if converted, otherwise the original value
13426          */
13427         undef : function(value){
13428             return typeof value != "undefined" ? value : "";
13429         },
13430
13431         /**
13432          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13433          * @param {String} value The string to encode
13434          * @return {String} The encoded text
13435          */
13436         htmlEncode : function(value){
13437             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13438         },
13439
13440         /**
13441          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13442          * @param {String} value The string to decode
13443          * @return {String} The decoded text
13444          */
13445         htmlDecode : function(value){
13446             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13447         },
13448
13449         /**
13450          * Trims any whitespace from either side of a string
13451          * @param {String} value The text to trim
13452          * @return {String} The trimmed text
13453          */
13454         trim : function(value){
13455             return String(value).replace(trimRe, "");
13456         },
13457
13458         /**
13459          * Returns a substring from within an original string
13460          * @param {String} value The original text
13461          * @param {Number} start The start index of the substring
13462          * @param {Number} length The length of the substring
13463          * @return {String} The substring
13464          */
13465         substr : function(value, start, length){
13466             return String(value).substr(start, length);
13467         },
13468
13469         /**
13470          * Converts a string to all lower case letters
13471          * @param {String} value The text to convert
13472          * @return {String} The converted text
13473          */
13474         lowercase : function(value){
13475             return String(value).toLowerCase();
13476         },
13477
13478         /**
13479          * Converts a string to all upper case letters
13480          * @param {String} value The text to convert
13481          * @return {String} The converted text
13482          */
13483         uppercase : function(value){
13484             return String(value).toUpperCase();
13485         },
13486
13487         /**
13488          * Converts the first character only of a string to upper case
13489          * @param {String} value The text to convert
13490          * @return {String} The converted text
13491          */
13492         capitalize : function(value){
13493             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13494         },
13495
13496         // private
13497         call : function(value, fn){
13498             if(arguments.length > 2){
13499                 var args = Array.prototype.slice.call(arguments, 2);
13500                 args.unshift(value);
13501                  
13502                 return /** eval:var:value */  eval(fn).apply(window, args);
13503             }else{
13504                 /** eval:var:value */
13505                 return /** eval:var:value */ eval(fn).call(window, value);
13506             }
13507         },
13508
13509        
13510         /**
13511          * safer version of Math.toFixed..??/
13512          * @param {Number/String} value The numeric value to format
13513          * @param {Number/String} value Decimal places 
13514          * @return {String} The formatted currency string
13515          */
13516         toFixed : function(v, n)
13517         {
13518             // why not use to fixed - precision is buggered???
13519             if (!n) {
13520                 return Math.round(v-0);
13521             }
13522             var fact = Math.pow(10,n+1);
13523             v = (Math.round((v-0)*fact))/fact;
13524             var z = (''+fact).substring(2);
13525             if (v == Math.floor(v)) {
13526                 return Math.floor(v) + '.' + z;
13527             }
13528             
13529             // now just padd decimals..
13530             var ps = String(v).split('.');
13531             var fd = (ps[1] + z);
13532             var r = fd.substring(0,n); 
13533             var rm = fd.substring(n); 
13534             if (rm < 5) {
13535                 return ps[0] + '.' + r;
13536             }
13537             r*=1; // turn it into a number;
13538             r++;
13539             if (String(r).length != n) {
13540                 ps[0]*=1;
13541                 ps[0]++;
13542                 r = String(r).substring(1); // chop the end off.
13543             }
13544             
13545             return ps[0] + '.' + r;
13546              
13547         },
13548         
13549         /**
13550          * Format a number as US currency
13551          * @param {Number/String} value The numeric value to format
13552          * @return {String} The formatted currency string
13553          */
13554         usMoney : function(v){
13555             return '$' + Roo.util.Format.number(v);
13556         },
13557         
13558         /**
13559          * Format a number
13560          * eventually this should probably emulate php's number_format
13561          * @param {Number/String} value The numeric value to format
13562          * @param {Number} decimals number of decimal places
13563          * @return {String} The formatted currency string
13564          */
13565         number : function(v,decimals)
13566         {
13567             // multiply and round.
13568             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13569             var mul = Math.pow(10, decimals);
13570             var zero = String(mul).substring(1);
13571             v = (Math.round((v-0)*mul))/mul;
13572             
13573             // if it's '0' number.. then
13574             
13575             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13576             v = String(v);
13577             var ps = v.split('.');
13578             var whole = ps[0];
13579             
13580             
13581             var r = /(\d+)(\d{3})/;
13582             // add comma's
13583             while (r.test(whole)) {
13584                 whole = whole.replace(r, '$1' + ',' + '$2');
13585             }
13586             
13587             
13588             var sub = ps[1] ?
13589                     // has decimals..
13590                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13591                     // does not have decimals
13592                     (decimals ? ('.' + zero) : '');
13593             
13594             
13595             return whole + sub ;
13596         },
13597         
13598         /**
13599          * Parse a value into a formatted date using the specified format pattern.
13600          * @param {Mixed} value The value to format
13601          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13602          * @return {String} The formatted date string
13603          */
13604         date : function(v, format){
13605             if(!v){
13606                 return "";
13607             }
13608             if(!(v instanceof Date)){
13609                 v = new Date(Date.parse(v));
13610             }
13611             return v.dateFormat(format || Roo.util.Format.defaults.date);
13612         },
13613
13614         /**
13615          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13616          * @param {String} format Any valid date format string
13617          * @return {Function} The date formatting function
13618          */
13619         dateRenderer : function(format){
13620             return function(v){
13621                 return Roo.util.Format.date(v, format);  
13622             };
13623         },
13624
13625         // private
13626         stripTagsRE : /<\/?[^>]+>/gi,
13627         
13628         /**
13629          * Strips all HTML tags
13630          * @param {Mixed} value The text from which to strip tags
13631          * @return {String} The stripped text
13632          */
13633         stripTags : function(v){
13634             return !v ? v : String(v).replace(this.stripTagsRE, "");
13635         }
13636     };
13637 }();
13638 Roo.util.Format.defaults = {
13639     date : 'd/M/Y'
13640 };/*
13641  * Based on:
13642  * Ext JS Library 1.1.1
13643  * Copyright(c) 2006-2007, Ext JS, LLC.
13644  *
13645  * Originally Released Under LGPL - original licence link has changed is not relivant.
13646  *
13647  * Fork - LGPL
13648  * <script type="text/javascript">
13649  */
13650
13651
13652  
13653
13654 /**
13655  * @class Roo.MasterTemplate
13656  * @extends Roo.Template
13657  * Provides a template that can have child templates. The syntax is:
13658 <pre><code>
13659 var t = new Roo.MasterTemplate(
13660         '&lt;select name="{name}"&gt;',
13661                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13662         '&lt;/select&gt;'
13663 );
13664 t.add('options', {value: 'foo', text: 'bar'});
13665 // or you can add multiple child elements in one shot
13666 t.addAll('options', [
13667     {value: 'foo', text: 'bar'},
13668     {value: 'foo2', text: 'bar2'},
13669     {value: 'foo3', text: 'bar3'}
13670 ]);
13671 // then append, applying the master template values
13672 t.append('my-form', {name: 'my-select'});
13673 </code></pre>
13674 * A name attribute for the child template is not required if you have only one child
13675 * template or you want to refer to them by index.
13676  */
13677 Roo.MasterTemplate = function(){
13678     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13679     this.originalHtml = this.html;
13680     var st = {};
13681     var m, re = this.subTemplateRe;
13682     re.lastIndex = 0;
13683     var subIndex = 0;
13684     while(m = re.exec(this.html)){
13685         var name = m[1], content = m[2];
13686         st[subIndex] = {
13687             name: name,
13688             index: subIndex,
13689             buffer: [],
13690             tpl : new Roo.Template(content)
13691         };
13692         if(name){
13693             st[name] = st[subIndex];
13694         }
13695         st[subIndex].tpl.compile();
13696         st[subIndex].tpl.call = this.call.createDelegate(this);
13697         subIndex++;
13698     }
13699     this.subCount = subIndex;
13700     this.subs = st;
13701 };
13702 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13703     /**
13704     * The regular expression used to match sub templates
13705     * @type RegExp
13706     * @property
13707     */
13708     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13709
13710     /**
13711      * Applies the passed values to a child template.
13712      * @param {String/Number} name (optional) The name or index of the child template
13713      * @param {Array/Object} values The values to be applied to the template
13714      * @return {MasterTemplate} this
13715      */
13716      add : function(name, values){
13717         if(arguments.length == 1){
13718             values = arguments[0];
13719             name = 0;
13720         }
13721         var s = this.subs[name];
13722         s.buffer[s.buffer.length] = s.tpl.apply(values);
13723         return this;
13724     },
13725
13726     /**
13727      * Applies all the passed values to a child template.
13728      * @param {String/Number} name (optional) The name or index of the child template
13729      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13730      * @param {Boolean} reset (optional) True to reset the template first
13731      * @return {MasterTemplate} this
13732      */
13733     fill : function(name, values, reset){
13734         var a = arguments;
13735         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13736             values = a[0];
13737             name = 0;
13738             reset = a[1];
13739         }
13740         if(reset){
13741             this.reset();
13742         }
13743         for(var i = 0, len = values.length; i < len; i++){
13744             this.add(name, values[i]);
13745         }
13746         return this;
13747     },
13748
13749     /**
13750      * Resets the template for reuse
13751      * @return {MasterTemplate} this
13752      */
13753      reset : function(){
13754         var s = this.subs;
13755         for(var i = 0; i < this.subCount; i++){
13756             s[i].buffer = [];
13757         }
13758         return this;
13759     },
13760
13761     applyTemplate : function(values){
13762         var s = this.subs;
13763         var replaceIndex = -1;
13764         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13765             return s[++replaceIndex].buffer.join("");
13766         });
13767         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13768     },
13769
13770     apply : function(){
13771         return this.applyTemplate.apply(this, arguments);
13772     },
13773
13774     compile : function(){return this;}
13775 });
13776
13777 /**
13778  * Alias for fill().
13779  * @method
13780  */
13781 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13782  /**
13783  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13784  * var tpl = Roo.MasterTemplate.from('element-id');
13785  * @param {String/HTMLElement} el
13786  * @param {Object} config
13787  * @static
13788  */
13789 Roo.MasterTemplate.from = function(el, config){
13790     el = Roo.getDom(el);
13791     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13792 };/*
13793  * Based on:
13794  * Ext JS Library 1.1.1
13795  * Copyright(c) 2006-2007, Ext JS, LLC.
13796  *
13797  * Originally Released Under LGPL - original licence link has changed is not relivant.
13798  *
13799  * Fork - LGPL
13800  * <script type="text/javascript">
13801  */
13802
13803  
13804 /**
13805  * @class Roo.util.CSS
13806  * Utility class for manipulating CSS rules
13807  * @singleton
13808  */
13809 Roo.util.CSS = function(){
13810         var rules = null;
13811         var doc = document;
13812
13813     var camelRe = /(-[a-z])/gi;
13814     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13815
13816    return {
13817    /**
13818     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13819     * tag and appended to the HEAD of the document.
13820     * @param {String|Object} cssText The text containing the css rules
13821     * @param {String} id An id to add to the stylesheet for later removal
13822     * @return {StyleSheet}
13823     */
13824     createStyleSheet : function(cssText, id){
13825         var ss;
13826         var head = doc.getElementsByTagName("head")[0];
13827         var nrules = doc.createElement("style");
13828         nrules.setAttribute("type", "text/css");
13829         if(id){
13830             nrules.setAttribute("id", id);
13831         }
13832         if (typeof(cssText) != 'string') {
13833             // support object maps..
13834             // not sure if this a good idea.. 
13835             // perhaps it should be merged with the general css handling
13836             // and handle js style props.
13837             var cssTextNew = [];
13838             for(var n in cssText) {
13839                 var citems = [];
13840                 for(var k in cssText[n]) {
13841                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13842                 }
13843                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13844                 
13845             }
13846             cssText = cssTextNew.join("\n");
13847             
13848         }
13849        
13850        
13851        if(Roo.isIE){
13852            head.appendChild(nrules);
13853            ss = nrules.styleSheet;
13854            ss.cssText = cssText;
13855        }else{
13856            try{
13857                 nrules.appendChild(doc.createTextNode(cssText));
13858            }catch(e){
13859                nrules.cssText = cssText; 
13860            }
13861            head.appendChild(nrules);
13862            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13863        }
13864        this.cacheStyleSheet(ss);
13865        return ss;
13866    },
13867
13868    /**
13869     * Removes a style or link tag by id
13870     * @param {String} id The id of the tag
13871     */
13872    removeStyleSheet : function(id){
13873        var existing = doc.getElementById(id);
13874        if(existing){
13875            existing.parentNode.removeChild(existing);
13876        }
13877    },
13878
13879    /**
13880     * Dynamically swaps an existing stylesheet reference for a new one
13881     * @param {String} id The id of an existing link tag to remove
13882     * @param {String} url The href of the new stylesheet to include
13883     */
13884    swapStyleSheet : function(id, url){
13885        this.removeStyleSheet(id);
13886        var ss = doc.createElement("link");
13887        ss.setAttribute("rel", "stylesheet");
13888        ss.setAttribute("type", "text/css");
13889        ss.setAttribute("id", id);
13890        ss.setAttribute("href", url);
13891        doc.getElementsByTagName("head")[0].appendChild(ss);
13892    },
13893    
13894    /**
13895     * Refresh the rule cache if you have dynamically added stylesheets
13896     * @return {Object} An object (hash) of rules indexed by selector
13897     */
13898    refreshCache : function(){
13899        return this.getRules(true);
13900    },
13901
13902    // private
13903    cacheStyleSheet : function(stylesheet){
13904        if(!rules){
13905            rules = {};
13906        }
13907        try{// try catch for cross domain access issue
13908            var ssRules = stylesheet.cssRules || stylesheet.rules;
13909            for(var j = ssRules.length-1; j >= 0; --j){
13910                rules[ssRules[j].selectorText] = ssRules[j];
13911            }
13912        }catch(e){}
13913    },
13914    
13915    /**
13916     * Gets all css rules for the document
13917     * @param {Boolean} refreshCache true to refresh the internal cache
13918     * @return {Object} An object (hash) of rules indexed by selector
13919     */
13920    getRules : function(refreshCache){
13921                 if(rules == null || refreshCache){
13922                         rules = {};
13923                         var ds = doc.styleSheets;
13924                         for(var i =0, len = ds.length; i < len; i++){
13925                             try{
13926                         this.cacheStyleSheet(ds[i]);
13927                     }catch(e){} 
13928                 }
13929                 }
13930                 return rules;
13931         },
13932         
13933         /**
13934     * Gets an an individual CSS rule by selector(s)
13935     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13936     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13937     * @return {CSSRule} The CSS rule or null if one is not found
13938     */
13939    getRule : function(selector, refreshCache){
13940                 var rs = this.getRules(refreshCache);
13941                 if(!(selector instanceof Array)){
13942                     return rs[selector];
13943                 }
13944                 for(var i = 0; i < selector.length; i++){
13945                         if(rs[selector[i]]){
13946                                 return rs[selector[i]];
13947                         }
13948                 }
13949                 return null;
13950         },
13951         
13952         
13953         /**
13954     * Updates a rule property
13955     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13956     * @param {String} property The css property
13957     * @param {String} value The new value for the property
13958     * @return {Boolean} true If a rule was found and updated
13959     */
13960    updateRule : function(selector, property, value){
13961                 if(!(selector instanceof Array)){
13962                         var rule = this.getRule(selector);
13963                         if(rule){
13964                                 rule.style[property.replace(camelRe, camelFn)] = value;
13965                                 return true;
13966                         }
13967                 }else{
13968                         for(var i = 0; i < selector.length; i++){
13969                                 if(this.updateRule(selector[i], property, value)){
13970                                         return true;
13971                                 }
13972                         }
13973                 }
13974                 return false;
13975         }
13976    };   
13977 }();/*
13978  * Based on:
13979  * Ext JS Library 1.1.1
13980  * Copyright(c) 2006-2007, Ext JS, LLC.
13981  *
13982  * Originally Released Under LGPL - original licence link has changed is not relivant.
13983  *
13984  * Fork - LGPL
13985  * <script type="text/javascript">
13986  */
13987
13988  
13989
13990 /**
13991  * @class Roo.util.ClickRepeater
13992  * @extends Roo.util.Observable
13993  * 
13994  * A wrapper class which can be applied to any element. Fires a "click" event while the
13995  * mouse is pressed. The interval between firings may be specified in the config but
13996  * defaults to 10 milliseconds.
13997  * 
13998  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13999  * 
14000  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14001  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14002  * Similar to an autorepeat key delay.
14003  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14004  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14005  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14006  *           "interval" and "delay" are ignored. "immediate" is honored.
14007  * @cfg {Boolean} preventDefault True to prevent the default click event
14008  * @cfg {Boolean} stopDefault True to stop the default click event
14009  * 
14010  * @history
14011  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14012  *     2007-02-02 jvs Renamed to ClickRepeater
14013  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14014  *
14015  *  @constructor
14016  * @param {String/HTMLElement/Element} el The element to listen on
14017  * @param {Object} config
14018  **/
14019 Roo.util.ClickRepeater = function(el, config)
14020 {
14021     this.el = Roo.get(el);
14022     this.el.unselectable();
14023
14024     Roo.apply(this, config);
14025
14026     this.addEvents({
14027     /**
14028      * @event mousedown
14029      * Fires when the mouse button is depressed.
14030      * @param {Roo.util.ClickRepeater} this
14031      */
14032         "mousedown" : true,
14033     /**
14034      * @event click
14035      * Fires on a specified interval during the time the element is pressed.
14036      * @param {Roo.util.ClickRepeater} this
14037      */
14038         "click" : true,
14039     /**
14040      * @event mouseup
14041      * Fires when the mouse key is released.
14042      * @param {Roo.util.ClickRepeater} this
14043      */
14044         "mouseup" : true
14045     });
14046
14047     this.el.on("mousedown", this.handleMouseDown, this);
14048     if(this.preventDefault || this.stopDefault){
14049         this.el.on("click", function(e){
14050             if(this.preventDefault){
14051                 e.preventDefault();
14052             }
14053             if(this.stopDefault){
14054                 e.stopEvent();
14055             }
14056         }, this);
14057     }
14058
14059     // allow inline handler
14060     if(this.handler){
14061         this.on("click", this.handler,  this.scope || this);
14062     }
14063
14064     Roo.util.ClickRepeater.superclass.constructor.call(this);
14065 };
14066
14067 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14068     interval : 20,
14069     delay: 250,
14070     preventDefault : true,
14071     stopDefault : false,
14072     timer : 0,
14073
14074     // private
14075     handleMouseDown : function(){
14076         clearTimeout(this.timer);
14077         this.el.blur();
14078         if(this.pressClass){
14079             this.el.addClass(this.pressClass);
14080         }
14081         this.mousedownTime = new Date();
14082
14083         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14084         this.el.on("mouseout", this.handleMouseOut, this);
14085
14086         this.fireEvent("mousedown", this);
14087         this.fireEvent("click", this);
14088         
14089         this.timer = this.click.defer(this.delay || this.interval, this);
14090     },
14091
14092     // private
14093     click : function(){
14094         this.fireEvent("click", this);
14095         this.timer = this.click.defer(this.getInterval(), this);
14096     },
14097
14098     // private
14099     getInterval: function(){
14100         if(!this.accelerate){
14101             return this.interval;
14102         }
14103         var pressTime = this.mousedownTime.getElapsed();
14104         if(pressTime < 500){
14105             return 400;
14106         }else if(pressTime < 1700){
14107             return 320;
14108         }else if(pressTime < 2600){
14109             return 250;
14110         }else if(pressTime < 3500){
14111             return 180;
14112         }else if(pressTime < 4400){
14113             return 140;
14114         }else if(pressTime < 5300){
14115             return 80;
14116         }else if(pressTime < 6200){
14117             return 50;
14118         }else{
14119             return 10;
14120         }
14121     },
14122
14123     // private
14124     handleMouseOut : function(){
14125         clearTimeout(this.timer);
14126         if(this.pressClass){
14127             this.el.removeClass(this.pressClass);
14128         }
14129         this.el.on("mouseover", this.handleMouseReturn, this);
14130     },
14131
14132     // private
14133     handleMouseReturn : function(){
14134         this.el.un("mouseover", this.handleMouseReturn);
14135         if(this.pressClass){
14136             this.el.addClass(this.pressClass);
14137         }
14138         this.click();
14139     },
14140
14141     // private
14142     handleMouseUp : function(){
14143         clearTimeout(this.timer);
14144         this.el.un("mouseover", this.handleMouseReturn);
14145         this.el.un("mouseout", this.handleMouseOut);
14146         Roo.get(document).un("mouseup", this.handleMouseUp);
14147         this.el.removeClass(this.pressClass);
14148         this.fireEvent("mouseup", this);
14149     }
14150 });/*
14151  * Based on:
14152  * Ext JS Library 1.1.1
14153  * Copyright(c) 2006-2007, Ext JS, LLC.
14154  *
14155  * Originally Released Under LGPL - original licence link has changed is not relivant.
14156  *
14157  * Fork - LGPL
14158  * <script type="text/javascript">
14159  */
14160
14161  
14162 /**
14163  * @class Roo.KeyNav
14164  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14165  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14166  * way to implement custom navigation schemes for any UI component.</p>
14167  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14168  * pageUp, pageDown, del, home, end.  Usage:</p>
14169  <pre><code>
14170 var nav = new Roo.KeyNav("my-element", {
14171     "left" : function(e){
14172         this.moveLeft(e.ctrlKey);
14173     },
14174     "right" : function(e){
14175         this.moveRight(e.ctrlKey);
14176     },
14177     "enter" : function(e){
14178         this.save();
14179     },
14180     scope : this
14181 });
14182 </code></pre>
14183  * @constructor
14184  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14185  * @param {Object} config The config
14186  */
14187 Roo.KeyNav = function(el, config){
14188     this.el = Roo.get(el);
14189     Roo.apply(this, config);
14190     if(!this.disabled){
14191         this.disabled = true;
14192         this.enable();
14193     }
14194 };
14195
14196 Roo.KeyNav.prototype = {
14197     /**
14198      * @cfg {Boolean} disabled
14199      * True to disable this KeyNav instance (defaults to false)
14200      */
14201     disabled : false,
14202     /**
14203      * @cfg {String} defaultEventAction
14204      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14205      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14206      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14207      */
14208     defaultEventAction: "stopEvent",
14209     /**
14210      * @cfg {Boolean} forceKeyDown
14211      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14212      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14213      * handle keydown instead of keypress.
14214      */
14215     forceKeyDown : false,
14216
14217     // private
14218     prepareEvent : function(e){
14219         var k = e.getKey();
14220         var h = this.keyToHandler[k];
14221         //if(h && this[h]){
14222         //    e.stopPropagation();
14223         //}
14224         if(Roo.isSafari && h && k >= 37 && k <= 40){
14225             e.stopEvent();
14226         }
14227     },
14228
14229     // private
14230     relay : function(e){
14231         var k = e.getKey();
14232         var h = this.keyToHandler[k];
14233         if(h && this[h]){
14234             if(this.doRelay(e, this[h], h) !== true){
14235                 e[this.defaultEventAction]();
14236             }
14237         }
14238     },
14239
14240     // private
14241     doRelay : function(e, h, hname){
14242         return h.call(this.scope || this, e);
14243     },
14244
14245     // possible handlers
14246     enter : false,
14247     left : false,
14248     right : false,
14249     up : false,
14250     down : false,
14251     tab : false,
14252     esc : false,
14253     pageUp : false,
14254     pageDown : false,
14255     del : false,
14256     home : false,
14257     end : false,
14258
14259     // quick lookup hash
14260     keyToHandler : {
14261         37 : "left",
14262         39 : "right",
14263         38 : "up",
14264         40 : "down",
14265         33 : "pageUp",
14266         34 : "pageDown",
14267         46 : "del",
14268         36 : "home",
14269         35 : "end",
14270         13 : "enter",
14271         27 : "esc",
14272         9  : "tab"
14273     },
14274
14275         /**
14276          * Enable this KeyNav
14277          */
14278         enable: function(){
14279                 if(this.disabled){
14280             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14281             // the EventObject will normalize Safari automatically
14282             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14283                 this.el.on("keydown", this.relay,  this);
14284             }else{
14285                 this.el.on("keydown", this.prepareEvent,  this);
14286                 this.el.on("keypress", this.relay,  this);
14287             }
14288                     this.disabled = false;
14289                 }
14290         },
14291
14292         /**
14293          * Disable this KeyNav
14294          */
14295         disable: function(){
14296                 if(!this.disabled){
14297                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14298                 this.el.un("keydown", this.relay);
14299             }else{
14300                 this.el.un("keydown", this.prepareEvent);
14301                 this.el.un("keypress", this.relay);
14302             }
14303                     this.disabled = true;
14304                 }
14305         }
14306 };/*
14307  * Based on:
14308  * Ext JS Library 1.1.1
14309  * Copyright(c) 2006-2007, Ext JS, LLC.
14310  *
14311  * Originally Released Under LGPL - original licence link has changed is not relivant.
14312  *
14313  * Fork - LGPL
14314  * <script type="text/javascript">
14315  */
14316
14317  
14318 /**
14319  * @class Roo.KeyMap
14320  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14321  * The constructor accepts the same config object as defined by {@link #addBinding}.
14322  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14323  * combination it will call the function with this signature (if the match is a multi-key
14324  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14325  * A KeyMap can also handle a string representation of keys.<br />
14326  * Usage:
14327  <pre><code>
14328 // map one key by key code
14329 var map = new Roo.KeyMap("my-element", {
14330     key: 13, // or Roo.EventObject.ENTER
14331     fn: myHandler,
14332     scope: myObject
14333 });
14334
14335 // map multiple keys to one action by string
14336 var map = new Roo.KeyMap("my-element", {
14337     key: "a\r\n\t",
14338     fn: myHandler,
14339     scope: myObject
14340 });
14341
14342 // map multiple keys to multiple actions by strings and array of codes
14343 var map = new Roo.KeyMap("my-element", [
14344     {
14345         key: [10,13],
14346         fn: function(){ alert("Return was pressed"); }
14347     }, {
14348         key: "abc",
14349         fn: function(){ alert('a, b or c was pressed'); }
14350     }, {
14351         key: "\t",
14352         ctrl:true,
14353         shift:true,
14354         fn: function(){ alert('Control + shift + tab was pressed.'); }
14355     }
14356 ]);
14357 </code></pre>
14358  * <b>Note: A KeyMap starts enabled</b>
14359  * @constructor
14360  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14361  * @param {Object} config The config (see {@link #addBinding})
14362  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14363  */
14364 Roo.KeyMap = function(el, config, eventName){
14365     this.el  = Roo.get(el);
14366     this.eventName = eventName || "keydown";
14367     this.bindings = [];
14368     if(config){
14369         this.addBinding(config);
14370     }
14371     this.enable();
14372 };
14373
14374 Roo.KeyMap.prototype = {
14375     /**
14376      * True to stop the event from bubbling and prevent the default browser action if the
14377      * key was handled by the KeyMap (defaults to false)
14378      * @type Boolean
14379      */
14380     stopEvent : false,
14381
14382     /**
14383      * Add a new binding to this KeyMap. The following config object properties are supported:
14384      * <pre>
14385 Property    Type             Description
14386 ----------  ---------------  ----------------------------------------------------------------------
14387 key         String/Array     A single keycode or an array of keycodes to handle
14388 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14389 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14390 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14391 fn          Function         The function to call when KeyMap finds the expected key combination
14392 scope       Object           The scope of the callback function
14393 </pre>
14394      *
14395      * Usage:
14396      * <pre><code>
14397 // Create a KeyMap
14398 var map = new Roo.KeyMap(document, {
14399     key: Roo.EventObject.ENTER,
14400     fn: handleKey,
14401     scope: this
14402 });
14403
14404 //Add a new binding to the existing KeyMap later
14405 map.addBinding({
14406     key: 'abc',
14407     shift: true,
14408     fn: handleKey,
14409     scope: this
14410 });
14411 </code></pre>
14412      * @param {Object/Array} config A single KeyMap config or an array of configs
14413      */
14414         addBinding : function(config){
14415         if(config instanceof Array){
14416             for(var i = 0, len = config.length; i < len; i++){
14417                 this.addBinding(config[i]);
14418             }
14419             return;
14420         }
14421         var keyCode = config.key,
14422             shift = config.shift, 
14423             ctrl = config.ctrl, 
14424             alt = config.alt,
14425             fn = config.fn,
14426             scope = config.scope;
14427         if(typeof keyCode == "string"){
14428             var ks = [];
14429             var keyString = keyCode.toUpperCase();
14430             for(var j = 0, len = keyString.length; j < len; j++){
14431                 ks.push(keyString.charCodeAt(j));
14432             }
14433             keyCode = ks;
14434         }
14435         var keyArray = keyCode instanceof Array;
14436         var handler = function(e){
14437             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14438                 var k = e.getKey();
14439                 if(keyArray){
14440                     for(var i = 0, len = keyCode.length; i < len; i++){
14441                         if(keyCode[i] == k){
14442                           if(this.stopEvent){
14443                               e.stopEvent();
14444                           }
14445                           fn.call(scope || window, k, e);
14446                           return;
14447                         }
14448                     }
14449                 }else{
14450                     if(k == keyCode){
14451                         if(this.stopEvent){
14452                            e.stopEvent();
14453                         }
14454                         fn.call(scope || window, k, e);
14455                     }
14456                 }
14457             }
14458         };
14459         this.bindings.push(handler);  
14460         },
14461
14462     /**
14463      * Shorthand for adding a single key listener
14464      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14465      * following options:
14466      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14467      * @param {Function} fn The function to call
14468      * @param {Object} scope (optional) The scope of the function
14469      */
14470     on : function(key, fn, scope){
14471         var keyCode, shift, ctrl, alt;
14472         if(typeof key == "object" && !(key instanceof Array)){
14473             keyCode = key.key;
14474             shift = key.shift;
14475             ctrl = key.ctrl;
14476             alt = key.alt;
14477         }else{
14478             keyCode = key;
14479         }
14480         this.addBinding({
14481             key: keyCode,
14482             shift: shift,
14483             ctrl: ctrl,
14484             alt: alt,
14485             fn: fn,
14486             scope: scope
14487         })
14488     },
14489
14490     // private
14491     handleKeyDown : function(e){
14492             if(this.enabled){ //just in case
14493             var b = this.bindings;
14494             for(var i = 0, len = b.length; i < len; i++){
14495                 b[i].call(this, e);
14496             }
14497             }
14498         },
14499         
14500         /**
14501          * Returns true if this KeyMap is enabled
14502          * @return {Boolean} 
14503          */
14504         isEnabled : function(){
14505             return this.enabled;  
14506         },
14507         
14508         /**
14509          * Enables this KeyMap
14510          */
14511         enable: function(){
14512                 if(!this.enabled){
14513                     this.el.on(this.eventName, this.handleKeyDown, this);
14514                     this.enabled = true;
14515                 }
14516         },
14517
14518         /**
14519          * Disable this KeyMap
14520          */
14521         disable: function(){
14522                 if(this.enabled){
14523                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14524                     this.enabled = false;
14525                 }
14526         }
14527 };/*
14528  * Based on:
14529  * Ext JS Library 1.1.1
14530  * Copyright(c) 2006-2007, Ext JS, LLC.
14531  *
14532  * Originally Released Under LGPL - original licence link has changed is not relivant.
14533  *
14534  * Fork - LGPL
14535  * <script type="text/javascript">
14536  */
14537
14538  
14539 /**
14540  * @class Roo.util.TextMetrics
14541  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14542  * wide, in pixels, a given block of text will be.
14543  * @singleton
14544  */
14545 Roo.util.TextMetrics = function(){
14546     var shared;
14547     return {
14548         /**
14549          * Measures the size of the specified text
14550          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14551          * that can affect the size of the rendered text
14552          * @param {String} text The text to measure
14553          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14554          * in order to accurately measure the text height
14555          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14556          */
14557         measure : function(el, text, fixedWidth){
14558             if(!shared){
14559                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14560             }
14561             shared.bind(el);
14562             shared.setFixedWidth(fixedWidth || 'auto');
14563             return shared.getSize(text);
14564         },
14565
14566         /**
14567          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14568          * the overhead of multiple calls to initialize the style properties on each measurement.
14569          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14570          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14571          * in order to accurately measure the text height
14572          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14573          */
14574         createInstance : function(el, fixedWidth){
14575             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14576         }
14577     };
14578 }();
14579
14580  
14581
14582 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14583     var ml = new Roo.Element(document.createElement('div'));
14584     document.body.appendChild(ml.dom);
14585     ml.position('absolute');
14586     ml.setLeftTop(-1000, -1000);
14587     ml.hide();
14588
14589     if(fixedWidth){
14590         ml.setWidth(fixedWidth);
14591     }
14592      
14593     var instance = {
14594         /**
14595          * Returns the size of the specified text based on the internal element's style and width properties
14596          * @memberOf Roo.util.TextMetrics.Instance#
14597          * @param {String} text The text to measure
14598          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14599          */
14600         getSize : function(text){
14601             ml.update(text);
14602             var s = ml.getSize();
14603             ml.update('');
14604             return s;
14605         },
14606
14607         /**
14608          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14609          * that can affect the size of the rendered text
14610          * @memberOf Roo.util.TextMetrics.Instance#
14611          * @param {String/HTMLElement} el The element, dom node or id
14612          */
14613         bind : function(el){
14614             ml.setStyle(
14615                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14616             );
14617         },
14618
14619         /**
14620          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14621          * to set a fixed width in order to accurately measure the text height.
14622          * @memberOf Roo.util.TextMetrics.Instance#
14623          * @param {Number} width The width to set on the element
14624          */
14625         setFixedWidth : function(width){
14626             ml.setWidth(width);
14627         },
14628
14629         /**
14630          * Returns the measured width of the specified text
14631          * @memberOf Roo.util.TextMetrics.Instance#
14632          * @param {String} text The text to measure
14633          * @return {Number} width The width in pixels
14634          */
14635         getWidth : function(text){
14636             ml.dom.style.width = 'auto';
14637             return this.getSize(text).width;
14638         },
14639
14640         /**
14641          * Returns the measured height of the specified text.  For multiline text, be sure to call
14642          * {@link #setFixedWidth} if necessary.
14643          * @memberOf Roo.util.TextMetrics.Instance#
14644          * @param {String} text The text to measure
14645          * @return {Number} height The height in pixels
14646          */
14647         getHeight : function(text){
14648             return this.getSize(text).height;
14649         }
14650     };
14651
14652     instance.bind(bindTo);
14653
14654     return instance;
14655 };
14656
14657 // backwards compat
14658 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14659  * Based on:
14660  * Ext JS Library 1.1.1
14661  * Copyright(c) 2006-2007, Ext JS, LLC.
14662  *
14663  * Originally Released Under LGPL - original licence link has changed is not relivant.
14664  *
14665  * Fork - LGPL
14666  * <script type="text/javascript">
14667  */
14668
14669 /**
14670  * @class Roo.state.Provider
14671  * Abstract base class for state provider implementations. This class provides methods
14672  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14673  * Provider interface.
14674  */
14675 Roo.state.Provider = function(){
14676     /**
14677      * @event statechange
14678      * Fires when a state change occurs.
14679      * @param {Provider} this This state provider
14680      * @param {String} key The state key which was changed
14681      * @param {String} value The encoded value for the state
14682      */
14683     this.addEvents({
14684         "statechange": true
14685     });
14686     this.state = {};
14687     Roo.state.Provider.superclass.constructor.call(this);
14688 };
14689 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14690     /**
14691      * Returns the current value for a key
14692      * @param {String} name The key name
14693      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14694      * @return {Mixed} The state data
14695      */
14696     get : function(name, defaultValue){
14697         return typeof this.state[name] == "undefined" ?
14698             defaultValue : this.state[name];
14699     },
14700     
14701     /**
14702      * Clears a value from the state
14703      * @param {String} name The key name
14704      */
14705     clear : function(name){
14706         delete this.state[name];
14707         this.fireEvent("statechange", this, name, null);
14708     },
14709     
14710     /**
14711      * Sets the value for a key
14712      * @param {String} name The key name
14713      * @param {Mixed} value The value to set
14714      */
14715     set : function(name, value){
14716         this.state[name] = value;
14717         this.fireEvent("statechange", this, name, value);
14718     },
14719     
14720     /**
14721      * Decodes a string previously encoded with {@link #encodeValue}.
14722      * @param {String} value The value to decode
14723      * @return {Mixed} The decoded value
14724      */
14725     decodeValue : function(cookie){
14726         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14727         var matches = re.exec(unescape(cookie));
14728         if(!matches || !matches[1]) return; // non state cookie
14729         var type = matches[1];
14730         var v = matches[2];
14731         switch(type){
14732             case "n":
14733                 return parseFloat(v);
14734             case "d":
14735                 return new Date(Date.parse(v));
14736             case "b":
14737                 return (v == "1");
14738             case "a":
14739                 var all = [];
14740                 var values = v.split("^");
14741                 for(var i = 0, len = values.length; i < len; i++){
14742                     all.push(this.decodeValue(values[i]));
14743                 }
14744                 return all;
14745            case "o":
14746                 var all = {};
14747                 var values = v.split("^");
14748                 for(var i = 0, len = values.length; i < len; i++){
14749                     var kv = values[i].split("=");
14750                     all[kv[0]] = this.decodeValue(kv[1]);
14751                 }
14752                 return all;
14753            default:
14754                 return v;
14755         }
14756     },
14757     
14758     /**
14759      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14760      * @param {Mixed} value The value to encode
14761      * @return {String} The encoded value
14762      */
14763     encodeValue : function(v){
14764         var enc;
14765         if(typeof v == "number"){
14766             enc = "n:" + v;
14767         }else if(typeof v == "boolean"){
14768             enc = "b:" + (v ? "1" : "0");
14769         }else if(v instanceof Date){
14770             enc = "d:" + v.toGMTString();
14771         }else if(v instanceof Array){
14772             var flat = "";
14773             for(var i = 0, len = v.length; i < len; i++){
14774                 flat += this.encodeValue(v[i]);
14775                 if(i != len-1) flat += "^";
14776             }
14777             enc = "a:" + flat;
14778         }else if(typeof v == "object"){
14779             var flat = "";
14780             for(var key in v){
14781                 if(typeof v[key] != "function"){
14782                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14783                 }
14784             }
14785             enc = "o:" + flat.substring(0, flat.length-1);
14786         }else{
14787             enc = "s:" + v;
14788         }
14789         return escape(enc);        
14790     }
14791 });
14792
14793 /*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803 /**
14804  * @class Roo.state.Manager
14805  * This is the global state manager. By default all components that are "state aware" check this class
14806  * for state information if you don't pass them a custom state provider. In order for this class
14807  * to be useful, it must be initialized with a provider when your application initializes.
14808  <pre><code>
14809 // in your initialization function
14810 init : function(){
14811    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14812    ...
14813    // supposed you have a {@link Roo.BorderLayout}
14814    var layout = new Roo.BorderLayout(...);
14815    layout.restoreState();
14816    // or a {Roo.BasicDialog}
14817    var dialog = new Roo.BasicDialog(...);
14818    dialog.restoreState();
14819  </code></pre>
14820  * @singleton
14821  */
14822 Roo.state.Manager = function(){
14823     var provider = new Roo.state.Provider();
14824     
14825     return {
14826         /**
14827          * Configures the default state provider for your application
14828          * @param {Provider} stateProvider The state provider to set
14829          */
14830         setProvider : function(stateProvider){
14831             provider = stateProvider;
14832         },
14833         
14834         /**
14835          * Returns the current value for a key
14836          * @param {String} name The key name
14837          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14838          * @return {Mixed} The state data
14839          */
14840         get : function(key, defaultValue){
14841             return provider.get(key, defaultValue);
14842         },
14843         
14844         /**
14845          * Sets the value for a key
14846          * @param {String} name The key name
14847          * @param {Mixed} value The state data
14848          */
14849          set : function(key, value){
14850             provider.set(key, value);
14851         },
14852         
14853         /**
14854          * Clears a value from the state
14855          * @param {String} name The key name
14856          */
14857         clear : function(key){
14858             provider.clear(key);
14859         },
14860         
14861         /**
14862          * Gets the currently configured state provider
14863          * @return {Provider} The state provider
14864          */
14865         getProvider : function(){
14866             return provider;
14867         }
14868     };
14869 }();
14870 /*
14871  * Based on:
14872  * Ext JS Library 1.1.1
14873  * Copyright(c) 2006-2007, Ext JS, LLC.
14874  *
14875  * Originally Released Under LGPL - original licence link has changed is not relivant.
14876  *
14877  * Fork - LGPL
14878  * <script type="text/javascript">
14879  */
14880 /**
14881  * @class Roo.state.CookieProvider
14882  * @extends Roo.state.Provider
14883  * The default Provider implementation which saves state via cookies.
14884  * <br />Usage:
14885  <pre><code>
14886    var cp = new Roo.state.CookieProvider({
14887        path: "/cgi-bin/",
14888        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14889        domain: "roojs.com"
14890    })
14891    Roo.state.Manager.setProvider(cp);
14892  </code></pre>
14893  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14894  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14895  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14896  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14897  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14898  * domain the page is running on including the 'www' like 'www.roojs.com')
14899  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14900  * @constructor
14901  * Create a new CookieProvider
14902  * @param {Object} config The configuration object
14903  */
14904 Roo.state.CookieProvider = function(config){
14905     Roo.state.CookieProvider.superclass.constructor.call(this);
14906     this.path = "/";
14907     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14908     this.domain = null;
14909     this.secure = false;
14910     Roo.apply(this, config);
14911     this.state = this.readCookies();
14912 };
14913
14914 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14915     // private
14916     set : function(name, value){
14917         if(typeof value == "undefined" || value === null){
14918             this.clear(name);
14919             return;
14920         }
14921         this.setCookie(name, value);
14922         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14923     },
14924
14925     // private
14926     clear : function(name){
14927         this.clearCookie(name);
14928         Roo.state.CookieProvider.superclass.clear.call(this, name);
14929     },
14930
14931     // private
14932     readCookies : function(){
14933         var cookies = {};
14934         var c = document.cookie + ";";
14935         var re = /\s?(.*?)=(.*?);/g;
14936         var matches;
14937         while((matches = re.exec(c)) != null){
14938             var name = matches[1];
14939             var value = matches[2];
14940             if(name && name.substring(0,3) == "ys-"){
14941                 cookies[name.substr(3)] = this.decodeValue(value);
14942             }
14943         }
14944         return cookies;
14945     },
14946
14947     // private
14948     setCookie : function(name, value){
14949         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14950            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14951            ((this.path == null) ? "" : ("; path=" + this.path)) +
14952            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14953            ((this.secure == true) ? "; secure" : "");
14954     },
14955
14956     // private
14957     clearCookie : function(name){
14958         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14959            ((this.path == null) ? "" : ("; path=" + this.path)) +
14960            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14961            ((this.secure == true) ? "; secure" : "");
14962     }
14963 });/*
14964  * Based on:
14965  * Ext JS Library 1.1.1
14966  * Copyright(c) 2006-2007, Ext JS, LLC.
14967  *
14968  * Originally Released Under LGPL - original licence link has changed is not relivant.
14969  *
14970  * Fork - LGPL
14971  * <script type="text/javascript">
14972  */
14973  
14974
14975 /**
14976  * @class Roo.ComponentMgr
14977  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14978  * @singleton
14979  */
14980 Roo.ComponentMgr = function(){
14981     var all = new Roo.util.MixedCollection();
14982
14983     return {
14984         /**
14985          * Registers a component.
14986          * @param {Roo.Component} c The component
14987          */
14988         register : function(c){
14989             all.add(c);
14990         },
14991
14992         /**
14993          * Unregisters a component.
14994          * @param {Roo.Component} c The component
14995          */
14996         unregister : function(c){
14997             all.remove(c);
14998         },
14999
15000         /**
15001          * Returns a component by id
15002          * @param {String} id The component id
15003          */
15004         get : function(id){
15005             return all.get(id);
15006         },
15007
15008         /**
15009          * Registers a function that will be called when a specified component is added to ComponentMgr
15010          * @param {String} id The component id
15011          * @param {Funtction} fn The callback function
15012          * @param {Object} scope The scope of the callback
15013          */
15014         onAvailable : function(id, fn, scope){
15015             all.on("add", function(index, o){
15016                 if(o.id == id){
15017                     fn.call(scope || o, o);
15018                     all.un("add", fn, scope);
15019                 }
15020             });
15021         }
15022     };
15023 }();/*
15024  * Based on:
15025  * Ext JS Library 1.1.1
15026  * Copyright(c) 2006-2007, Ext JS, LLC.
15027  *
15028  * Originally Released Under LGPL - original licence link has changed is not relivant.
15029  *
15030  * Fork - LGPL
15031  * <script type="text/javascript">
15032  */
15033  
15034 /**
15035  * @class Roo.Component
15036  * @extends Roo.util.Observable
15037  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15038  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15039  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15040  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15041  * All visual components (widgets) that require rendering into a layout should subclass Component.
15042  * @constructor
15043  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15044  * 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
15045  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15046  */
15047 Roo.Component = function(config){
15048     config = config || {};
15049     if(config.tagName || config.dom || typeof config == "string"){ // element object
15050         config = {el: config, id: config.id || config};
15051     }
15052     this.initialConfig = config;
15053
15054     Roo.apply(this, config);
15055     this.addEvents({
15056         /**
15057          * @event disable
15058          * Fires after the component is disabled.
15059              * @param {Roo.Component} this
15060              */
15061         disable : true,
15062         /**
15063          * @event enable
15064          * Fires after the component is enabled.
15065              * @param {Roo.Component} this
15066              */
15067         enable : true,
15068         /**
15069          * @event beforeshow
15070          * Fires before the component is shown.  Return false to stop the show.
15071              * @param {Roo.Component} this
15072              */
15073         beforeshow : true,
15074         /**
15075          * @event show
15076          * Fires after the component is shown.
15077              * @param {Roo.Component} this
15078              */
15079         show : true,
15080         /**
15081          * @event beforehide
15082          * Fires before the component is hidden. Return false to stop the hide.
15083              * @param {Roo.Component} this
15084              */
15085         beforehide : true,
15086         /**
15087          * @event hide
15088          * Fires after the component is hidden.
15089              * @param {Roo.Component} this
15090              */
15091         hide : true,
15092         /**
15093          * @event beforerender
15094          * Fires before the component is rendered. Return false to stop the render.
15095              * @param {Roo.Component} this
15096              */
15097         beforerender : true,
15098         /**
15099          * @event render
15100          * Fires after the component is rendered.
15101              * @param {Roo.Component} this
15102              */
15103         render : true,
15104         /**
15105          * @event beforedestroy
15106          * Fires before the component is destroyed. Return false to stop the destroy.
15107              * @param {Roo.Component} this
15108              */
15109         beforedestroy : true,
15110         /**
15111          * @event destroy
15112          * Fires after the component is destroyed.
15113              * @param {Roo.Component} this
15114              */
15115         destroy : true
15116     });
15117     if(!this.id){
15118         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15119     }
15120     Roo.ComponentMgr.register(this);
15121     Roo.Component.superclass.constructor.call(this);
15122     this.initComponent();
15123     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15124         this.render(this.renderTo);
15125         delete this.renderTo;
15126     }
15127 };
15128
15129 /** @private */
15130 Roo.Component.AUTO_ID = 1000;
15131
15132 Roo.extend(Roo.Component, Roo.util.Observable, {
15133     /**
15134      * @scope Roo.Component.prototype
15135      * @type {Boolean}
15136      * true if this component is hidden. Read-only.
15137      */
15138     hidden : false,
15139     /**
15140      * @type {Boolean}
15141      * true if this component is disabled. Read-only.
15142      */
15143     disabled : false,
15144     /**
15145      * @type {Boolean}
15146      * true if this component has been rendered. Read-only.
15147      */
15148     rendered : false,
15149     
15150     /** @cfg {String} disableClass
15151      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15152      */
15153     disabledClass : "x-item-disabled",
15154         /** @cfg {Boolean} allowDomMove
15155          * Whether the component can move the Dom node when rendering (defaults to true).
15156          */
15157     allowDomMove : true,
15158     /** @cfg {String} hideMode
15159      * How this component should hidden. Supported values are
15160      * "visibility" (css visibility), "offsets" (negative offset position) and
15161      * "display" (css display) - defaults to "display".
15162      */
15163     hideMode: 'display',
15164
15165     /** @private */
15166     ctype : "Roo.Component",
15167
15168     /**
15169      * @cfg {String} actionMode 
15170      * which property holds the element that used for  hide() / show() / disable() / enable()
15171      * default is 'el' 
15172      */
15173     actionMode : "el",
15174
15175     /** @private */
15176     getActionEl : function(){
15177         return this[this.actionMode];
15178     },
15179
15180     initComponent : Roo.emptyFn,
15181     /**
15182      * If this is a lazy rendering component, render it to its container element.
15183      * @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.
15184      */
15185     render : function(container, position){
15186         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15187             if(!container && this.el){
15188                 this.el = Roo.get(this.el);
15189                 container = this.el.dom.parentNode;
15190                 this.allowDomMove = false;
15191             }
15192             this.container = Roo.get(container);
15193             this.rendered = true;
15194             if(position !== undefined){
15195                 if(typeof position == 'number'){
15196                     position = this.container.dom.childNodes[position];
15197                 }else{
15198                     position = Roo.getDom(position);
15199                 }
15200             }
15201             this.onRender(this.container, position || null);
15202             if(this.cls){
15203                 this.el.addClass(this.cls);
15204                 delete this.cls;
15205             }
15206             if(this.style){
15207                 this.el.applyStyles(this.style);
15208                 delete this.style;
15209             }
15210             this.fireEvent("render", this);
15211             this.afterRender(this.container);
15212             if(this.hidden){
15213                 this.hide();
15214             }
15215             if(this.disabled){
15216                 this.disable();
15217             }
15218         }
15219         return this;
15220     },
15221
15222     /** @private */
15223     // default function is not really useful
15224     onRender : function(ct, position){
15225         if(this.el){
15226             this.el = Roo.get(this.el);
15227             if(this.allowDomMove !== false){
15228                 ct.dom.insertBefore(this.el.dom, position);
15229             }
15230         }
15231     },
15232
15233     /** @private */
15234     getAutoCreate : function(){
15235         var cfg = typeof this.autoCreate == "object" ?
15236                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15237         if(this.id && !cfg.id){
15238             cfg.id = this.id;
15239         }
15240         return cfg;
15241     },
15242
15243     /** @private */
15244     afterRender : Roo.emptyFn,
15245
15246     /**
15247      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15248      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15249      */
15250     destroy : function(){
15251         if(this.fireEvent("beforedestroy", this) !== false){
15252             this.purgeListeners();
15253             this.beforeDestroy();
15254             if(this.rendered){
15255                 this.el.removeAllListeners();
15256                 this.el.remove();
15257                 if(this.actionMode == "container"){
15258                     this.container.remove();
15259                 }
15260             }
15261             this.onDestroy();
15262             Roo.ComponentMgr.unregister(this);
15263             this.fireEvent("destroy", this);
15264         }
15265     },
15266
15267         /** @private */
15268     beforeDestroy : function(){
15269
15270     },
15271
15272         /** @private */
15273         onDestroy : function(){
15274
15275     },
15276
15277     /**
15278      * Returns the underlying {@link Roo.Element}.
15279      * @return {Roo.Element} The element
15280      */
15281     getEl : function(){
15282         return this.el;
15283     },
15284
15285     /**
15286      * Returns the id of this component.
15287      * @return {String}
15288      */
15289     getId : function(){
15290         return this.id;
15291     },
15292
15293     /**
15294      * Try to focus this component.
15295      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15296      * @return {Roo.Component} this
15297      */
15298     focus : function(selectText){
15299         if(this.rendered){
15300             this.el.focus();
15301             if(selectText === true){
15302                 this.el.dom.select();
15303             }
15304         }
15305         return this;
15306     },
15307
15308     /** @private */
15309     blur : function(){
15310         if(this.rendered){
15311             this.el.blur();
15312         }
15313         return this;
15314     },
15315
15316     /**
15317      * Disable this component.
15318      * @return {Roo.Component} this
15319      */
15320     disable : function(){
15321         if(this.rendered){
15322             this.onDisable();
15323         }
15324         this.disabled = true;
15325         this.fireEvent("disable", this);
15326         return this;
15327     },
15328
15329         // private
15330     onDisable : function(){
15331         this.getActionEl().addClass(this.disabledClass);
15332         this.el.dom.disabled = true;
15333     },
15334
15335     /**
15336      * Enable this component.
15337      * @return {Roo.Component} this
15338      */
15339     enable : function(){
15340         if(this.rendered){
15341             this.onEnable();
15342         }
15343         this.disabled = false;
15344         this.fireEvent("enable", this);
15345         return this;
15346     },
15347
15348         // private
15349     onEnable : function(){
15350         this.getActionEl().removeClass(this.disabledClass);
15351         this.el.dom.disabled = false;
15352     },
15353
15354     /**
15355      * Convenience function for setting disabled/enabled by boolean.
15356      * @param {Boolean} disabled
15357      */
15358     setDisabled : function(disabled){
15359         this[disabled ? "disable" : "enable"]();
15360     },
15361
15362     /**
15363      * Show this component.
15364      * @return {Roo.Component} this
15365      */
15366     show: function(){
15367         if(this.fireEvent("beforeshow", this) !== false){
15368             this.hidden = false;
15369             if(this.rendered){
15370                 this.onShow();
15371             }
15372             this.fireEvent("show", this);
15373         }
15374         return this;
15375     },
15376
15377     // private
15378     onShow : function(){
15379         var ae = this.getActionEl();
15380         if(this.hideMode == 'visibility'){
15381             ae.dom.style.visibility = "visible";
15382         }else if(this.hideMode == 'offsets'){
15383             ae.removeClass('x-hidden');
15384         }else{
15385             ae.dom.style.display = "";
15386         }
15387     },
15388
15389     /**
15390      * Hide this component.
15391      * @return {Roo.Component} this
15392      */
15393     hide: function(){
15394         if(this.fireEvent("beforehide", this) !== false){
15395             this.hidden = true;
15396             if(this.rendered){
15397                 this.onHide();
15398             }
15399             this.fireEvent("hide", this);
15400         }
15401         return this;
15402     },
15403
15404     // private
15405     onHide : function(){
15406         var ae = this.getActionEl();
15407         if(this.hideMode == 'visibility'){
15408             ae.dom.style.visibility = "hidden";
15409         }else if(this.hideMode == 'offsets'){
15410             ae.addClass('x-hidden');
15411         }else{
15412             ae.dom.style.display = "none";
15413         }
15414     },
15415
15416     /**
15417      * Convenience function to hide or show this component by boolean.
15418      * @param {Boolean} visible True to show, false to hide
15419      * @return {Roo.Component} this
15420      */
15421     setVisible: function(visible){
15422         if(visible) {
15423             this.show();
15424         }else{
15425             this.hide();
15426         }
15427         return this;
15428     },
15429
15430     /**
15431      * Returns true if this component is visible.
15432      */
15433     isVisible : function(){
15434         return this.getActionEl().isVisible();
15435     },
15436
15437     cloneConfig : function(overrides){
15438         overrides = overrides || {};
15439         var id = overrides.id || Roo.id();
15440         var cfg = Roo.applyIf(overrides, this.initialConfig);
15441         cfg.id = id; // prevent dup id
15442         return new this.constructor(cfg);
15443     }
15444 });/*
15445  * Based on:
15446  * Ext JS Library 1.1.1
15447  * Copyright(c) 2006-2007, Ext JS, LLC.
15448  *
15449  * Originally Released Under LGPL - original licence link has changed is not relivant.
15450  *
15451  * Fork - LGPL
15452  * <script type="text/javascript">
15453  */
15454
15455 /**
15456  * @class Roo.BoxComponent
15457  * @extends Roo.Component
15458  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15459  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15460  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15461  * layout containers.
15462  * @constructor
15463  * @param {Roo.Element/String/Object} config The configuration options.
15464  */
15465 Roo.BoxComponent = function(config){
15466     Roo.Component.call(this, config);
15467     this.addEvents({
15468         /**
15469          * @event resize
15470          * Fires after the component is resized.
15471              * @param {Roo.Component} this
15472              * @param {Number} adjWidth The box-adjusted width that was set
15473              * @param {Number} adjHeight The box-adjusted height that was set
15474              * @param {Number} rawWidth The width that was originally specified
15475              * @param {Number} rawHeight The height that was originally specified
15476              */
15477         resize : true,
15478         /**
15479          * @event move
15480          * Fires after the component is moved.
15481              * @param {Roo.Component} this
15482              * @param {Number} x The new x position
15483              * @param {Number} y The new y position
15484              */
15485         move : true
15486     });
15487 };
15488
15489 Roo.extend(Roo.BoxComponent, Roo.Component, {
15490     // private, set in afterRender to signify that the component has been rendered
15491     boxReady : false,
15492     // private, used to defer height settings to subclasses
15493     deferHeight: false,
15494     /** @cfg {Number} width
15495      * width (optional) size of component
15496      */
15497      /** @cfg {Number} height
15498      * height (optional) size of component
15499      */
15500      
15501     /**
15502      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15503      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15504      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15505      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15506      * @return {Roo.BoxComponent} this
15507      */
15508     setSize : function(w, h){
15509         // support for standard size objects
15510         if(typeof w == 'object'){
15511             h = w.height;
15512             w = w.width;
15513         }
15514         // not rendered
15515         if(!this.boxReady){
15516             this.width = w;
15517             this.height = h;
15518             return this;
15519         }
15520
15521         // prevent recalcs when not needed
15522         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15523             return this;
15524         }
15525         this.lastSize = {width: w, height: h};
15526
15527         var adj = this.adjustSize(w, h);
15528         var aw = adj.width, ah = adj.height;
15529         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15530             var rz = this.getResizeEl();
15531             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15532                 rz.setSize(aw, ah);
15533             }else if(!this.deferHeight && ah !== undefined){
15534                 rz.setHeight(ah);
15535             }else if(aw !== undefined){
15536                 rz.setWidth(aw);
15537             }
15538             this.onResize(aw, ah, w, h);
15539             this.fireEvent('resize', this, aw, ah, w, h);
15540         }
15541         return this;
15542     },
15543
15544     /**
15545      * Gets the current size of the component's underlying element.
15546      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15547      */
15548     getSize : function(){
15549         return this.el.getSize();
15550     },
15551
15552     /**
15553      * Gets the current XY position of the component's underlying element.
15554      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15555      * @return {Array} The XY position of the element (e.g., [100, 200])
15556      */
15557     getPosition : function(local){
15558         if(local === true){
15559             return [this.el.getLeft(true), this.el.getTop(true)];
15560         }
15561         return this.xy || this.el.getXY();
15562     },
15563
15564     /**
15565      * Gets the current box measurements of the component's underlying element.
15566      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15567      * @returns {Object} box An object in the format {x, y, width, height}
15568      */
15569     getBox : function(local){
15570         var s = this.el.getSize();
15571         if(local){
15572             s.x = this.el.getLeft(true);
15573             s.y = this.el.getTop(true);
15574         }else{
15575             var xy = this.xy || this.el.getXY();
15576             s.x = xy[0];
15577             s.y = xy[1];
15578         }
15579         return s;
15580     },
15581
15582     /**
15583      * Sets the current box measurements of the component's underlying element.
15584      * @param {Object} box An object in the format {x, y, width, height}
15585      * @returns {Roo.BoxComponent} this
15586      */
15587     updateBox : function(box){
15588         this.setSize(box.width, box.height);
15589         this.setPagePosition(box.x, box.y);
15590         return this;
15591     },
15592
15593     // protected
15594     getResizeEl : function(){
15595         return this.resizeEl || this.el;
15596     },
15597
15598     // protected
15599     getPositionEl : function(){
15600         return this.positionEl || this.el;
15601     },
15602
15603     /**
15604      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15605      * This method fires the move event.
15606      * @param {Number} left The new left
15607      * @param {Number} top The new top
15608      * @returns {Roo.BoxComponent} this
15609      */
15610     setPosition : function(x, y){
15611         this.x = x;
15612         this.y = y;
15613         if(!this.boxReady){
15614             return this;
15615         }
15616         var adj = this.adjustPosition(x, y);
15617         var ax = adj.x, ay = adj.y;
15618
15619         var el = this.getPositionEl();
15620         if(ax !== undefined || ay !== undefined){
15621             if(ax !== undefined && ay !== undefined){
15622                 el.setLeftTop(ax, ay);
15623             }else if(ax !== undefined){
15624                 el.setLeft(ax);
15625             }else if(ay !== undefined){
15626                 el.setTop(ay);
15627             }
15628             this.onPosition(ax, ay);
15629             this.fireEvent('move', this, ax, ay);
15630         }
15631         return this;
15632     },
15633
15634     /**
15635      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15636      * This method fires the move event.
15637      * @param {Number} x The new x position
15638      * @param {Number} y The new y position
15639      * @returns {Roo.BoxComponent} this
15640      */
15641     setPagePosition : function(x, y){
15642         this.pageX = x;
15643         this.pageY = y;
15644         if(!this.boxReady){
15645             return;
15646         }
15647         if(x === undefined || y === undefined){ // cannot translate undefined points
15648             return;
15649         }
15650         var p = this.el.translatePoints(x, y);
15651         this.setPosition(p.left, p.top);
15652         return this;
15653     },
15654
15655     // private
15656     onRender : function(ct, position){
15657         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15658         if(this.resizeEl){
15659             this.resizeEl = Roo.get(this.resizeEl);
15660         }
15661         if(this.positionEl){
15662             this.positionEl = Roo.get(this.positionEl);
15663         }
15664     },
15665
15666     // private
15667     afterRender : function(){
15668         Roo.BoxComponent.superclass.afterRender.call(this);
15669         this.boxReady = true;
15670         this.setSize(this.width, this.height);
15671         if(this.x || this.y){
15672             this.setPosition(this.x, this.y);
15673         }
15674         if(this.pageX || this.pageY){
15675             this.setPagePosition(this.pageX, this.pageY);
15676         }
15677     },
15678
15679     /**
15680      * Force the component's size to recalculate based on the underlying element's current height and width.
15681      * @returns {Roo.BoxComponent} this
15682      */
15683     syncSize : function(){
15684         delete this.lastSize;
15685         this.setSize(this.el.getWidth(), this.el.getHeight());
15686         return this;
15687     },
15688
15689     /**
15690      * Called after the component is resized, this method is empty by default but can be implemented by any
15691      * subclass that needs to perform custom logic after a resize occurs.
15692      * @param {Number} adjWidth The box-adjusted width that was set
15693      * @param {Number} adjHeight The box-adjusted height that was set
15694      * @param {Number} rawWidth The width that was originally specified
15695      * @param {Number} rawHeight The height that was originally specified
15696      */
15697     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15698
15699     },
15700
15701     /**
15702      * Called after the component is moved, this method is empty by default but can be implemented by any
15703      * subclass that needs to perform custom logic after a move occurs.
15704      * @param {Number} x The new x position
15705      * @param {Number} y The new y position
15706      */
15707     onPosition : function(x, y){
15708
15709     },
15710
15711     // private
15712     adjustSize : function(w, h){
15713         if(this.autoWidth){
15714             w = 'auto';
15715         }
15716         if(this.autoHeight){
15717             h = 'auto';
15718         }
15719         return {width : w, height: h};
15720     },
15721
15722     // private
15723     adjustPosition : function(x, y){
15724         return {x : x, y: y};
15725     }
15726 });/*
15727  * Original code for Roojs - LGPL
15728  * <script type="text/javascript">
15729  */
15730  
15731 /**
15732  * @class Roo.XComponent
15733  * A delayed Element creator...
15734  * Or a way to group chunks of interface together.
15735  * 
15736  * Mypart.xyx = new Roo.XComponent({
15737
15738     parent : 'Mypart.xyz', // empty == document.element.!!
15739     order : '001',
15740     name : 'xxxx'
15741     region : 'xxxx'
15742     disabled : function() {} 
15743      
15744     tree : function() { // return an tree of xtype declared components
15745         var MODULE = this;
15746         return 
15747         {
15748             xtype : 'NestedLayoutPanel',
15749             // technicall
15750         }
15751      ]
15752  *})
15753  *
15754  *
15755  * It can be used to build a big heiracy, with parent etc.
15756  * or you can just use this to render a single compoent to a dom element
15757  * MYPART.render(Roo.Element | String(id) | dom_element )
15758  * 
15759  * @extends Roo.util.Observable
15760  * @constructor
15761  * @param cfg {Object} configuration of component
15762  * 
15763  */
15764 Roo.XComponent = function(cfg) {
15765     Roo.apply(this, cfg);
15766     this.addEvents({ 
15767         /**
15768              * @event built
15769              * Fires when this the componnt is built
15770              * @param {Roo.XComponent} c the component
15771              */
15772         'built' : true
15773         
15774     });
15775     this.region = this.region || 'center'; // default..
15776     Roo.XComponent.register(this);
15777     this.modules = false;
15778     this.el = false; // where the layout goes..
15779     
15780     
15781 }
15782 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15783     /**
15784      * @property el
15785      * The created element (with Roo.factory())
15786      * @type {Roo.Layout}
15787      */
15788     el  : false,
15789     
15790     /**
15791      * @property el
15792      * for BC  - use el in new code
15793      * @type {Roo.Layout}
15794      */
15795     panel : false,
15796     
15797     /**
15798      * @property layout
15799      * for BC  - use el in new code
15800      * @type {Roo.Layout}
15801      */
15802     layout : false,
15803     
15804      /**
15805      * @cfg {Function|boolean} disabled
15806      * If this module is disabled by some rule, return true from the funtion
15807      */
15808     disabled : false,
15809     
15810     /**
15811      * @cfg {String} parent 
15812      * Name of parent element which it get xtype added to..
15813      */
15814     parent: false,
15815     
15816     /**
15817      * @cfg {String} order
15818      * Used to set the order in which elements are created (usefull for multiple tabs)
15819      */
15820     
15821     order : false,
15822     /**
15823      * @cfg {String} name
15824      * String to display while loading.
15825      */
15826     name : false,
15827     /**
15828      * @cfg {String} region
15829      * Region to render component to (defaults to center)
15830      */
15831     region : 'center',
15832     
15833     /**
15834      * @cfg {Array} items
15835      * A single item array - the first element is the root of the tree..
15836      * It's done this way to stay compatible with the Xtype system...
15837      */
15838     items : false,
15839     
15840     /**
15841      * @property _tree
15842      * The method that retuns the tree of parts that make up this compoennt 
15843      * @type {function}
15844      */
15845     _tree  : false,
15846     
15847      /**
15848      * render
15849      * render element to dom or tree
15850      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15851      */
15852     
15853     render : function(el)
15854     {
15855         
15856         el = el || false;
15857         var hp = this.parent ? 1 : 0;
15858         
15859         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15860             // if parent is a '#.....' string, then let's use that..
15861             var ename = this.parent.substr(1)
15862             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15863             el = Roo.get(ename);
15864             if (!el && !this.parent) {
15865                 Roo.log("Warning - element can not be found :#" + ename );
15866                 return;
15867             }
15868         }
15869         
15870         
15871         if (!this.parent) {
15872             
15873             el = el ? Roo.get(el) : false;      
15874             
15875             // it's a top level one..
15876             this.parent =  {
15877                 el : new Roo.BorderLayout(el || document.body, {
15878                 
15879                      center: {
15880                          titlebar: false,
15881                          autoScroll:false,
15882                          closeOnTab: true,
15883                          tabPosition: 'top',
15884                           //resizeTabs: true,
15885                          alwaysShowTabs: el && hp? false :  true,
15886                          hideTabs: el || !hp ? true :  false,
15887                          minTabWidth: 140
15888                      }
15889                  })
15890             }
15891         }
15892         
15893                 if (!this.parent.el) {
15894                         // probably an old style ctor, which has been disabled.
15895                         return;
15896                         
15897                 }
15898                 // The 'tree' method is  '_tree now' 
15899             
15900         var tree = this._tree ? this._tree() : this.tree();
15901         tree.region = tree.region || this.region;
15902         
15903         if (this.parent.el === true) {
15904             // bootstrap... - body..
15905             this.parent.el = Roo.factory(tree);
15906         }
15907         
15908         this.el = this.parent.el.addxtype(tree);
15909         this.fireEvent('built', this);
15910         
15911         this.panel = this.el;
15912         this.layout = this.panel.layout;
15913                 this.parentLayout = this.parent.layout  || false;  
15914          
15915     }
15916     
15917 });
15918
15919 Roo.apply(Roo.XComponent, {
15920     /**
15921      * @property  hideProgress
15922      * true to disable the building progress bar.. usefull on single page renders.
15923      * @type Boolean
15924      */
15925     hideProgress : false,
15926     /**
15927      * @property  buildCompleted
15928      * True when the builder has completed building the interface.
15929      * @type Boolean
15930      */
15931     buildCompleted : false,
15932      
15933     /**
15934      * @property  topModule
15935      * the upper most module - uses document.element as it's constructor.
15936      * @type Object
15937      */
15938      
15939     topModule  : false,
15940       
15941     /**
15942      * @property  modules
15943      * array of modules to be created by registration system.
15944      * @type {Array} of Roo.XComponent
15945      */
15946     
15947     modules : [],
15948     /**
15949      * @property  elmodules
15950      * array of modules to be created by which use #ID 
15951      * @type {Array} of Roo.XComponent
15952      */
15953      
15954     elmodules : [],
15955
15956      /**
15957      * @property  build_from_html
15958      * Build elements from html - used by bootstrap HTML stuff 
15959      *    - this is cleared after build is completed
15960      * @type {boolean} true  (default false)
15961      */
15962      
15963     build_from_html : false,
15964
15965     /**
15966      * Register components to be built later.
15967      *
15968      * This solves the following issues
15969      * - Building is not done on page load, but after an authentication process has occured.
15970      * - Interface elements are registered on page load
15971      * - Parent Interface elements may not be loaded before child, so this handles that..
15972      * 
15973      *
15974      * example:
15975      * 
15976      * MyApp.register({
15977           order : '000001',
15978           module : 'Pman.Tab.projectMgr',
15979           region : 'center',
15980           parent : 'Pman.layout',
15981           disabled : false,  // or use a function..
15982         })
15983      
15984      * * @param {Object} details about module
15985      */
15986     register : function(obj) {
15987                 
15988         Roo.XComponent.event.fireEvent('register', obj);
15989         switch(typeof(obj.disabled) ) {
15990                 
15991             case 'undefined':
15992                 break;
15993             
15994             case 'function':
15995                 if ( obj.disabled() ) {
15996                         return;
15997                 }
15998                 break;
15999             
16000             default:
16001                 if (obj.disabled) {
16002                         return;
16003                 }
16004                 break;
16005         }
16006                 
16007         this.modules.push(obj);
16008          
16009     },
16010     /**
16011      * convert a string to an object..
16012      * eg. 'AAA.BBB' -> finds AAA.BBB
16013
16014      */
16015     
16016     toObject : function(str)
16017     {
16018         if (!str || typeof(str) == 'object') {
16019             return str;
16020         }
16021         if (str.substring(0,1) == '#') {
16022             return str;
16023         }
16024
16025         var ar = str.split('.');
16026         var rt, o;
16027         rt = ar.shift();
16028             /** eval:var:o */
16029         try {
16030             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16031         } catch (e) {
16032             throw "Module not found : " + str;
16033         }
16034         
16035         if (o === false) {
16036             throw "Module not found : " + str;
16037         }
16038         Roo.each(ar, function(e) {
16039             if (typeof(o[e]) == 'undefined') {
16040                 throw "Module not found : " + str;
16041             }
16042             o = o[e];
16043         });
16044         
16045         return o;
16046         
16047     },
16048     
16049     
16050     /**
16051      * move modules into their correct place in the tree..
16052      * 
16053      */
16054     preBuild : function ()
16055     {
16056         var _t = this;
16057         Roo.each(this.modules , function (obj)
16058         {
16059             Roo.XComponent.event.fireEvent('beforebuild', obj);
16060             
16061             var opar = obj.parent;
16062             try { 
16063                 obj.parent = this.toObject(opar);
16064             } catch(e) {
16065                 Roo.log("parent:toObject failed: " + e.toString());
16066                 return;
16067             }
16068             
16069             if (!obj.parent) {
16070                 Roo.debug && Roo.log("GOT top level module");
16071                 Roo.debug && Roo.log(obj);
16072                 obj.modules = new Roo.util.MixedCollection(false, 
16073                     function(o) { return o.order + '' }
16074                 );
16075                 this.topModule = obj;
16076                 return;
16077             }
16078                         // parent is a string (usually a dom element name..)
16079             if (typeof(obj.parent) == 'string') {
16080                 this.elmodules.push(obj);
16081                 return;
16082             }
16083             if (obj.parent.constructor != Roo.XComponent) {
16084                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16085             }
16086             if (!obj.parent.modules) {
16087                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16088                     function(o) { return o.order + '' }
16089                 );
16090             }
16091             if (obj.parent.disabled) {
16092                 obj.disabled = true;
16093             }
16094             obj.parent.modules.add(obj);
16095         }, this);
16096     },
16097     
16098      /**
16099      * make a list of modules to build.
16100      * @return {Array} list of modules. 
16101      */ 
16102     
16103     buildOrder : function()
16104     {
16105         var _this = this;
16106         var cmp = function(a,b) {   
16107             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16108         };
16109         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16110             throw "No top level modules to build";
16111         }
16112         
16113         // make a flat list in order of modules to build.
16114         var mods = this.topModule ? [ this.topModule ] : [];
16115                 
16116         
16117         // elmodules (is a list of DOM based modules )
16118         Roo.each(this.elmodules, function(e) {
16119             mods.push(e);
16120             if (!this.topModule &&
16121                 typeof(e.parent) == 'string' &&
16122                 e.parent.substring(0,1) == '#' &&
16123                 Roo.get(e.parent.substr(1))
16124                ) {
16125                 
16126                 _this.topModule = e;
16127             }
16128             
16129         });
16130
16131         
16132         // add modules to their parents..
16133         var addMod = function(m) {
16134             Roo.debug && Roo.log("build Order: add: " + m.name);
16135                 
16136             mods.push(m);
16137             if (m.modules && !m.disabled) {
16138                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16139                 m.modules.keySort('ASC',  cmp );
16140                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16141     
16142                 m.modules.each(addMod);
16143             } else {
16144                 Roo.debug && Roo.log("build Order: no child modules");
16145             }
16146             // not sure if this is used any more..
16147             if (m.finalize) {
16148                 m.finalize.name = m.name + " (clean up) ";
16149                 mods.push(m.finalize);
16150             }
16151             
16152         }
16153         if (this.topModule && this.topModule.modules) { 
16154             this.topModule.modules.keySort('ASC',  cmp );
16155             this.topModule.modules.each(addMod);
16156         } 
16157         return mods;
16158     },
16159     
16160      /**
16161      * Build the registered modules.
16162      * @param {Object} parent element.
16163      * @param {Function} optional method to call after module has been added.
16164      * 
16165      */ 
16166    
16167     build : function(opts) 
16168     {
16169         
16170         if (typeof(opts) != 'undefined') {
16171             Roo.apply(this,opts);
16172         }
16173         
16174         this.preBuild();
16175         var mods = this.buildOrder();
16176       
16177         //this.allmods = mods;
16178         //Roo.debug && Roo.log(mods);
16179         //return;
16180         if (!mods.length) { // should not happen
16181             throw "NO modules!!!";
16182         }
16183         
16184         
16185         var msg = "Building Interface...";
16186         // flash it up as modal - so we store the mask!?
16187         if (!this.hideProgress && Roo.MessageBox) {
16188             Roo.MessageBox.show({ title: 'loading' });
16189             Roo.MessageBox.show({
16190                title: "Please wait...",
16191                msg: msg,
16192                width:450,
16193                progress:true,
16194                closable:false,
16195                modal: false
16196               
16197             });
16198         }
16199         var total = mods.length;
16200         
16201         var _this = this;
16202         var progressRun = function() {
16203             if (!mods.length) {
16204                 Roo.debug && Roo.log('hide?');
16205                 if (!this.hideProgress && Roo.MessageBox) {
16206                     Roo.MessageBox.hide();
16207                 }
16208                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16209                 
16210                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16211                 
16212                 // THE END...
16213                 return false;   
16214             }
16215             
16216             var m = mods.shift();
16217             
16218             
16219             Roo.debug && Roo.log(m);
16220             // not sure if this is supported any more.. - modules that are are just function
16221             if (typeof(m) == 'function') { 
16222                 m.call(this);
16223                 return progressRun.defer(10, _this);
16224             } 
16225             
16226             
16227             msg = "Building Interface " + (total  - mods.length) + 
16228                     " of " + total + 
16229                     (m.name ? (' - ' + m.name) : '');
16230                         Roo.debug && Roo.log(msg);
16231             if (!this.hideProgress &&  Roo.MessageBox) { 
16232                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16233             }
16234             
16235          
16236             // is the module disabled?
16237             var disabled = (typeof(m.disabled) == 'function') ?
16238                 m.disabled.call(m.module.disabled) : m.disabled;    
16239             
16240             
16241             if (disabled) {
16242                 return progressRun(); // we do not update the display!
16243             }
16244             
16245             // now build 
16246             
16247                         
16248                         
16249             m.render();
16250             // it's 10 on top level, and 1 on others??? why...
16251             return progressRun.defer(10, _this);
16252              
16253         }
16254         progressRun.defer(1, _this);
16255      
16256         
16257         
16258     },
16259         
16260         
16261         /**
16262          * Event Object.
16263          *
16264          *
16265          */
16266         event: false, 
16267     /**
16268          * wrapper for event.on - aliased later..  
16269          * Typically use to register a event handler for register:
16270          *
16271          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16272          *
16273          */
16274     on : false
16275    
16276     
16277     
16278 });
16279
16280 Roo.XComponent.event = new Roo.util.Observable({
16281                 events : { 
16282                         /**
16283                          * @event register
16284                          * Fires when an Component is registered,
16285                          * set the disable property on the Component to stop registration.
16286                          * @param {Roo.XComponent} c the component being registerd.
16287                          * 
16288                          */
16289                         'register' : true,
16290             /**
16291                          * @event beforebuild
16292                          * Fires before each Component is built
16293                          * can be used to apply permissions.
16294                          * @param {Roo.XComponent} c the component being registerd.
16295                          * 
16296                          */
16297                         'beforebuild' : true,
16298                         /**
16299                          * @event buildcomplete
16300                          * Fires on the top level element when all elements have been built
16301                          * @param {Roo.XComponent} the top level component.
16302                          */
16303                         'buildcomplete' : true
16304                         
16305                 }
16306 });
16307
16308 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16309  /*
16310  * Based on:
16311  * Ext JS Library 1.1.1
16312  * Copyright(c) 2006-2007, Ext JS, LLC.
16313  *
16314  * Originally Released Under LGPL - original licence link has changed is not relivant.
16315  *
16316  * Fork - LGPL
16317  * <script type="text/javascript">
16318  */
16319
16320
16321
16322 /*
16323  * These classes are derivatives of the similarly named classes in the YUI Library.
16324  * The original license:
16325  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16326  * Code licensed under the BSD License:
16327  * http://developer.yahoo.net/yui/license.txt
16328  */
16329
16330 (function() {
16331
16332 var Event=Roo.EventManager;
16333 var Dom=Roo.lib.Dom;
16334
16335 /**
16336  * @class Roo.dd.DragDrop
16337  * @extends Roo.util.Observable
16338  * Defines the interface and base operation of items that that can be
16339  * dragged or can be drop targets.  It was designed to be extended, overriding
16340  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16341  * Up to three html elements can be associated with a DragDrop instance:
16342  * <ul>
16343  * <li>linked element: the element that is passed into the constructor.
16344  * This is the element which defines the boundaries for interaction with
16345  * other DragDrop objects.</li>
16346  * <li>handle element(s): The drag operation only occurs if the element that
16347  * was clicked matches a handle element.  By default this is the linked
16348  * element, but there are times that you will want only a portion of the
16349  * linked element to initiate the drag operation, and the setHandleElId()
16350  * method provides a way to define this.</li>
16351  * <li>drag element: this represents the element that would be moved along
16352  * with the cursor during a drag operation.  By default, this is the linked
16353  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16354  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16355  * </li>
16356  * </ul>
16357  * This class should not be instantiated until the onload event to ensure that
16358  * the associated elements are available.
16359  * The following would define a DragDrop obj that would interact with any
16360  * other DragDrop obj in the "group1" group:
16361  * <pre>
16362  *  dd = new Roo.dd.DragDrop("div1", "group1");
16363  * </pre>
16364  * Since none of the event handlers have been implemented, nothing would
16365  * actually happen if you were to run the code above.  Normally you would
16366  * override this class or one of the default implementations, but you can
16367  * also override the methods you want on an instance of the class...
16368  * <pre>
16369  *  dd.onDragDrop = function(e, id) {
16370  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16371  *  }
16372  * </pre>
16373  * @constructor
16374  * @param {String} id of the element that is linked to this instance
16375  * @param {String} sGroup the group of related DragDrop objects
16376  * @param {object} config an object containing configurable attributes
16377  *                Valid properties for DragDrop:
16378  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16379  */
16380 Roo.dd.DragDrop = function(id, sGroup, config) {
16381     if (id) {
16382         this.init(id, sGroup, config);
16383     }
16384     
16385 };
16386
16387 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16388
16389     /**
16390      * The id of the element associated with this object.  This is what we
16391      * refer to as the "linked element" because the size and position of
16392      * this element is used to determine when the drag and drop objects have
16393      * interacted.
16394      * @property id
16395      * @type String
16396      */
16397     id: null,
16398
16399     /**
16400      * Configuration attributes passed into the constructor
16401      * @property config
16402      * @type object
16403      */
16404     config: null,
16405
16406     /**
16407      * The id of the element that will be dragged.  By default this is same
16408      * as the linked element , but could be changed to another element. Ex:
16409      * Roo.dd.DDProxy
16410      * @property dragElId
16411      * @type String
16412      * @private
16413      */
16414     dragElId: null,
16415
16416     /**
16417      * the id of the element that initiates the drag operation.  By default
16418      * this is the linked element, but could be changed to be a child of this
16419      * element.  This lets us do things like only starting the drag when the
16420      * header element within the linked html element is clicked.
16421      * @property handleElId
16422      * @type String
16423      * @private
16424      */
16425     handleElId: null,
16426
16427     /**
16428      * An associative array of HTML tags that will be ignored if clicked.
16429      * @property invalidHandleTypes
16430      * @type {string: string}
16431      */
16432     invalidHandleTypes: null,
16433
16434     /**
16435      * An associative array of ids for elements that will be ignored if clicked
16436      * @property invalidHandleIds
16437      * @type {string: string}
16438      */
16439     invalidHandleIds: null,
16440
16441     /**
16442      * An indexted array of css class names for elements that will be ignored
16443      * if clicked.
16444      * @property invalidHandleClasses
16445      * @type string[]
16446      */
16447     invalidHandleClasses: null,
16448
16449     /**
16450      * The linked element's absolute X position at the time the drag was
16451      * started
16452      * @property startPageX
16453      * @type int
16454      * @private
16455      */
16456     startPageX: 0,
16457
16458     /**
16459      * The linked element's absolute X position at the time the drag was
16460      * started
16461      * @property startPageY
16462      * @type int
16463      * @private
16464      */
16465     startPageY: 0,
16466
16467     /**
16468      * The group defines a logical collection of DragDrop objects that are
16469      * related.  Instances only get events when interacting with other
16470      * DragDrop object in the same group.  This lets us define multiple
16471      * groups using a single DragDrop subclass if we want.
16472      * @property groups
16473      * @type {string: string}
16474      */
16475     groups: null,
16476
16477     /**
16478      * Individual drag/drop instances can be locked.  This will prevent
16479      * onmousedown start drag.
16480      * @property locked
16481      * @type boolean
16482      * @private
16483      */
16484     locked: false,
16485
16486     /**
16487      * Lock this instance
16488      * @method lock
16489      */
16490     lock: function() { this.locked = true; },
16491
16492     /**
16493      * Unlock this instace
16494      * @method unlock
16495      */
16496     unlock: function() { this.locked = false; },
16497
16498     /**
16499      * By default, all insances can be a drop target.  This can be disabled by
16500      * setting isTarget to false.
16501      * @method isTarget
16502      * @type boolean
16503      */
16504     isTarget: true,
16505
16506     /**
16507      * The padding configured for this drag and drop object for calculating
16508      * the drop zone intersection with this object.
16509      * @method padding
16510      * @type int[]
16511      */
16512     padding: null,
16513
16514     /**
16515      * Cached reference to the linked element
16516      * @property _domRef
16517      * @private
16518      */
16519     _domRef: null,
16520
16521     /**
16522      * Internal typeof flag
16523      * @property __ygDragDrop
16524      * @private
16525      */
16526     __ygDragDrop: true,
16527
16528     /**
16529      * Set to true when horizontal contraints are applied
16530      * @property constrainX
16531      * @type boolean
16532      * @private
16533      */
16534     constrainX: false,
16535
16536     /**
16537      * Set to true when vertical contraints are applied
16538      * @property constrainY
16539      * @type boolean
16540      * @private
16541      */
16542     constrainY: false,
16543
16544     /**
16545      * The left constraint
16546      * @property minX
16547      * @type int
16548      * @private
16549      */
16550     minX: 0,
16551
16552     /**
16553      * The right constraint
16554      * @property maxX
16555      * @type int
16556      * @private
16557      */
16558     maxX: 0,
16559
16560     /**
16561      * The up constraint
16562      * @property minY
16563      * @type int
16564      * @type int
16565      * @private
16566      */
16567     minY: 0,
16568
16569     /**
16570      * The down constraint
16571      * @property maxY
16572      * @type int
16573      * @private
16574      */
16575     maxY: 0,
16576
16577     /**
16578      * Maintain offsets when we resetconstraints.  Set to true when you want
16579      * the position of the element relative to its parent to stay the same
16580      * when the page changes
16581      *
16582      * @property maintainOffset
16583      * @type boolean
16584      */
16585     maintainOffset: false,
16586
16587     /**
16588      * Array of pixel locations the element will snap to if we specified a
16589      * horizontal graduation/interval.  This array is generated automatically
16590      * when you define a tick interval.
16591      * @property xTicks
16592      * @type int[]
16593      */
16594     xTicks: null,
16595
16596     /**
16597      * Array of pixel locations the element will snap to if we specified a
16598      * vertical graduation/interval.  This array is generated automatically
16599      * when you define a tick interval.
16600      * @property yTicks
16601      * @type int[]
16602      */
16603     yTicks: null,
16604
16605     /**
16606      * By default the drag and drop instance will only respond to the primary
16607      * button click (left button for a right-handed mouse).  Set to true to
16608      * allow drag and drop to start with any mouse click that is propogated
16609      * by the browser
16610      * @property primaryButtonOnly
16611      * @type boolean
16612      */
16613     primaryButtonOnly: true,
16614
16615     /**
16616      * The availabe property is false until the linked dom element is accessible.
16617      * @property available
16618      * @type boolean
16619      */
16620     available: false,
16621
16622     /**
16623      * By default, drags can only be initiated if the mousedown occurs in the
16624      * region the linked element is.  This is done in part to work around a
16625      * bug in some browsers that mis-report the mousedown if the previous
16626      * mouseup happened outside of the window.  This property is set to true
16627      * if outer handles are defined.
16628      *
16629      * @property hasOuterHandles
16630      * @type boolean
16631      * @default false
16632      */
16633     hasOuterHandles: false,
16634
16635     /**
16636      * Code that executes immediately before the startDrag event
16637      * @method b4StartDrag
16638      * @private
16639      */
16640     b4StartDrag: function(x, y) { },
16641
16642     /**
16643      * Abstract method called after a drag/drop object is clicked
16644      * and the drag or mousedown time thresholds have beeen met.
16645      * @method startDrag
16646      * @param {int} X click location
16647      * @param {int} Y click location
16648      */
16649     startDrag: function(x, y) { /* override this */ },
16650
16651     /**
16652      * Code that executes immediately before the onDrag event
16653      * @method b4Drag
16654      * @private
16655      */
16656     b4Drag: function(e) { },
16657
16658     /**
16659      * Abstract method called during the onMouseMove event while dragging an
16660      * object.
16661      * @method onDrag
16662      * @param {Event} e the mousemove event
16663      */
16664     onDrag: function(e) { /* override this */ },
16665
16666     /**
16667      * Abstract method called when this element fist begins hovering over
16668      * another DragDrop obj
16669      * @method onDragEnter
16670      * @param {Event} e the mousemove event
16671      * @param {String|DragDrop[]} id In POINT mode, the element
16672      * id this is hovering over.  In INTERSECT mode, an array of one or more
16673      * dragdrop items being hovered over.
16674      */
16675     onDragEnter: function(e, id) { /* override this */ },
16676
16677     /**
16678      * Code that executes immediately before the onDragOver event
16679      * @method b4DragOver
16680      * @private
16681      */
16682     b4DragOver: function(e) { },
16683
16684     /**
16685      * Abstract method called when this element is hovering over another
16686      * DragDrop obj
16687      * @method onDragOver
16688      * @param {Event} e the mousemove event
16689      * @param {String|DragDrop[]} id In POINT mode, the element
16690      * id this is hovering over.  In INTERSECT mode, an array of dd items
16691      * being hovered over.
16692      */
16693     onDragOver: function(e, id) { /* override this */ },
16694
16695     /**
16696      * Code that executes immediately before the onDragOut event
16697      * @method b4DragOut
16698      * @private
16699      */
16700     b4DragOut: function(e) { },
16701
16702     /**
16703      * Abstract method called when we are no longer hovering over an element
16704      * @method onDragOut
16705      * @param {Event} e the mousemove event
16706      * @param {String|DragDrop[]} id In POINT mode, the element
16707      * id this was hovering over.  In INTERSECT mode, an array of dd items
16708      * that the mouse is no longer over.
16709      */
16710     onDragOut: function(e, id) { /* override this */ },
16711
16712     /**
16713      * Code that executes immediately before the onDragDrop event
16714      * @method b4DragDrop
16715      * @private
16716      */
16717     b4DragDrop: function(e) { },
16718
16719     /**
16720      * Abstract method called when this item is dropped on another DragDrop
16721      * obj
16722      * @method onDragDrop
16723      * @param {Event} e the mouseup event
16724      * @param {String|DragDrop[]} id In POINT mode, the element
16725      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16726      * was dropped on.
16727      */
16728     onDragDrop: function(e, id) { /* override this */ },
16729
16730     /**
16731      * Abstract method called when this item is dropped on an area with no
16732      * drop target
16733      * @method onInvalidDrop
16734      * @param {Event} e the mouseup event
16735      */
16736     onInvalidDrop: function(e) { /* override this */ },
16737
16738     /**
16739      * Code that executes immediately before the endDrag event
16740      * @method b4EndDrag
16741      * @private
16742      */
16743     b4EndDrag: function(e) { },
16744
16745     /**
16746      * Fired when we are done dragging the object
16747      * @method endDrag
16748      * @param {Event} e the mouseup event
16749      */
16750     endDrag: function(e) { /* override this */ },
16751
16752     /**
16753      * Code executed immediately before the onMouseDown event
16754      * @method b4MouseDown
16755      * @param {Event} e the mousedown event
16756      * @private
16757      */
16758     b4MouseDown: function(e) {  },
16759
16760     /**
16761      * Event handler that fires when a drag/drop obj gets a mousedown
16762      * @method onMouseDown
16763      * @param {Event} e the mousedown event
16764      */
16765     onMouseDown: function(e) { /* override this */ },
16766
16767     /**
16768      * Event handler that fires when a drag/drop obj gets a mouseup
16769      * @method onMouseUp
16770      * @param {Event} e the mouseup event
16771      */
16772     onMouseUp: function(e) { /* override this */ },
16773
16774     /**
16775      * Override the onAvailable method to do what is needed after the initial
16776      * position was determined.
16777      * @method onAvailable
16778      */
16779     onAvailable: function () {
16780     },
16781
16782     /*
16783      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16784      * @type Object
16785      */
16786     defaultPadding : {left:0, right:0, top:0, bottom:0},
16787
16788     /*
16789      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16790  *
16791  * Usage:
16792  <pre><code>
16793  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16794                 { dragElId: "existingProxyDiv" });
16795  dd.startDrag = function(){
16796      this.constrainTo("parent-id");
16797  };
16798  </code></pre>
16799  * Or you can initalize it using the {@link Roo.Element} object:
16800  <pre><code>
16801  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16802      startDrag : function(){
16803          this.constrainTo("parent-id");
16804      }
16805  });
16806  </code></pre>
16807      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16808      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16809      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16810      * an object containing the sides to pad. For example: {right:10, bottom:10}
16811      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16812      */
16813     constrainTo : function(constrainTo, pad, inContent){
16814         if(typeof pad == "number"){
16815             pad = {left: pad, right:pad, top:pad, bottom:pad};
16816         }
16817         pad = pad || this.defaultPadding;
16818         var b = Roo.get(this.getEl()).getBox();
16819         var ce = Roo.get(constrainTo);
16820         var s = ce.getScroll();
16821         var c, cd = ce.dom;
16822         if(cd == document.body){
16823             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16824         }else{
16825             xy = ce.getXY();
16826             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16827         }
16828
16829
16830         var topSpace = b.y - c.y;
16831         var leftSpace = b.x - c.x;
16832
16833         this.resetConstraints();
16834         this.setXConstraint(leftSpace - (pad.left||0), // left
16835                 c.width - leftSpace - b.width - (pad.right||0) //right
16836         );
16837         this.setYConstraint(topSpace - (pad.top||0), //top
16838                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16839         );
16840     },
16841
16842     /**
16843      * Returns a reference to the linked element
16844      * @method getEl
16845      * @return {HTMLElement} the html element
16846      */
16847     getEl: function() {
16848         if (!this._domRef) {
16849             this._domRef = Roo.getDom(this.id);
16850         }
16851
16852         return this._domRef;
16853     },
16854
16855     /**
16856      * Returns a reference to the actual element to drag.  By default this is
16857      * the same as the html element, but it can be assigned to another
16858      * element. An example of this can be found in Roo.dd.DDProxy
16859      * @method getDragEl
16860      * @return {HTMLElement} the html element
16861      */
16862     getDragEl: function() {
16863         return Roo.getDom(this.dragElId);
16864     },
16865
16866     /**
16867      * Sets up the DragDrop object.  Must be called in the constructor of any
16868      * Roo.dd.DragDrop subclass
16869      * @method init
16870      * @param id the id of the linked element
16871      * @param {String} sGroup the group of related items
16872      * @param {object} config configuration attributes
16873      */
16874     init: function(id, sGroup, config) {
16875         this.initTarget(id, sGroup, config);
16876         if (!Roo.isTouch) {
16877             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16878         }
16879         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16880         // Event.on(this.id, "selectstart", Event.preventDefault);
16881     },
16882
16883     /**
16884      * Initializes Targeting functionality only... the object does not
16885      * get a mousedown handler.
16886      * @method initTarget
16887      * @param id the id of the linked element
16888      * @param {String} sGroup the group of related items
16889      * @param {object} config configuration attributes
16890      */
16891     initTarget: function(id, sGroup, config) {
16892
16893         // configuration attributes
16894         this.config = config || {};
16895
16896         // create a local reference to the drag and drop manager
16897         this.DDM = Roo.dd.DDM;
16898         // initialize the groups array
16899         this.groups = {};
16900
16901         // assume that we have an element reference instead of an id if the
16902         // parameter is not a string
16903         if (typeof id !== "string") {
16904             id = Roo.id(id);
16905         }
16906
16907         // set the id
16908         this.id = id;
16909
16910         // add to an interaction group
16911         this.addToGroup((sGroup) ? sGroup : "default");
16912
16913         // We don't want to register this as the handle with the manager
16914         // so we just set the id rather than calling the setter.
16915         this.handleElId = id;
16916
16917         // the linked element is the element that gets dragged by default
16918         this.setDragElId(id);
16919
16920         // by default, clicked anchors will not start drag operations.
16921         this.invalidHandleTypes = { A: "A" };
16922         this.invalidHandleIds = {};
16923         this.invalidHandleClasses = [];
16924
16925         this.applyConfig();
16926
16927         this.handleOnAvailable();
16928     },
16929
16930     /**
16931      * Applies the configuration parameters that were passed into the constructor.
16932      * This is supposed to happen at each level through the inheritance chain.  So
16933      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16934      * DragDrop in order to get all of the parameters that are available in
16935      * each object.
16936      * @method applyConfig
16937      */
16938     applyConfig: function() {
16939
16940         // configurable properties:
16941         //    padding, isTarget, maintainOffset, primaryButtonOnly
16942         this.padding           = this.config.padding || [0, 0, 0, 0];
16943         this.isTarget          = (this.config.isTarget !== false);
16944         this.maintainOffset    = (this.config.maintainOffset);
16945         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16946
16947     },
16948
16949     /**
16950      * Executed when the linked element is available
16951      * @method handleOnAvailable
16952      * @private
16953      */
16954     handleOnAvailable: function() {
16955         this.available = true;
16956         this.resetConstraints();
16957         this.onAvailable();
16958     },
16959
16960      /**
16961      * Configures the padding for the target zone in px.  Effectively expands
16962      * (or reduces) the virtual object size for targeting calculations.
16963      * Supports css-style shorthand; if only one parameter is passed, all sides
16964      * will have that padding, and if only two are passed, the top and bottom
16965      * will have the first param, the left and right the second.
16966      * @method setPadding
16967      * @param {int} iTop    Top pad
16968      * @param {int} iRight  Right pad
16969      * @param {int} iBot    Bot pad
16970      * @param {int} iLeft   Left pad
16971      */
16972     setPadding: function(iTop, iRight, iBot, iLeft) {
16973         // this.padding = [iLeft, iRight, iTop, iBot];
16974         if (!iRight && 0 !== iRight) {
16975             this.padding = [iTop, iTop, iTop, iTop];
16976         } else if (!iBot && 0 !== iBot) {
16977             this.padding = [iTop, iRight, iTop, iRight];
16978         } else {
16979             this.padding = [iTop, iRight, iBot, iLeft];
16980         }
16981     },
16982
16983     /**
16984      * Stores the initial placement of the linked element.
16985      * @method setInitialPosition
16986      * @param {int} diffX   the X offset, default 0
16987      * @param {int} diffY   the Y offset, default 0
16988      */
16989     setInitPosition: function(diffX, diffY) {
16990         var el = this.getEl();
16991
16992         if (!this.DDM.verifyEl(el)) {
16993             return;
16994         }
16995
16996         var dx = diffX || 0;
16997         var dy = diffY || 0;
16998
16999         var p = Dom.getXY( el );
17000
17001         this.initPageX = p[0] - dx;
17002         this.initPageY = p[1] - dy;
17003
17004         this.lastPageX = p[0];
17005         this.lastPageY = p[1];
17006
17007
17008         this.setStartPosition(p);
17009     },
17010
17011     /**
17012      * Sets the start position of the element.  This is set when the obj
17013      * is initialized, the reset when a drag is started.
17014      * @method setStartPosition
17015      * @param pos current position (from previous lookup)
17016      * @private
17017      */
17018     setStartPosition: function(pos) {
17019         var p = pos || Dom.getXY( this.getEl() );
17020         this.deltaSetXY = null;
17021
17022         this.startPageX = p[0];
17023         this.startPageY = p[1];
17024     },
17025
17026     /**
17027      * Add this instance to a group of related drag/drop objects.  All
17028      * instances belong to at least one group, and can belong to as many
17029      * groups as needed.
17030      * @method addToGroup
17031      * @param sGroup {string} the name of the group
17032      */
17033     addToGroup: function(sGroup) {
17034         this.groups[sGroup] = true;
17035         this.DDM.regDragDrop(this, sGroup);
17036     },
17037
17038     /**
17039      * Remove's this instance from the supplied interaction group
17040      * @method removeFromGroup
17041      * @param {string}  sGroup  The group to drop
17042      */
17043     removeFromGroup: function(sGroup) {
17044         if (this.groups[sGroup]) {
17045             delete this.groups[sGroup];
17046         }
17047
17048         this.DDM.removeDDFromGroup(this, sGroup);
17049     },
17050
17051     /**
17052      * Allows you to specify that an element other than the linked element
17053      * will be moved with the cursor during a drag
17054      * @method setDragElId
17055      * @param id {string} the id of the element that will be used to initiate the drag
17056      */
17057     setDragElId: function(id) {
17058         this.dragElId = id;
17059     },
17060
17061     /**
17062      * Allows you to specify a child of the linked element that should be
17063      * used to initiate the drag operation.  An example of this would be if
17064      * you have a content div with text and links.  Clicking anywhere in the
17065      * content area would normally start the drag operation.  Use this method
17066      * to specify that an element inside of the content div is the element
17067      * that starts the drag operation.
17068      * @method setHandleElId
17069      * @param id {string} the id of the element that will be used to
17070      * initiate the drag.
17071      */
17072     setHandleElId: function(id) {
17073         if (typeof id !== "string") {
17074             id = Roo.id(id);
17075         }
17076         this.handleElId = id;
17077         this.DDM.regHandle(this.id, id);
17078     },
17079
17080     /**
17081      * Allows you to set an element outside of the linked element as a drag
17082      * handle
17083      * @method setOuterHandleElId
17084      * @param id the id of the element that will be used to initiate the drag
17085      */
17086     setOuterHandleElId: function(id) {
17087         if (typeof id !== "string") {
17088             id = Roo.id(id);
17089         }
17090         Event.on(id, "mousedown",
17091                 this.handleMouseDown, this);
17092         this.setHandleElId(id);
17093
17094         this.hasOuterHandles = true;
17095     },
17096
17097     /**
17098      * Remove all drag and drop hooks for this element
17099      * @method unreg
17100      */
17101     unreg: function() {
17102         Event.un(this.id, "mousedown",
17103                 this.handleMouseDown);
17104         Event.un(this.id, "touchstart",
17105                 this.handleMouseDown);
17106         this._domRef = null;
17107         this.DDM._remove(this);
17108     },
17109
17110     destroy : function(){
17111         this.unreg();
17112     },
17113
17114     /**
17115      * Returns true if this instance is locked, or the drag drop mgr is locked
17116      * (meaning that all drag/drop is disabled on the page.)
17117      * @method isLocked
17118      * @return {boolean} true if this obj or all drag/drop is locked, else
17119      * false
17120      */
17121     isLocked: function() {
17122         return (this.DDM.isLocked() || this.locked);
17123     },
17124
17125     /**
17126      * Fired when this object is clicked
17127      * @method handleMouseDown
17128      * @param {Event} e
17129      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17130      * @private
17131      */
17132     handleMouseDown: function(e, oDD){
17133      
17134         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17135             //Roo.log('not touch/ button !=0');
17136             return;
17137         }
17138         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17139             return; // double touch..
17140         }
17141         
17142
17143         if (this.isLocked()) {
17144             //Roo.log('locked');
17145             return;
17146         }
17147
17148         this.DDM.refreshCache(this.groups);
17149 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17150         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17151         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17152             //Roo.log('no outer handes or not over target');
17153                 // do nothing.
17154         } else {
17155 //            Roo.log('check validator');
17156             if (this.clickValidator(e)) {
17157 //                Roo.log('validate success');
17158                 // set the initial element position
17159                 this.setStartPosition();
17160
17161
17162                 this.b4MouseDown(e);
17163                 this.onMouseDown(e);
17164
17165                 this.DDM.handleMouseDown(e, this);
17166
17167                 this.DDM.stopEvent(e);
17168             } else {
17169
17170
17171             }
17172         }
17173     },
17174
17175     clickValidator: function(e) {
17176         var target = e.getTarget();
17177         return ( this.isValidHandleChild(target) &&
17178                     (this.id == this.handleElId ||
17179                         this.DDM.handleWasClicked(target, this.id)) );
17180     },
17181
17182     /**
17183      * Allows you to specify a tag name that should not start a drag operation
17184      * when clicked.  This is designed to facilitate embedding links within a
17185      * drag handle that do something other than start the drag.
17186      * @method addInvalidHandleType
17187      * @param {string} tagName the type of element to exclude
17188      */
17189     addInvalidHandleType: function(tagName) {
17190         var type = tagName.toUpperCase();
17191         this.invalidHandleTypes[type] = type;
17192     },
17193
17194     /**
17195      * Lets you to specify an element id for a child of a drag handle
17196      * that should not initiate a drag
17197      * @method addInvalidHandleId
17198      * @param {string} id the element id of the element you wish to ignore
17199      */
17200     addInvalidHandleId: function(id) {
17201         if (typeof id !== "string") {
17202             id = Roo.id(id);
17203         }
17204         this.invalidHandleIds[id] = id;
17205     },
17206
17207     /**
17208      * Lets you specify a css class of elements that will not initiate a drag
17209      * @method addInvalidHandleClass
17210      * @param {string} cssClass the class of the elements you wish to ignore
17211      */
17212     addInvalidHandleClass: function(cssClass) {
17213         this.invalidHandleClasses.push(cssClass);
17214     },
17215
17216     /**
17217      * Unsets an excluded tag name set by addInvalidHandleType
17218      * @method removeInvalidHandleType
17219      * @param {string} tagName the type of element to unexclude
17220      */
17221     removeInvalidHandleType: function(tagName) {
17222         var type = tagName.toUpperCase();
17223         // this.invalidHandleTypes[type] = null;
17224         delete this.invalidHandleTypes[type];
17225     },
17226
17227     /**
17228      * Unsets an invalid handle id
17229      * @method removeInvalidHandleId
17230      * @param {string} id the id of the element to re-enable
17231      */
17232     removeInvalidHandleId: function(id) {
17233         if (typeof id !== "string") {
17234             id = Roo.id(id);
17235         }
17236         delete this.invalidHandleIds[id];
17237     },
17238
17239     /**
17240      * Unsets an invalid css class
17241      * @method removeInvalidHandleClass
17242      * @param {string} cssClass the class of the element(s) you wish to
17243      * re-enable
17244      */
17245     removeInvalidHandleClass: function(cssClass) {
17246         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17247             if (this.invalidHandleClasses[i] == cssClass) {
17248                 delete this.invalidHandleClasses[i];
17249             }
17250         }
17251     },
17252
17253     /**
17254      * Checks the tag exclusion list to see if this click should be ignored
17255      * @method isValidHandleChild
17256      * @param {HTMLElement} node the HTMLElement to evaluate
17257      * @return {boolean} true if this is a valid tag type, false if not
17258      */
17259     isValidHandleChild: function(node) {
17260
17261         var valid = true;
17262         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17263         var nodeName;
17264         try {
17265             nodeName = node.nodeName.toUpperCase();
17266         } catch(e) {
17267             nodeName = node.nodeName;
17268         }
17269         valid = valid && !this.invalidHandleTypes[nodeName];
17270         valid = valid && !this.invalidHandleIds[node.id];
17271
17272         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17273             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17274         }
17275
17276
17277         return valid;
17278
17279     },
17280
17281     /**
17282      * Create the array of horizontal tick marks if an interval was specified
17283      * in setXConstraint().
17284      * @method setXTicks
17285      * @private
17286      */
17287     setXTicks: function(iStartX, iTickSize) {
17288         this.xTicks = [];
17289         this.xTickSize = iTickSize;
17290
17291         var tickMap = {};
17292
17293         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17294             if (!tickMap[i]) {
17295                 this.xTicks[this.xTicks.length] = i;
17296                 tickMap[i] = true;
17297             }
17298         }
17299
17300         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17301             if (!tickMap[i]) {
17302                 this.xTicks[this.xTicks.length] = i;
17303                 tickMap[i] = true;
17304             }
17305         }
17306
17307         this.xTicks.sort(this.DDM.numericSort) ;
17308     },
17309
17310     /**
17311      * Create the array of vertical tick marks if an interval was specified in
17312      * setYConstraint().
17313      * @method setYTicks
17314      * @private
17315      */
17316     setYTicks: function(iStartY, iTickSize) {
17317         this.yTicks = [];
17318         this.yTickSize = iTickSize;
17319
17320         var tickMap = {};
17321
17322         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17323             if (!tickMap[i]) {
17324                 this.yTicks[this.yTicks.length] = i;
17325                 tickMap[i] = true;
17326             }
17327         }
17328
17329         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17330             if (!tickMap[i]) {
17331                 this.yTicks[this.yTicks.length] = i;
17332                 tickMap[i] = true;
17333             }
17334         }
17335
17336         this.yTicks.sort(this.DDM.numericSort) ;
17337     },
17338
17339     /**
17340      * By default, the element can be dragged any place on the screen.  Use
17341      * this method to limit the horizontal travel of the element.  Pass in
17342      * 0,0 for the parameters if you want to lock the drag to the y axis.
17343      * @method setXConstraint
17344      * @param {int} iLeft the number of pixels the element can move to the left
17345      * @param {int} iRight the number of pixels the element can move to the
17346      * right
17347      * @param {int} iTickSize optional parameter for specifying that the
17348      * element
17349      * should move iTickSize pixels at a time.
17350      */
17351     setXConstraint: function(iLeft, iRight, iTickSize) {
17352         this.leftConstraint = iLeft;
17353         this.rightConstraint = iRight;
17354
17355         this.minX = this.initPageX - iLeft;
17356         this.maxX = this.initPageX + iRight;
17357         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17358
17359         this.constrainX = true;
17360     },
17361
17362     /**
17363      * Clears any constraints applied to this instance.  Also clears ticks
17364      * since they can't exist independent of a constraint at this time.
17365      * @method clearConstraints
17366      */
17367     clearConstraints: function() {
17368         this.constrainX = false;
17369         this.constrainY = false;
17370         this.clearTicks();
17371     },
17372
17373     /**
17374      * Clears any tick interval defined for this instance
17375      * @method clearTicks
17376      */
17377     clearTicks: function() {
17378         this.xTicks = null;
17379         this.yTicks = null;
17380         this.xTickSize = 0;
17381         this.yTickSize = 0;
17382     },
17383
17384     /**
17385      * By default, the element can be dragged any place on the screen.  Set
17386      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17387      * parameters if you want to lock the drag to the x axis.
17388      * @method setYConstraint
17389      * @param {int} iUp the number of pixels the element can move up
17390      * @param {int} iDown the number of pixels the element can move down
17391      * @param {int} iTickSize optional parameter for specifying that the
17392      * element should move iTickSize pixels at a time.
17393      */
17394     setYConstraint: function(iUp, iDown, iTickSize) {
17395         this.topConstraint = iUp;
17396         this.bottomConstraint = iDown;
17397
17398         this.minY = this.initPageY - iUp;
17399         this.maxY = this.initPageY + iDown;
17400         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17401
17402         this.constrainY = true;
17403
17404     },
17405
17406     /**
17407      * resetConstraints must be called if you manually reposition a dd element.
17408      * @method resetConstraints
17409      * @param {boolean} maintainOffset
17410      */
17411     resetConstraints: function() {
17412
17413
17414         // Maintain offsets if necessary
17415         if (this.initPageX || this.initPageX === 0) {
17416             // figure out how much this thing has moved
17417             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17418             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17419
17420             this.setInitPosition(dx, dy);
17421
17422         // This is the first time we have detected the element's position
17423         } else {
17424             this.setInitPosition();
17425         }
17426
17427         if (this.constrainX) {
17428             this.setXConstraint( this.leftConstraint,
17429                                  this.rightConstraint,
17430                                  this.xTickSize        );
17431         }
17432
17433         if (this.constrainY) {
17434             this.setYConstraint( this.topConstraint,
17435                                  this.bottomConstraint,
17436                                  this.yTickSize         );
17437         }
17438     },
17439
17440     /**
17441      * Normally the drag element is moved pixel by pixel, but we can specify
17442      * that it move a number of pixels at a time.  This method resolves the
17443      * location when we have it set up like this.
17444      * @method getTick
17445      * @param {int} val where we want to place the object
17446      * @param {int[]} tickArray sorted array of valid points
17447      * @return {int} the closest tick
17448      * @private
17449      */
17450     getTick: function(val, tickArray) {
17451
17452         if (!tickArray) {
17453             // If tick interval is not defined, it is effectively 1 pixel,
17454             // so we return the value passed to us.
17455             return val;
17456         } else if (tickArray[0] >= val) {
17457             // The value is lower than the first tick, so we return the first
17458             // tick.
17459             return tickArray[0];
17460         } else {
17461             for (var i=0, len=tickArray.length; i<len; ++i) {
17462                 var next = i + 1;
17463                 if (tickArray[next] && tickArray[next] >= val) {
17464                     var diff1 = val - tickArray[i];
17465                     var diff2 = tickArray[next] - val;
17466                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17467                 }
17468             }
17469
17470             // The value is larger than the last tick, so we return the last
17471             // tick.
17472             return tickArray[tickArray.length - 1];
17473         }
17474     },
17475
17476     /**
17477      * toString method
17478      * @method toString
17479      * @return {string} string representation of the dd obj
17480      */
17481     toString: function() {
17482         return ("DragDrop " + this.id);
17483     }
17484
17485 });
17486
17487 })();
17488 /*
17489  * Based on:
17490  * Ext JS Library 1.1.1
17491  * Copyright(c) 2006-2007, Ext JS, LLC.
17492  *
17493  * Originally Released Under LGPL - original licence link has changed is not relivant.
17494  *
17495  * Fork - LGPL
17496  * <script type="text/javascript">
17497  */
17498
17499
17500 /**
17501  * The drag and drop utility provides a framework for building drag and drop
17502  * applications.  In addition to enabling drag and drop for specific elements,
17503  * the drag and drop elements are tracked by the manager class, and the
17504  * interactions between the various elements are tracked during the drag and
17505  * the implementing code is notified about these important moments.
17506  */
17507
17508 // Only load the library once.  Rewriting the manager class would orphan
17509 // existing drag and drop instances.
17510 if (!Roo.dd.DragDropMgr) {
17511
17512 /**
17513  * @class Roo.dd.DragDropMgr
17514  * DragDropMgr is a singleton that tracks the element interaction for
17515  * all DragDrop items in the window.  Generally, you will not call
17516  * this class directly, but it does have helper methods that could
17517  * be useful in your DragDrop implementations.
17518  * @singleton
17519  */
17520 Roo.dd.DragDropMgr = function() {
17521
17522     var Event = Roo.EventManager;
17523
17524     return {
17525
17526         /**
17527          * Two dimensional Array of registered DragDrop objects.  The first
17528          * dimension is the DragDrop item group, the second the DragDrop
17529          * object.
17530          * @property ids
17531          * @type {string: string}
17532          * @private
17533          * @static
17534          */
17535         ids: {},
17536
17537         /**
17538          * Array of element ids defined as drag handles.  Used to determine
17539          * if the element that generated the mousedown event is actually the
17540          * handle and not the html element itself.
17541          * @property handleIds
17542          * @type {string: string}
17543          * @private
17544          * @static
17545          */
17546         handleIds: {},
17547
17548         /**
17549          * the DragDrop object that is currently being dragged
17550          * @property dragCurrent
17551          * @type DragDrop
17552          * @private
17553          * @static
17554          **/
17555         dragCurrent: null,
17556
17557         /**
17558          * the DragDrop object(s) that are being hovered over
17559          * @property dragOvers
17560          * @type Array
17561          * @private
17562          * @static
17563          */
17564         dragOvers: {},
17565
17566         /**
17567          * the X distance between the cursor and the object being dragged
17568          * @property deltaX
17569          * @type int
17570          * @private
17571          * @static
17572          */
17573         deltaX: 0,
17574
17575         /**
17576          * the Y distance between the cursor and the object being dragged
17577          * @property deltaY
17578          * @type int
17579          * @private
17580          * @static
17581          */
17582         deltaY: 0,
17583
17584         /**
17585          * Flag to determine if we should prevent the default behavior of the
17586          * events we define. By default this is true, but this can be set to
17587          * false if you need the default behavior (not recommended)
17588          * @property preventDefault
17589          * @type boolean
17590          * @static
17591          */
17592         preventDefault: true,
17593
17594         /**
17595          * Flag to determine if we should stop the propagation of the events
17596          * we generate. This is true by default but you may want to set it to
17597          * false if the html element contains other features that require the
17598          * mouse click.
17599          * @property stopPropagation
17600          * @type boolean
17601          * @static
17602          */
17603         stopPropagation: true,
17604
17605         /**
17606          * Internal flag that is set to true when drag and drop has been
17607          * intialized
17608          * @property initialized
17609          * @private
17610          * @static
17611          */
17612         initalized: false,
17613
17614         /**
17615          * All drag and drop can be disabled.
17616          * @property locked
17617          * @private
17618          * @static
17619          */
17620         locked: false,
17621
17622         /**
17623          * Called the first time an element is registered.
17624          * @method init
17625          * @private
17626          * @static
17627          */
17628         init: function() {
17629             this.initialized = true;
17630         },
17631
17632         /**
17633          * In point mode, drag and drop interaction is defined by the
17634          * location of the cursor during the drag/drop
17635          * @property POINT
17636          * @type int
17637          * @static
17638          */
17639         POINT: 0,
17640
17641         /**
17642          * In intersect mode, drag and drop interactio nis defined by the
17643          * overlap of two or more drag and drop objects.
17644          * @property INTERSECT
17645          * @type int
17646          * @static
17647          */
17648         INTERSECT: 1,
17649
17650         /**
17651          * The current drag and drop mode.  Default: POINT
17652          * @property mode
17653          * @type int
17654          * @static
17655          */
17656         mode: 0,
17657
17658         /**
17659          * Runs method on all drag and drop objects
17660          * @method _execOnAll
17661          * @private
17662          * @static
17663          */
17664         _execOnAll: function(sMethod, args) {
17665             for (var i in this.ids) {
17666                 for (var j in this.ids[i]) {
17667                     var oDD = this.ids[i][j];
17668                     if (! this.isTypeOfDD(oDD)) {
17669                         continue;
17670                     }
17671                     oDD[sMethod].apply(oDD, args);
17672                 }
17673             }
17674         },
17675
17676         /**
17677          * Drag and drop initialization.  Sets up the global event handlers
17678          * @method _onLoad
17679          * @private
17680          * @static
17681          */
17682         _onLoad: function() {
17683
17684             this.init();
17685
17686             if (!Roo.isTouch) {
17687                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17688                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17689             }
17690             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17691             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17692             
17693             Event.on(window,   "unload",    this._onUnload, this, true);
17694             Event.on(window,   "resize",    this._onResize, this, true);
17695             // Event.on(window,   "mouseout",    this._test);
17696
17697         },
17698
17699         /**
17700          * Reset constraints on all drag and drop objs
17701          * @method _onResize
17702          * @private
17703          * @static
17704          */
17705         _onResize: function(e) {
17706             this._execOnAll("resetConstraints", []);
17707         },
17708
17709         /**
17710          * Lock all drag and drop functionality
17711          * @method lock
17712          * @static
17713          */
17714         lock: function() { this.locked = true; },
17715
17716         /**
17717          * Unlock all drag and drop functionality
17718          * @method unlock
17719          * @static
17720          */
17721         unlock: function() { this.locked = false; },
17722
17723         /**
17724          * Is drag and drop locked?
17725          * @method isLocked
17726          * @return {boolean} True if drag and drop is locked, false otherwise.
17727          * @static
17728          */
17729         isLocked: function() { return this.locked; },
17730
17731         /**
17732          * Location cache that is set for all drag drop objects when a drag is
17733          * initiated, cleared when the drag is finished.
17734          * @property locationCache
17735          * @private
17736          * @static
17737          */
17738         locationCache: {},
17739
17740         /**
17741          * Set useCache to false if you want to force object the lookup of each
17742          * drag and drop linked element constantly during a drag.
17743          * @property useCache
17744          * @type boolean
17745          * @static
17746          */
17747         useCache: true,
17748
17749         /**
17750          * The number of pixels that the mouse needs to move after the
17751          * mousedown before the drag is initiated.  Default=3;
17752          * @property clickPixelThresh
17753          * @type int
17754          * @static
17755          */
17756         clickPixelThresh: 3,
17757
17758         /**
17759          * The number of milliseconds after the mousedown event to initiate the
17760          * drag if we don't get a mouseup event. Default=1000
17761          * @property clickTimeThresh
17762          * @type int
17763          * @static
17764          */
17765         clickTimeThresh: 350,
17766
17767         /**
17768          * Flag that indicates that either the drag pixel threshold or the
17769          * mousdown time threshold has been met
17770          * @property dragThreshMet
17771          * @type boolean
17772          * @private
17773          * @static
17774          */
17775         dragThreshMet: false,
17776
17777         /**
17778          * Timeout used for the click time threshold
17779          * @property clickTimeout
17780          * @type Object
17781          * @private
17782          * @static
17783          */
17784         clickTimeout: null,
17785
17786         /**
17787          * The X position of the mousedown event stored for later use when a
17788          * drag threshold is met.
17789          * @property startX
17790          * @type int
17791          * @private
17792          * @static
17793          */
17794         startX: 0,
17795
17796         /**
17797          * The Y position of the mousedown event stored for later use when a
17798          * drag threshold is met.
17799          * @property startY
17800          * @type int
17801          * @private
17802          * @static
17803          */
17804         startY: 0,
17805
17806         /**
17807          * Each DragDrop instance must be registered with the DragDropMgr.
17808          * This is executed in DragDrop.init()
17809          * @method regDragDrop
17810          * @param {DragDrop} oDD the DragDrop object to register
17811          * @param {String} sGroup the name of the group this element belongs to
17812          * @static
17813          */
17814         regDragDrop: function(oDD, sGroup) {
17815             if (!this.initialized) { this.init(); }
17816
17817             if (!this.ids[sGroup]) {
17818                 this.ids[sGroup] = {};
17819             }
17820             this.ids[sGroup][oDD.id] = oDD;
17821         },
17822
17823         /**
17824          * Removes the supplied dd instance from the supplied group. Executed
17825          * by DragDrop.removeFromGroup, so don't call this function directly.
17826          * @method removeDDFromGroup
17827          * @private
17828          * @static
17829          */
17830         removeDDFromGroup: function(oDD, sGroup) {
17831             if (!this.ids[sGroup]) {
17832                 this.ids[sGroup] = {};
17833             }
17834
17835             var obj = this.ids[sGroup];
17836             if (obj && obj[oDD.id]) {
17837                 delete obj[oDD.id];
17838             }
17839         },
17840
17841         /**
17842          * Unregisters a drag and drop item.  This is executed in
17843          * DragDrop.unreg, use that method instead of calling this directly.
17844          * @method _remove
17845          * @private
17846          * @static
17847          */
17848         _remove: function(oDD) {
17849             for (var g in oDD.groups) {
17850                 if (g && this.ids[g][oDD.id]) {
17851                     delete this.ids[g][oDD.id];
17852                 }
17853             }
17854             delete this.handleIds[oDD.id];
17855         },
17856
17857         /**
17858          * Each DragDrop handle element must be registered.  This is done
17859          * automatically when executing DragDrop.setHandleElId()
17860          * @method regHandle
17861          * @param {String} sDDId the DragDrop id this element is a handle for
17862          * @param {String} sHandleId the id of the element that is the drag
17863          * handle
17864          * @static
17865          */
17866         regHandle: function(sDDId, sHandleId) {
17867             if (!this.handleIds[sDDId]) {
17868                 this.handleIds[sDDId] = {};
17869             }
17870             this.handleIds[sDDId][sHandleId] = sHandleId;
17871         },
17872
17873         /**
17874          * Utility function to determine if a given element has been
17875          * registered as a drag drop item.
17876          * @method isDragDrop
17877          * @param {String} id the element id to check
17878          * @return {boolean} true if this element is a DragDrop item,
17879          * false otherwise
17880          * @static
17881          */
17882         isDragDrop: function(id) {
17883             return ( this.getDDById(id) ) ? true : false;
17884         },
17885
17886         /**
17887          * Returns the drag and drop instances that are in all groups the
17888          * passed in instance belongs to.
17889          * @method getRelated
17890          * @param {DragDrop} p_oDD the obj to get related data for
17891          * @param {boolean} bTargetsOnly if true, only return targetable objs
17892          * @return {DragDrop[]} the related instances
17893          * @static
17894          */
17895         getRelated: function(p_oDD, bTargetsOnly) {
17896             var oDDs = [];
17897             for (var i in p_oDD.groups) {
17898                 for (j in this.ids[i]) {
17899                     var dd = this.ids[i][j];
17900                     if (! this.isTypeOfDD(dd)) {
17901                         continue;
17902                     }
17903                     if (!bTargetsOnly || dd.isTarget) {
17904                         oDDs[oDDs.length] = dd;
17905                     }
17906                 }
17907             }
17908
17909             return oDDs;
17910         },
17911
17912         /**
17913          * Returns true if the specified dd target is a legal target for
17914          * the specifice drag obj
17915          * @method isLegalTarget
17916          * @param {DragDrop} the drag obj
17917          * @param {DragDrop} the target
17918          * @return {boolean} true if the target is a legal target for the
17919          * dd obj
17920          * @static
17921          */
17922         isLegalTarget: function (oDD, oTargetDD) {
17923             var targets = this.getRelated(oDD, true);
17924             for (var i=0, len=targets.length;i<len;++i) {
17925                 if (targets[i].id == oTargetDD.id) {
17926                     return true;
17927                 }
17928             }
17929
17930             return false;
17931         },
17932
17933         /**
17934          * My goal is to be able to transparently determine if an object is
17935          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17936          * returns "object", oDD.constructor.toString() always returns
17937          * "DragDrop" and not the name of the subclass.  So for now it just
17938          * evaluates a well-known variable in DragDrop.
17939          * @method isTypeOfDD
17940          * @param {Object} the object to evaluate
17941          * @return {boolean} true if typeof oDD = DragDrop
17942          * @static
17943          */
17944         isTypeOfDD: function (oDD) {
17945             return (oDD && oDD.__ygDragDrop);
17946         },
17947
17948         /**
17949          * Utility function to determine if a given element has been
17950          * registered as a drag drop handle for the given Drag Drop object.
17951          * @method isHandle
17952          * @param {String} id the element id to check
17953          * @return {boolean} true if this element is a DragDrop handle, false
17954          * otherwise
17955          * @static
17956          */
17957         isHandle: function(sDDId, sHandleId) {
17958             return ( this.handleIds[sDDId] &&
17959                             this.handleIds[sDDId][sHandleId] );
17960         },
17961
17962         /**
17963          * Returns the DragDrop instance for a given id
17964          * @method getDDById
17965          * @param {String} id the id of the DragDrop object
17966          * @return {DragDrop} the drag drop object, null if it is not found
17967          * @static
17968          */
17969         getDDById: function(id) {
17970             for (var i in this.ids) {
17971                 if (this.ids[i][id]) {
17972                     return this.ids[i][id];
17973                 }
17974             }
17975             return null;
17976         },
17977
17978         /**
17979          * Fired after a registered DragDrop object gets the mousedown event.
17980          * Sets up the events required to track the object being dragged
17981          * @method handleMouseDown
17982          * @param {Event} e the event
17983          * @param oDD the DragDrop object being dragged
17984          * @private
17985          * @static
17986          */
17987         handleMouseDown: function(e, oDD) {
17988             if(Roo.QuickTips){
17989                 Roo.QuickTips.disable();
17990             }
17991             this.currentTarget = e.getTarget();
17992
17993             this.dragCurrent = oDD;
17994
17995             var el = oDD.getEl();
17996
17997             // track start position
17998             this.startX = e.getPageX();
17999             this.startY = e.getPageY();
18000
18001             this.deltaX = this.startX - el.offsetLeft;
18002             this.deltaY = this.startY - el.offsetTop;
18003
18004             this.dragThreshMet = false;
18005
18006             this.clickTimeout = setTimeout(
18007                     function() {
18008                         var DDM = Roo.dd.DDM;
18009                         DDM.startDrag(DDM.startX, DDM.startY);
18010                     },
18011                     this.clickTimeThresh );
18012         },
18013
18014         /**
18015          * Fired when either the drag pixel threshol or the mousedown hold
18016          * time threshold has been met.
18017          * @method startDrag
18018          * @param x {int} the X position of the original mousedown
18019          * @param y {int} the Y position of the original mousedown
18020          * @static
18021          */
18022         startDrag: function(x, y) {
18023             clearTimeout(this.clickTimeout);
18024             if (this.dragCurrent) {
18025                 this.dragCurrent.b4StartDrag(x, y);
18026                 this.dragCurrent.startDrag(x, y);
18027             }
18028             this.dragThreshMet = true;
18029         },
18030
18031         /**
18032          * Internal function to handle the mouseup event.  Will be invoked
18033          * from the context of the document.
18034          * @method handleMouseUp
18035          * @param {Event} e the event
18036          * @private
18037          * @static
18038          */
18039         handleMouseUp: function(e) {
18040
18041             if(Roo.QuickTips){
18042                 Roo.QuickTips.enable();
18043             }
18044             if (! this.dragCurrent) {
18045                 return;
18046             }
18047
18048             clearTimeout(this.clickTimeout);
18049
18050             if (this.dragThreshMet) {
18051                 this.fireEvents(e, true);
18052             } else {
18053             }
18054
18055             this.stopDrag(e);
18056
18057             this.stopEvent(e);
18058         },
18059
18060         /**
18061          * Utility to stop event propagation and event default, if these
18062          * features are turned on.
18063          * @method stopEvent
18064          * @param {Event} e the event as returned by this.getEvent()
18065          * @static
18066          */
18067         stopEvent: function(e){
18068             if(this.stopPropagation) {
18069                 e.stopPropagation();
18070             }
18071
18072             if (this.preventDefault) {
18073                 e.preventDefault();
18074             }
18075         },
18076
18077         /**
18078          * Internal function to clean up event handlers after the drag
18079          * operation is complete
18080          * @method stopDrag
18081          * @param {Event} e the event
18082          * @private
18083          * @static
18084          */
18085         stopDrag: function(e) {
18086             // Fire the drag end event for the item that was dragged
18087             if (this.dragCurrent) {
18088                 if (this.dragThreshMet) {
18089                     this.dragCurrent.b4EndDrag(e);
18090                     this.dragCurrent.endDrag(e);
18091                 }
18092
18093                 this.dragCurrent.onMouseUp(e);
18094             }
18095
18096             this.dragCurrent = null;
18097             this.dragOvers = {};
18098         },
18099
18100         /**
18101          * Internal function to handle the mousemove event.  Will be invoked
18102          * from the context of the html element.
18103          *
18104          * @TODO figure out what we can do about mouse events lost when the
18105          * user drags objects beyond the window boundary.  Currently we can
18106          * detect this in internet explorer by verifying that the mouse is
18107          * down during the mousemove event.  Firefox doesn't give us the
18108          * button state on the mousemove event.
18109          * @method handleMouseMove
18110          * @param {Event} e the event
18111          * @private
18112          * @static
18113          */
18114         handleMouseMove: function(e) {
18115             if (! this.dragCurrent) {
18116                 return true;
18117             }
18118
18119             // var button = e.which || e.button;
18120
18121             // check for IE mouseup outside of page boundary
18122             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18123                 this.stopEvent(e);
18124                 return this.handleMouseUp(e);
18125             }
18126
18127             if (!this.dragThreshMet) {
18128                 var diffX = Math.abs(this.startX - e.getPageX());
18129                 var diffY = Math.abs(this.startY - e.getPageY());
18130                 if (diffX > this.clickPixelThresh ||
18131                             diffY > this.clickPixelThresh) {
18132                     this.startDrag(this.startX, this.startY);
18133                 }
18134             }
18135
18136             if (this.dragThreshMet) {
18137                 this.dragCurrent.b4Drag(e);
18138                 this.dragCurrent.onDrag(e);
18139                 if(!this.dragCurrent.moveOnly){
18140                     this.fireEvents(e, false);
18141                 }
18142             }
18143
18144             this.stopEvent(e);
18145
18146             return true;
18147         },
18148
18149         /**
18150          * Iterates over all of the DragDrop elements to find ones we are
18151          * hovering over or dropping on
18152          * @method fireEvents
18153          * @param {Event} e the event
18154          * @param {boolean} isDrop is this a drop op or a mouseover op?
18155          * @private
18156          * @static
18157          */
18158         fireEvents: function(e, isDrop) {
18159             var dc = this.dragCurrent;
18160
18161             // If the user did the mouse up outside of the window, we could
18162             // get here even though we have ended the drag.
18163             if (!dc || dc.isLocked()) {
18164                 return;
18165             }
18166
18167             var pt = e.getPoint();
18168
18169             // cache the previous dragOver array
18170             var oldOvers = [];
18171
18172             var outEvts   = [];
18173             var overEvts  = [];
18174             var dropEvts  = [];
18175             var enterEvts = [];
18176
18177             // Check to see if the object(s) we were hovering over is no longer
18178             // being hovered over so we can fire the onDragOut event
18179             for (var i in this.dragOvers) {
18180
18181                 var ddo = this.dragOvers[i];
18182
18183                 if (! this.isTypeOfDD(ddo)) {
18184                     continue;
18185                 }
18186
18187                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18188                     outEvts.push( ddo );
18189                 }
18190
18191                 oldOvers[i] = true;
18192                 delete this.dragOvers[i];
18193             }
18194
18195             for (var sGroup in dc.groups) {
18196
18197                 if ("string" != typeof sGroup) {
18198                     continue;
18199                 }
18200
18201                 for (i in this.ids[sGroup]) {
18202                     var oDD = this.ids[sGroup][i];
18203                     if (! this.isTypeOfDD(oDD)) {
18204                         continue;
18205                     }
18206
18207                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18208                         if (this.isOverTarget(pt, oDD, this.mode)) {
18209                             // look for drop interactions
18210                             if (isDrop) {
18211                                 dropEvts.push( oDD );
18212                             // look for drag enter and drag over interactions
18213                             } else {
18214
18215                                 // initial drag over: dragEnter fires
18216                                 if (!oldOvers[oDD.id]) {
18217                                     enterEvts.push( oDD );
18218                                 // subsequent drag overs: dragOver fires
18219                                 } else {
18220                                     overEvts.push( oDD );
18221                                 }
18222
18223                                 this.dragOvers[oDD.id] = oDD;
18224                             }
18225                         }
18226                     }
18227                 }
18228             }
18229
18230             if (this.mode) {
18231                 if (outEvts.length) {
18232                     dc.b4DragOut(e, outEvts);
18233                     dc.onDragOut(e, outEvts);
18234                 }
18235
18236                 if (enterEvts.length) {
18237                     dc.onDragEnter(e, enterEvts);
18238                 }
18239
18240                 if (overEvts.length) {
18241                     dc.b4DragOver(e, overEvts);
18242                     dc.onDragOver(e, overEvts);
18243                 }
18244
18245                 if (dropEvts.length) {
18246                     dc.b4DragDrop(e, dropEvts);
18247                     dc.onDragDrop(e, dropEvts);
18248                 }
18249
18250             } else {
18251                 // fire dragout events
18252                 var len = 0;
18253                 for (i=0, len=outEvts.length; i<len; ++i) {
18254                     dc.b4DragOut(e, outEvts[i].id);
18255                     dc.onDragOut(e, outEvts[i].id);
18256                 }
18257
18258                 // fire enter events
18259                 for (i=0,len=enterEvts.length; i<len; ++i) {
18260                     // dc.b4DragEnter(e, oDD.id);
18261                     dc.onDragEnter(e, enterEvts[i].id);
18262                 }
18263
18264                 // fire over events
18265                 for (i=0,len=overEvts.length; i<len; ++i) {
18266                     dc.b4DragOver(e, overEvts[i].id);
18267                     dc.onDragOver(e, overEvts[i].id);
18268                 }
18269
18270                 // fire drop events
18271                 for (i=0, len=dropEvts.length; i<len; ++i) {
18272                     dc.b4DragDrop(e, dropEvts[i].id);
18273                     dc.onDragDrop(e, dropEvts[i].id);
18274                 }
18275
18276             }
18277
18278             // notify about a drop that did not find a target
18279             if (isDrop && !dropEvts.length) {
18280                 dc.onInvalidDrop(e);
18281             }
18282
18283         },
18284
18285         /**
18286          * Helper function for getting the best match from the list of drag
18287          * and drop objects returned by the drag and drop events when we are
18288          * in INTERSECT mode.  It returns either the first object that the
18289          * cursor is over, or the object that has the greatest overlap with
18290          * the dragged element.
18291          * @method getBestMatch
18292          * @param  {DragDrop[]} dds The array of drag and drop objects
18293          * targeted
18294          * @return {DragDrop}       The best single match
18295          * @static
18296          */
18297         getBestMatch: function(dds) {
18298             var winner = null;
18299             // Return null if the input is not what we expect
18300             //if (!dds || !dds.length || dds.length == 0) {
18301                // winner = null;
18302             // If there is only one item, it wins
18303             //} else if (dds.length == 1) {
18304
18305             var len = dds.length;
18306
18307             if (len == 1) {
18308                 winner = dds[0];
18309             } else {
18310                 // Loop through the targeted items
18311                 for (var i=0; i<len; ++i) {
18312                     var dd = dds[i];
18313                     // If the cursor is over the object, it wins.  If the
18314                     // cursor is over multiple matches, the first one we come
18315                     // to wins.
18316                     if (dd.cursorIsOver) {
18317                         winner = dd;
18318                         break;
18319                     // Otherwise the object with the most overlap wins
18320                     } else {
18321                         if (!winner ||
18322                             winner.overlap.getArea() < dd.overlap.getArea()) {
18323                             winner = dd;
18324                         }
18325                     }
18326                 }
18327             }
18328
18329             return winner;
18330         },
18331
18332         /**
18333          * Refreshes the cache of the top-left and bottom-right points of the
18334          * drag and drop objects in the specified group(s).  This is in the
18335          * format that is stored in the drag and drop instance, so typical
18336          * usage is:
18337          * <code>
18338          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18339          * </code>
18340          * Alternatively:
18341          * <code>
18342          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18343          * </code>
18344          * @TODO this really should be an indexed array.  Alternatively this
18345          * method could accept both.
18346          * @method refreshCache
18347          * @param {Object} groups an associative array of groups to refresh
18348          * @static
18349          */
18350         refreshCache: function(groups) {
18351             for (var sGroup in groups) {
18352                 if ("string" != typeof sGroup) {
18353                     continue;
18354                 }
18355                 for (var i in this.ids[sGroup]) {
18356                     var oDD = this.ids[sGroup][i];
18357
18358                     if (this.isTypeOfDD(oDD)) {
18359                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18360                         var loc = this.getLocation(oDD);
18361                         if (loc) {
18362                             this.locationCache[oDD.id] = loc;
18363                         } else {
18364                             delete this.locationCache[oDD.id];
18365                             // this will unregister the drag and drop object if
18366                             // the element is not in a usable state
18367                             // oDD.unreg();
18368                         }
18369                     }
18370                 }
18371             }
18372         },
18373
18374         /**
18375          * This checks to make sure an element exists and is in the DOM.  The
18376          * main purpose is to handle cases where innerHTML is used to remove
18377          * drag and drop objects from the DOM.  IE provides an 'unspecified
18378          * error' when trying to access the offsetParent of such an element
18379          * @method verifyEl
18380          * @param {HTMLElement} el the element to check
18381          * @return {boolean} true if the element looks usable
18382          * @static
18383          */
18384         verifyEl: function(el) {
18385             if (el) {
18386                 var parent;
18387                 if(Roo.isIE){
18388                     try{
18389                         parent = el.offsetParent;
18390                     }catch(e){}
18391                 }else{
18392                     parent = el.offsetParent;
18393                 }
18394                 if (parent) {
18395                     return true;
18396                 }
18397             }
18398
18399             return false;
18400         },
18401
18402         /**
18403          * Returns a Region object containing the drag and drop element's position
18404          * and size, including the padding configured for it
18405          * @method getLocation
18406          * @param {DragDrop} oDD the drag and drop object to get the
18407          *                       location for
18408          * @return {Roo.lib.Region} a Region object representing the total area
18409          *                             the element occupies, including any padding
18410          *                             the instance is configured for.
18411          * @static
18412          */
18413         getLocation: function(oDD) {
18414             if (! this.isTypeOfDD(oDD)) {
18415                 return null;
18416             }
18417
18418             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18419
18420             try {
18421                 pos= Roo.lib.Dom.getXY(el);
18422             } catch (e) { }
18423
18424             if (!pos) {
18425                 return null;
18426             }
18427
18428             x1 = pos[0];
18429             x2 = x1 + el.offsetWidth;
18430             y1 = pos[1];
18431             y2 = y1 + el.offsetHeight;
18432
18433             t = y1 - oDD.padding[0];
18434             r = x2 + oDD.padding[1];
18435             b = y2 + oDD.padding[2];
18436             l = x1 - oDD.padding[3];
18437
18438             return new Roo.lib.Region( t, r, b, l );
18439         },
18440
18441         /**
18442          * Checks the cursor location to see if it over the target
18443          * @method isOverTarget
18444          * @param {Roo.lib.Point} pt The point to evaluate
18445          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18446          * @return {boolean} true if the mouse is over the target
18447          * @private
18448          * @static
18449          */
18450         isOverTarget: function(pt, oTarget, intersect) {
18451             // use cache if available
18452             var loc = this.locationCache[oTarget.id];
18453             if (!loc || !this.useCache) {
18454                 loc = this.getLocation(oTarget);
18455                 this.locationCache[oTarget.id] = loc;
18456
18457             }
18458
18459             if (!loc) {
18460                 return false;
18461             }
18462
18463             oTarget.cursorIsOver = loc.contains( pt );
18464
18465             // DragDrop is using this as a sanity check for the initial mousedown
18466             // in this case we are done.  In POINT mode, if the drag obj has no
18467             // contraints, we are also done. Otherwise we need to evaluate the
18468             // location of the target as related to the actual location of the
18469             // dragged element.
18470             var dc = this.dragCurrent;
18471             if (!dc || !dc.getTargetCoord ||
18472                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18473                 return oTarget.cursorIsOver;
18474             }
18475
18476             oTarget.overlap = null;
18477
18478             // Get the current location of the drag element, this is the
18479             // location of the mouse event less the delta that represents
18480             // where the original mousedown happened on the element.  We
18481             // need to consider constraints and ticks as well.
18482             var pos = dc.getTargetCoord(pt.x, pt.y);
18483
18484             var el = dc.getDragEl();
18485             var curRegion = new Roo.lib.Region( pos.y,
18486                                                    pos.x + el.offsetWidth,
18487                                                    pos.y + el.offsetHeight,
18488                                                    pos.x );
18489
18490             var overlap = curRegion.intersect(loc);
18491
18492             if (overlap) {
18493                 oTarget.overlap = overlap;
18494                 return (intersect) ? true : oTarget.cursorIsOver;
18495             } else {
18496                 return false;
18497             }
18498         },
18499
18500         /**
18501          * unload event handler
18502          * @method _onUnload
18503          * @private
18504          * @static
18505          */
18506         _onUnload: function(e, me) {
18507             Roo.dd.DragDropMgr.unregAll();
18508         },
18509
18510         /**
18511          * Cleans up the drag and drop events and objects.
18512          * @method unregAll
18513          * @private
18514          * @static
18515          */
18516         unregAll: function() {
18517
18518             if (this.dragCurrent) {
18519                 this.stopDrag();
18520                 this.dragCurrent = null;
18521             }
18522
18523             this._execOnAll("unreg", []);
18524
18525             for (i in this.elementCache) {
18526                 delete this.elementCache[i];
18527             }
18528
18529             this.elementCache = {};
18530             this.ids = {};
18531         },
18532
18533         /**
18534          * A cache of DOM elements
18535          * @property elementCache
18536          * @private
18537          * @static
18538          */
18539         elementCache: {},
18540
18541         /**
18542          * Get the wrapper for the DOM element specified
18543          * @method getElWrapper
18544          * @param {String} id the id of the element to get
18545          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18546          * @private
18547          * @deprecated This wrapper isn't that useful
18548          * @static
18549          */
18550         getElWrapper: function(id) {
18551             var oWrapper = this.elementCache[id];
18552             if (!oWrapper || !oWrapper.el) {
18553                 oWrapper = this.elementCache[id] =
18554                     new this.ElementWrapper(Roo.getDom(id));
18555             }
18556             return oWrapper;
18557         },
18558
18559         /**
18560          * Returns the actual DOM element
18561          * @method getElement
18562          * @param {String} id the id of the elment to get
18563          * @return {Object} The element
18564          * @deprecated use Roo.getDom instead
18565          * @static
18566          */
18567         getElement: function(id) {
18568             return Roo.getDom(id);
18569         },
18570
18571         /**
18572          * Returns the style property for the DOM element (i.e.,
18573          * document.getElById(id).style)
18574          * @method getCss
18575          * @param {String} id the id of the elment to get
18576          * @return {Object} The style property of the element
18577          * @deprecated use Roo.getDom instead
18578          * @static
18579          */
18580         getCss: function(id) {
18581             var el = Roo.getDom(id);
18582             return (el) ? el.style : null;
18583         },
18584
18585         /**
18586          * Inner class for cached elements
18587          * @class DragDropMgr.ElementWrapper
18588          * @for DragDropMgr
18589          * @private
18590          * @deprecated
18591          */
18592         ElementWrapper: function(el) {
18593                 /**
18594                  * The element
18595                  * @property el
18596                  */
18597                 this.el = el || null;
18598                 /**
18599                  * The element id
18600                  * @property id
18601                  */
18602                 this.id = this.el && el.id;
18603                 /**
18604                  * A reference to the style property
18605                  * @property css
18606                  */
18607                 this.css = this.el && el.style;
18608             },
18609
18610         /**
18611          * Returns the X position of an html element
18612          * @method getPosX
18613          * @param el the element for which to get the position
18614          * @return {int} the X coordinate
18615          * @for DragDropMgr
18616          * @deprecated use Roo.lib.Dom.getX instead
18617          * @static
18618          */
18619         getPosX: function(el) {
18620             return Roo.lib.Dom.getX(el);
18621         },
18622
18623         /**
18624          * Returns the Y position of an html element
18625          * @method getPosY
18626          * @param el the element for which to get the position
18627          * @return {int} the Y coordinate
18628          * @deprecated use Roo.lib.Dom.getY instead
18629          * @static
18630          */
18631         getPosY: function(el) {
18632             return Roo.lib.Dom.getY(el);
18633         },
18634
18635         /**
18636          * Swap two nodes.  In IE, we use the native method, for others we
18637          * emulate the IE behavior
18638          * @method swapNode
18639          * @param n1 the first node to swap
18640          * @param n2 the other node to swap
18641          * @static
18642          */
18643         swapNode: function(n1, n2) {
18644             if (n1.swapNode) {
18645                 n1.swapNode(n2);
18646             } else {
18647                 var p = n2.parentNode;
18648                 var s = n2.nextSibling;
18649
18650                 if (s == n1) {
18651                     p.insertBefore(n1, n2);
18652                 } else if (n2 == n1.nextSibling) {
18653                     p.insertBefore(n2, n1);
18654                 } else {
18655                     n1.parentNode.replaceChild(n2, n1);
18656                     p.insertBefore(n1, s);
18657                 }
18658             }
18659         },
18660
18661         /**
18662          * Returns the current scroll position
18663          * @method getScroll
18664          * @private
18665          * @static
18666          */
18667         getScroll: function () {
18668             var t, l, dde=document.documentElement, db=document.body;
18669             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18670                 t = dde.scrollTop;
18671                 l = dde.scrollLeft;
18672             } else if (db) {
18673                 t = db.scrollTop;
18674                 l = db.scrollLeft;
18675             } else {
18676
18677             }
18678             return { top: t, left: l };
18679         },
18680
18681         /**
18682          * Returns the specified element style property
18683          * @method getStyle
18684          * @param {HTMLElement} el          the element
18685          * @param {string}      styleProp   the style property
18686          * @return {string} The value of the style property
18687          * @deprecated use Roo.lib.Dom.getStyle
18688          * @static
18689          */
18690         getStyle: function(el, styleProp) {
18691             return Roo.fly(el).getStyle(styleProp);
18692         },
18693
18694         /**
18695          * Gets the scrollTop
18696          * @method getScrollTop
18697          * @return {int} the document's scrollTop
18698          * @static
18699          */
18700         getScrollTop: function () { return this.getScroll().top; },
18701
18702         /**
18703          * Gets the scrollLeft
18704          * @method getScrollLeft
18705          * @return {int} the document's scrollTop
18706          * @static
18707          */
18708         getScrollLeft: function () { return this.getScroll().left; },
18709
18710         /**
18711          * Sets the x/y position of an element to the location of the
18712          * target element.
18713          * @method moveToEl
18714          * @param {HTMLElement} moveEl      The element to move
18715          * @param {HTMLElement} targetEl    The position reference element
18716          * @static
18717          */
18718         moveToEl: function (moveEl, targetEl) {
18719             var aCoord = Roo.lib.Dom.getXY(targetEl);
18720             Roo.lib.Dom.setXY(moveEl, aCoord);
18721         },
18722
18723         /**
18724          * Numeric array sort function
18725          * @method numericSort
18726          * @static
18727          */
18728         numericSort: function(a, b) { return (a - b); },
18729
18730         /**
18731          * Internal counter
18732          * @property _timeoutCount
18733          * @private
18734          * @static
18735          */
18736         _timeoutCount: 0,
18737
18738         /**
18739          * Trying to make the load order less important.  Without this we get
18740          * an error if this file is loaded before the Event Utility.
18741          * @method _addListeners
18742          * @private
18743          * @static
18744          */
18745         _addListeners: function() {
18746             var DDM = Roo.dd.DDM;
18747             if ( Roo.lib.Event && document ) {
18748                 DDM._onLoad();
18749             } else {
18750                 if (DDM._timeoutCount > 2000) {
18751                 } else {
18752                     setTimeout(DDM._addListeners, 10);
18753                     if (document && document.body) {
18754                         DDM._timeoutCount += 1;
18755                     }
18756                 }
18757             }
18758         },
18759
18760         /**
18761          * Recursively searches the immediate parent and all child nodes for
18762          * the handle element in order to determine wheter or not it was
18763          * clicked.
18764          * @method handleWasClicked
18765          * @param node the html element to inspect
18766          * @static
18767          */
18768         handleWasClicked: function(node, id) {
18769             if (this.isHandle(id, node.id)) {
18770                 return true;
18771             } else {
18772                 // check to see if this is a text node child of the one we want
18773                 var p = node.parentNode;
18774
18775                 while (p) {
18776                     if (this.isHandle(id, p.id)) {
18777                         return true;
18778                     } else {
18779                         p = p.parentNode;
18780                     }
18781                 }
18782             }
18783
18784             return false;
18785         }
18786
18787     };
18788
18789 }();
18790
18791 // shorter alias, save a few bytes
18792 Roo.dd.DDM = Roo.dd.DragDropMgr;
18793 Roo.dd.DDM._addListeners();
18794
18795 }/*
18796  * Based on:
18797  * Ext JS Library 1.1.1
18798  * Copyright(c) 2006-2007, Ext JS, LLC.
18799  *
18800  * Originally Released Under LGPL - original licence link has changed is not relivant.
18801  *
18802  * Fork - LGPL
18803  * <script type="text/javascript">
18804  */
18805
18806 /**
18807  * @class Roo.dd.DD
18808  * A DragDrop implementation where the linked element follows the
18809  * mouse cursor during a drag.
18810  * @extends Roo.dd.DragDrop
18811  * @constructor
18812  * @param {String} id the id of the linked element
18813  * @param {String} sGroup the group of related DragDrop items
18814  * @param {object} config an object containing configurable attributes
18815  *                Valid properties for DD:
18816  *                    scroll
18817  */
18818 Roo.dd.DD = function(id, sGroup, config) {
18819     if (id) {
18820         this.init(id, sGroup, config);
18821     }
18822 };
18823
18824 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18825
18826     /**
18827      * When set to true, the utility automatically tries to scroll the browser
18828      * window wehn a drag and drop element is dragged near the viewport boundary.
18829      * Defaults to true.
18830      * @property scroll
18831      * @type boolean
18832      */
18833     scroll: true,
18834
18835     /**
18836      * Sets the pointer offset to the distance between the linked element's top
18837      * left corner and the location the element was clicked
18838      * @method autoOffset
18839      * @param {int} iPageX the X coordinate of the click
18840      * @param {int} iPageY the Y coordinate of the click
18841      */
18842     autoOffset: function(iPageX, iPageY) {
18843         var x = iPageX - this.startPageX;
18844         var y = iPageY - this.startPageY;
18845         this.setDelta(x, y);
18846     },
18847
18848     /**
18849      * Sets the pointer offset.  You can call this directly to force the
18850      * offset to be in a particular location (e.g., pass in 0,0 to set it
18851      * to the center of the object)
18852      * @method setDelta
18853      * @param {int} iDeltaX the distance from the left
18854      * @param {int} iDeltaY the distance from the top
18855      */
18856     setDelta: function(iDeltaX, iDeltaY) {
18857         this.deltaX = iDeltaX;
18858         this.deltaY = iDeltaY;
18859     },
18860
18861     /**
18862      * Sets the drag element to the location of the mousedown or click event,
18863      * maintaining the cursor location relative to the location on the element
18864      * that was clicked.  Override this if you want to place the element in a
18865      * location other than where the cursor is.
18866      * @method setDragElPos
18867      * @param {int} iPageX the X coordinate of the mousedown or drag event
18868      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18869      */
18870     setDragElPos: function(iPageX, iPageY) {
18871         // the first time we do this, we are going to check to make sure
18872         // the element has css positioning
18873
18874         var el = this.getDragEl();
18875         this.alignElWithMouse(el, iPageX, iPageY);
18876     },
18877
18878     /**
18879      * Sets the element to the location of the mousedown or click event,
18880      * maintaining the cursor location relative to the location on the element
18881      * that was clicked.  Override this if you want to place the element in a
18882      * location other than where the cursor is.
18883      * @method alignElWithMouse
18884      * @param {HTMLElement} el the element to move
18885      * @param {int} iPageX the X coordinate of the mousedown or drag event
18886      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18887      */
18888     alignElWithMouse: function(el, iPageX, iPageY) {
18889         var oCoord = this.getTargetCoord(iPageX, iPageY);
18890         var fly = el.dom ? el : Roo.fly(el);
18891         if (!this.deltaSetXY) {
18892             var aCoord = [oCoord.x, oCoord.y];
18893             fly.setXY(aCoord);
18894             var newLeft = fly.getLeft(true);
18895             var newTop  = fly.getTop(true);
18896             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18897         } else {
18898             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18899         }
18900
18901         this.cachePosition(oCoord.x, oCoord.y);
18902         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18903         return oCoord;
18904     },
18905
18906     /**
18907      * Saves the most recent position so that we can reset the constraints and
18908      * tick marks on-demand.  We need to know this so that we can calculate the
18909      * number of pixels the element is offset from its original position.
18910      * @method cachePosition
18911      * @param iPageX the current x position (optional, this just makes it so we
18912      * don't have to look it up again)
18913      * @param iPageY the current y position (optional, this just makes it so we
18914      * don't have to look it up again)
18915      */
18916     cachePosition: function(iPageX, iPageY) {
18917         if (iPageX) {
18918             this.lastPageX = iPageX;
18919             this.lastPageY = iPageY;
18920         } else {
18921             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18922             this.lastPageX = aCoord[0];
18923             this.lastPageY = aCoord[1];
18924         }
18925     },
18926
18927     /**
18928      * Auto-scroll the window if the dragged object has been moved beyond the
18929      * visible window boundary.
18930      * @method autoScroll
18931      * @param {int} x the drag element's x position
18932      * @param {int} y the drag element's y position
18933      * @param {int} h the height of the drag element
18934      * @param {int} w the width of the drag element
18935      * @private
18936      */
18937     autoScroll: function(x, y, h, w) {
18938
18939         if (this.scroll) {
18940             // The client height
18941             var clientH = Roo.lib.Dom.getViewWidth();
18942
18943             // The client width
18944             var clientW = Roo.lib.Dom.getViewHeight();
18945
18946             // The amt scrolled down
18947             var st = this.DDM.getScrollTop();
18948
18949             // The amt scrolled right
18950             var sl = this.DDM.getScrollLeft();
18951
18952             // Location of the bottom of the element
18953             var bot = h + y;
18954
18955             // Location of the right of the element
18956             var right = w + x;
18957
18958             // The distance from the cursor to the bottom of the visible area,
18959             // adjusted so that we don't scroll if the cursor is beyond the
18960             // element drag constraints
18961             var toBot = (clientH + st - y - this.deltaY);
18962
18963             // The distance from the cursor to the right of the visible area
18964             var toRight = (clientW + sl - x - this.deltaX);
18965
18966
18967             // How close to the edge the cursor must be before we scroll
18968             // var thresh = (document.all) ? 100 : 40;
18969             var thresh = 40;
18970
18971             // How many pixels to scroll per autoscroll op.  This helps to reduce
18972             // clunky scrolling. IE is more sensitive about this ... it needs this
18973             // value to be higher.
18974             var scrAmt = (document.all) ? 80 : 30;
18975
18976             // Scroll down if we are near the bottom of the visible page and the
18977             // obj extends below the crease
18978             if ( bot > clientH && toBot < thresh ) {
18979                 window.scrollTo(sl, st + scrAmt);
18980             }
18981
18982             // Scroll up if the window is scrolled down and the top of the object
18983             // goes above the top border
18984             if ( y < st && st > 0 && y - st < thresh ) {
18985                 window.scrollTo(sl, st - scrAmt);
18986             }
18987
18988             // Scroll right if the obj is beyond the right border and the cursor is
18989             // near the border.
18990             if ( right > clientW && toRight < thresh ) {
18991                 window.scrollTo(sl + scrAmt, st);
18992             }
18993
18994             // Scroll left if the window has been scrolled to the right and the obj
18995             // extends past the left border
18996             if ( x < sl && sl > 0 && x - sl < thresh ) {
18997                 window.scrollTo(sl - scrAmt, st);
18998             }
18999         }
19000     },
19001
19002     /**
19003      * Finds the location the element should be placed if we want to move
19004      * it to where the mouse location less the click offset would place us.
19005      * @method getTargetCoord
19006      * @param {int} iPageX the X coordinate of the click
19007      * @param {int} iPageY the Y coordinate of the click
19008      * @return an object that contains the coordinates (Object.x and Object.y)
19009      * @private
19010      */
19011     getTargetCoord: function(iPageX, iPageY) {
19012
19013
19014         var x = iPageX - this.deltaX;
19015         var y = iPageY - this.deltaY;
19016
19017         if (this.constrainX) {
19018             if (x < this.minX) { x = this.minX; }
19019             if (x > this.maxX) { x = this.maxX; }
19020         }
19021
19022         if (this.constrainY) {
19023             if (y < this.minY) { y = this.minY; }
19024             if (y > this.maxY) { y = this.maxY; }
19025         }
19026
19027         x = this.getTick(x, this.xTicks);
19028         y = this.getTick(y, this.yTicks);
19029
19030
19031         return {x:x, y:y};
19032     },
19033
19034     /*
19035      * Sets up config options specific to this class. Overrides
19036      * Roo.dd.DragDrop, but all versions of this method through the
19037      * inheritance chain are called
19038      */
19039     applyConfig: function() {
19040         Roo.dd.DD.superclass.applyConfig.call(this);
19041         this.scroll = (this.config.scroll !== false);
19042     },
19043
19044     /*
19045      * Event that fires prior to the onMouseDown event.  Overrides
19046      * Roo.dd.DragDrop.
19047      */
19048     b4MouseDown: function(e) {
19049         // this.resetConstraints();
19050         this.autoOffset(e.getPageX(),
19051                             e.getPageY());
19052     },
19053
19054     /*
19055      * Event that fires prior to the onDrag event.  Overrides
19056      * Roo.dd.DragDrop.
19057      */
19058     b4Drag: function(e) {
19059         this.setDragElPos(e.getPageX(),
19060                             e.getPageY());
19061     },
19062
19063     toString: function() {
19064         return ("DD " + this.id);
19065     }
19066
19067     //////////////////////////////////////////////////////////////////////////
19068     // Debugging ygDragDrop events that can be overridden
19069     //////////////////////////////////////////////////////////////////////////
19070     /*
19071     startDrag: function(x, y) {
19072     },
19073
19074     onDrag: function(e) {
19075     },
19076
19077     onDragEnter: function(e, id) {
19078     },
19079
19080     onDragOver: function(e, id) {
19081     },
19082
19083     onDragOut: function(e, id) {
19084     },
19085
19086     onDragDrop: function(e, id) {
19087     },
19088
19089     endDrag: function(e) {
19090     }
19091
19092     */
19093
19094 });/*
19095  * Based on:
19096  * Ext JS Library 1.1.1
19097  * Copyright(c) 2006-2007, Ext JS, LLC.
19098  *
19099  * Originally Released Under LGPL - original licence link has changed is not relivant.
19100  *
19101  * Fork - LGPL
19102  * <script type="text/javascript">
19103  */
19104
19105 /**
19106  * @class Roo.dd.DDProxy
19107  * A DragDrop implementation that inserts an empty, bordered div into
19108  * the document that follows the cursor during drag operations.  At the time of
19109  * the click, the frame div is resized to the dimensions of the linked html
19110  * element, and moved to the exact location of the linked element.
19111  *
19112  * References to the "frame" element refer to the single proxy element that
19113  * was created to be dragged in place of all DDProxy elements on the
19114  * page.
19115  *
19116  * @extends Roo.dd.DD
19117  * @constructor
19118  * @param {String} id the id of the linked html element
19119  * @param {String} sGroup the group of related DragDrop objects
19120  * @param {object} config an object containing configurable attributes
19121  *                Valid properties for DDProxy in addition to those in DragDrop:
19122  *                   resizeFrame, centerFrame, dragElId
19123  */
19124 Roo.dd.DDProxy = function(id, sGroup, config) {
19125     if (id) {
19126         this.init(id, sGroup, config);
19127         this.initFrame();
19128     }
19129 };
19130
19131 /**
19132  * The default drag frame div id
19133  * @property Roo.dd.DDProxy.dragElId
19134  * @type String
19135  * @static
19136  */
19137 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19138
19139 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19140
19141     /**
19142      * By default we resize the drag frame to be the same size as the element
19143      * we want to drag (this is to get the frame effect).  We can turn it off
19144      * if we want a different behavior.
19145      * @property resizeFrame
19146      * @type boolean
19147      */
19148     resizeFrame: true,
19149
19150     /**
19151      * By default the frame is positioned exactly where the drag element is, so
19152      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19153      * you do not have constraints on the obj is to have the drag frame centered
19154      * around the cursor.  Set centerFrame to true for this effect.
19155      * @property centerFrame
19156      * @type boolean
19157      */
19158     centerFrame: false,
19159
19160     /**
19161      * Creates the proxy element if it does not yet exist
19162      * @method createFrame
19163      */
19164     createFrame: function() {
19165         var self = this;
19166         var body = document.body;
19167
19168         if (!body || !body.firstChild) {
19169             setTimeout( function() { self.createFrame(); }, 50 );
19170             return;
19171         }
19172
19173         var div = this.getDragEl();
19174
19175         if (!div) {
19176             div    = document.createElement("div");
19177             div.id = this.dragElId;
19178             var s  = div.style;
19179
19180             s.position   = "absolute";
19181             s.visibility = "hidden";
19182             s.cursor     = "move";
19183             s.border     = "2px solid #aaa";
19184             s.zIndex     = 999;
19185
19186             // appendChild can blow up IE if invoked prior to the window load event
19187             // while rendering a table.  It is possible there are other scenarios
19188             // that would cause this to happen as well.
19189             body.insertBefore(div, body.firstChild);
19190         }
19191     },
19192
19193     /**
19194      * Initialization for the drag frame element.  Must be called in the
19195      * constructor of all subclasses
19196      * @method initFrame
19197      */
19198     initFrame: function() {
19199         this.createFrame();
19200     },
19201
19202     applyConfig: function() {
19203         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19204
19205         this.resizeFrame = (this.config.resizeFrame !== false);
19206         this.centerFrame = (this.config.centerFrame);
19207         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19208     },
19209
19210     /**
19211      * Resizes the drag frame to the dimensions of the clicked object, positions
19212      * it over the object, and finally displays it
19213      * @method showFrame
19214      * @param {int} iPageX X click position
19215      * @param {int} iPageY Y click position
19216      * @private
19217      */
19218     showFrame: function(iPageX, iPageY) {
19219         var el = this.getEl();
19220         var dragEl = this.getDragEl();
19221         var s = dragEl.style;
19222
19223         this._resizeProxy();
19224
19225         if (this.centerFrame) {
19226             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19227                            Math.round(parseInt(s.height, 10)/2) );
19228         }
19229
19230         this.setDragElPos(iPageX, iPageY);
19231
19232         Roo.fly(dragEl).show();
19233     },
19234
19235     /**
19236      * The proxy is automatically resized to the dimensions of the linked
19237      * element when a drag is initiated, unless resizeFrame is set to false
19238      * @method _resizeProxy
19239      * @private
19240      */
19241     _resizeProxy: function() {
19242         if (this.resizeFrame) {
19243             var el = this.getEl();
19244             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19245         }
19246     },
19247
19248     // overrides Roo.dd.DragDrop
19249     b4MouseDown: function(e) {
19250         var x = e.getPageX();
19251         var y = e.getPageY();
19252         this.autoOffset(x, y);
19253         this.setDragElPos(x, y);
19254     },
19255
19256     // overrides Roo.dd.DragDrop
19257     b4StartDrag: function(x, y) {
19258         // show the drag frame
19259         this.showFrame(x, y);
19260     },
19261
19262     // overrides Roo.dd.DragDrop
19263     b4EndDrag: function(e) {
19264         Roo.fly(this.getDragEl()).hide();
19265     },
19266
19267     // overrides Roo.dd.DragDrop
19268     // By default we try to move the element to the last location of the frame.
19269     // This is so that the default behavior mirrors that of Roo.dd.DD.
19270     endDrag: function(e) {
19271
19272         var lel = this.getEl();
19273         var del = this.getDragEl();
19274
19275         // Show the drag frame briefly so we can get its position
19276         del.style.visibility = "";
19277
19278         this.beforeMove();
19279         // Hide the linked element before the move to get around a Safari
19280         // rendering bug.
19281         lel.style.visibility = "hidden";
19282         Roo.dd.DDM.moveToEl(lel, del);
19283         del.style.visibility = "hidden";
19284         lel.style.visibility = "";
19285
19286         this.afterDrag();
19287     },
19288
19289     beforeMove : function(){
19290
19291     },
19292
19293     afterDrag : function(){
19294
19295     },
19296
19297     toString: function() {
19298         return ("DDProxy " + this.id);
19299     }
19300
19301 });
19302 /*
19303  * Based on:
19304  * Ext JS Library 1.1.1
19305  * Copyright(c) 2006-2007, Ext JS, LLC.
19306  *
19307  * Originally Released Under LGPL - original licence link has changed is not relivant.
19308  *
19309  * Fork - LGPL
19310  * <script type="text/javascript">
19311  */
19312
19313  /**
19314  * @class Roo.dd.DDTarget
19315  * A DragDrop implementation that does not move, but can be a drop
19316  * target.  You would get the same result by simply omitting implementation
19317  * for the event callbacks, but this way we reduce the processing cost of the
19318  * event listener and the callbacks.
19319  * @extends Roo.dd.DragDrop
19320  * @constructor
19321  * @param {String} id the id of the element that is a drop target
19322  * @param {String} sGroup the group of related DragDrop objects
19323  * @param {object} config an object containing configurable attributes
19324  *                 Valid properties for DDTarget in addition to those in
19325  *                 DragDrop:
19326  *                    none
19327  */
19328 Roo.dd.DDTarget = function(id, sGroup, config) {
19329     if (id) {
19330         this.initTarget(id, sGroup, config);
19331     }
19332     if (config.listeners || config.events) { 
19333        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19334             listeners : config.listeners || {}, 
19335             events : config.events || {} 
19336         });    
19337     }
19338 };
19339
19340 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19341 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19342     toString: function() {
19343         return ("DDTarget " + this.id);
19344     }
19345 });
19346 /*
19347  * Based on:
19348  * Ext JS Library 1.1.1
19349  * Copyright(c) 2006-2007, Ext JS, LLC.
19350  *
19351  * Originally Released Under LGPL - original licence link has changed is not relivant.
19352  *
19353  * Fork - LGPL
19354  * <script type="text/javascript">
19355  */
19356  
19357
19358 /**
19359  * @class Roo.dd.ScrollManager
19360  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19361  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19362  * @singleton
19363  */
19364 Roo.dd.ScrollManager = function(){
19365     var ddm = Roo.dd.DragDropMgr;
19366     var els = {};
19367     var dragEl = null;
19368     var proc = {};
19369     
19370     
19371     
19372     var onStop = function(e){
19373         dragEl = null;
19374         clearProc();
19375     };
19376     
19377     var triggerRefresh = function(){
19378         if(ddm.dragCurrent){
19379              ddm.refreshCache(ddm.dragCurrent.groups);
19380         }
19381     };
19382     
19383     var doScroll = function(){
19384         if(ddm.dragCurrent){
19385             var dds = Roo.dd.ScrollManager;
19386             if(!dds.animate){
19387                 if(proc.el.scroll(proc.dir, dds.increment)){
19388                     triggerRefresh();
19389                 }
19390             }else{
19391                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19392             }
19393         }
19394     };
19395     
19396     var clearProc = function(){
19397         if(proc.id){
19398             clearInterval(proc.id);
19399         }
19400         proc.id = 0;
19401         proc.el = null;
19402         proc.dir = "";
19403     };
19404     
19405     var startProc = function(el, dir){
19406          Roo.log('scroll startproc');
19407         clearProc();
19408         proc.el = el;
19409         proc.dir = dir;
19410         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19411     };
19412     
19413     var onFire = function(e, isDrop){
19414        
19415         if(isDrop || !ddm.dragCurrent){ return; }
19416         var dds = Roo.dd.ScrollManager;
19417         if(!dragEl || dragEl != ddm.dragCurrent){
19418             dragEl = ddm.dragCurrent;
19419             // refresh regions on drag start
19420             dds.refreshCache();
19421         }
19422         
19423         var xy = Roo.lib.Event.getXY(e);
19424         var pt = new Roo.lib.Point(xy[0], xy[1]);
19425         for(var id in els){
19426             var el = els[id], r = el._region;
19427             if(r && r.contains(pt) && el.isScrollable()){
19428                 if(r.bottom - pt.y <= dds.thresh){
19429                     if(proc.el != el){
19430                         startProc(el, "down");
19431                     }
19432                     return;
19433                 }else if(r.right - pt.x <= dds.thresh){
19434                     if(proc.el != el){
19435                         startProc(el, "left");
19436                     }
19437                     return;
19438                 }else if(pt.y - r.top <= dds.thresh){
19439                     if(proc.el != el){
19440                         startProc(el, "up");
19441                     }
19442                     return;
19443                 }else if(pt.x - r.left <= dds.thresh){
19444                     if(proc.el != el){
19445                         startProc(el, "right");
19446                     }
19447                     return;
19448                 }
19449             }
19450         }
19451         clearProc();
19452     };
19453     
19454     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19455     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19456     
19457     return {
19458         /**
19459          * Registers new overflow element(s) to auto scroll
19460          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19461          */
19462         register : function(el){
19463             if(el instanceof Array){
19464                 for(var i = 0, len = el.length; i < len; i++) {
19465                         this.register(el[i]);
19466                 }
19467             }else{
19468                 el = Roo.get(el);
19469                 els[el.id] = el;
19470             }
19471             Roo.dd.ScrollManager.els = els;
19472         },
19473         
19474         /**
19475          * Unregisters overflow element(s) so they are no longer scrolled
19476          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19477          */
19478         unregister : function(el){
19479             if(el instanceof Array){
19480                 for(var i = 0, len = el.length; i < len; i++) {
19481                         this.unregister(el[i]);
19482                 }
19483             }else{
19484                 el = Roo.get(el);
19485                 delete els[el.id];
19486             }
19487         },
19488         
19489         /**
19490          * The number of pixels from the edge of a container the pointer needs to be to 
19491          * trigger scrolling (defaults to 25)
19492          * @type Number
19493          */
19494         thresh : 25,
19495         
19496         /**
19497          * The number of pixels to scroll in each scroll increment (defaults to 50)
19498          * @type Number
19499          */
19500         increment : 100,
19501         
19502         /**
19503          * The frequency of scrolls in milliseconds (defaults to 500)
19504          * @type Number
19505          */
19506         frequency : 500,
19507         
19508         /**
19509          * True to animate the scroll (defaults to true)
19510          * @type Boolean
19511          */
19512         animate: true,
19513         
19514         /**
19515          * The animation duration in seconds - 
19516          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19517          * @type Number
19518          */
19519         animDuration: .4,
19520         
19521         /**
19522          * Manually trigger a cache refresh.
19523          */
19524         refreshCache : function(){
19525             for(var id in els){
19526                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19527                     els[id]._region = els[id].getRegion();
19528                 }
19529             }
19530         }
19531     };
19532 }();/*
19533  * Based on:
19534  * Ext JS Library 1.1.1
19535  * Copyright(c) 2006-2007, Ext JS, LLC.
19536  *
19537  * Originally Released Under LGPL - original licence link has changed is not relivant.
19538  *
19539  * Fork - LGPL
19540  * <script type="text/javascript">
19541  */
19542  
19543
19544 /**
19545  * @class Roo.dd.Registry
19546  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19547  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19548  * @singleton
19549  */
19550 Roo.dd.Registry = function(){
19551     var elements = {}; 
19552     var handles = {}; 
19553     var autoIdSeed = 0;
19554
19555     var getId = function(el, autogen){
19556         if(typeof el == "string"){
19557             return el;
19558         }
19559         var id = el.id;
19560         if(!id && autogen !== false){
19561             id = "roodd-" + (++autoIdSeed);
19562             el.id = id;
19563         }
19564         return id;
19565     };
19566     
19567     return {
19568     /**
19569      * Register a drag drop element
19570      * @param {String|HTMLElement} element The id or DOM node to register
19571      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19572      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19573      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19574      * populated in the data object (if applicable):
19575      * <pre>
19576 Value      Description<br />
19577 ---------  ------------------------------------------<br />
19578 handles    Array of DOM nodes that trigger dragging<br />
19579            for the element being registered<br />
19580 isHandle   True if the element passed in triggers<br />
19581            dragging itself, else false
19582 </pre>
19583      */
19584         register : function(el, data){
19585             data = data || {};
19586             if(typeof el == "string"){
19587                 el = document.getElementById(el);
19588             }
19589             data.ddel = el;
19590             elements[getId(el)] = data;
19591             if(data.isHandle !== false){
19592                 handles[data.ddel.id] = data;
19593             }
19594             if(data.handles){
19595                 var hs = data.handles;
19596                 for(var i = 0, len = hs.length; i < len; i++){
19597                         handles[getId(hs[i])] = data;
19598                 }
19599             }
19600         },
19601
19602     /**
19603      * Unregister a drag drop element
19604      * @param {String|HTMLElement}  element The id or DOM node to unregister
19605      */
19606         unregister : function(el){
19607             var id = getId(el, false);
19608             var data = elements[id];
19609             if(data){
19610                 delete elements[id];
19611                 if(data.handles){
19612                     var hs = data.handles;
19613                     for(var i = 0, len = hs.length; i < len; i++){
19614                         delete handles[getId(hs[i], false)];
19615                     }
19616                 }
19617             }
19618         },
19619
19620     /**
19621      * Returns the handle registered for a DOM Node by id
19622      * @param {String|HTMLElement} id The DOM node or id to look up
19623      * @return {Object} handle The custom handle data
19624      */
19625         getHandle : function(id){
19626             if(typeof id != "string"){ // must be element?
19627                 id = id.id;
19628             }
19629             return handles[id];
19630         },
19631
19632     /**
19633      * Returns the handle that is registered for the DOM node that is the target of the event
19634      * @param {Event} e The event
19635      * @return {Object} handle The custom handle data
19636      */
19637         getHandleFromEvent : function(e){
19638             var t = Roo.lib.Event.getTarget(e);
19639             return t ? handles[t.id] : null;
19640         },
19641
19642     /**
19643      * Returns a custom data object that is registered for a DOM node by id
19644      * @param {String|HTMLElement} id The DOM node or id to look up
19645      * @return {Object} data The custom data
19646      */
19647         getTarget : function(id){
19648             if(typeof id != "string"){ // must be element?
19649                 id = id.id;
19650             }
19651             return elements[id];
19652         },
19653
19654     /**
19655      * Returns a custom data object that is registered for the DOM node that is the target of the event
19656      * @param {Event} e The event
19657      * @return {Object} data The custom data
19658      */
19659         getTargetFromEvent : function(e){
19660             var t = Roo.lib.Event.getTarget(e);
19661             return t ? elements[t.id] || handles[t.id] : null;
19662         }
19663     };
19664 }();/*
19665  * Based on:
19666  * Ext JS Library 1.1.1
19667  * Copyright(c) 2006-2007, Ext JS, LLC.
19668  *
19669  * Originally Released Under LGPL - original licence link has changed is not relivant.
19670  *
19671  * Fork - LGPL
19672  * <script type="text/javascript">
19673  */
19674  
19675
19676 /**
19677  * @class Roo.dd.StatusProxy
19678  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19679  * default drag proxy used by all Roo.dd components.
19680  * @constructor
19681  * @param {Object} config
19682  */
19683 Roo.dd.StatusProxy = function(config){
19684     Roo.apply(this, config);
19685     this.id = this.id || Roo.id();
19686     this.el = new Roo.Layer({
19687         dh: {
19688             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19689                 {tag: "div", cls: "x-dd-drop-icon"},
19690                 {tag: "div", cls: "x-dd-drag-ghost"}
19691             ]
19692         }, 
19693         shadow: !config || config.shadow !== false
19694     });
19695     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19696     this.dropStatus = this.dropNotAllowed;
19697 };
19698
19699 Roo.dd.StatusProxy.prototype = {
19700     /**
19701      * @cfg {String} dropAllowed
19702      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19703      */
19704     dropAllowed : "x-dd-drop-ok",
19705     /**
19706      * @cfg {String} dropNotAllowed
19707      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19708      */
19709     dropNotAllowed : "x-dd-drop-nodrop",
19710
19711     /**
19712      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19713      * over the current target element.
19714      * @param {String} cssClass The css class for the new drop status indicator image
19715      */
19716     setStatus : function(cssClass){
19717         cssClass = cssClass || this.dropNotAllowed;
19718         if(this.dropStatus != cssClass){
19719             this.el.replaceClass(this.dropStatus, cssClass);
19720             this.dropStatus = cssClass;
19721         }
19722     },
19723
19724     /**
19725      * Resets the status indicator to the default dropNotAllowed value
19726      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19727      */
19728     reset : function(clearGhost){
19729         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19730         this.dropStatus = this.dropNotAllowed;
19731         if(clearGhost){
19732             this.ghost.update("");
19733         }
19734     },
19735
19736     /**
19737      * Updates the contents of the ghost element
19738      * @param {String} html The html that will replace the current innerHTML of the ghost element
19739      */
19740     update : function(html){
19741         if(typeof html == "string"){
19742             this.ghost.update(html);
19743         }else{
19744             this.ghost.update("");
19745             html.style.margin = "0";
19746             this.ghost.dom.appendChild(html);
19747         }
19748         // ensure float = none set?? cant remember why though.
19749         var el = this.ghost.dom.firstChild;
19750                 if(el){
19751                         Roo.fly(el).setStyle('float', 'none');
19752                 }
19753     },
19754     
19755     /**
19756      * Returns the underlying proxy {@link Roo.Layer}
19757      * @return {Roo.Layer} el
19758     */
19759     getEl : function(){
19760         return this.el;
19761     },
19762
19763     /**
19764      * Returns the ghost element
19765      * @return {Roo.Element} el
19766      */
19767     getGhost : function(){
19768         return this.ghost;
19769     },
19770
19771     /**
19772      * Hides the proxy
19773      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19774      */
19775     hide : function(clear){
19776         this.el.hide();
19777         if(clear){
19778             this.reset(true);
19779         }
19780     },
19781
19782     /**
19783      * Stops the repair animation if it's currently running
19784      */
19785     stop : function(){
19786         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19787             this.anim.stop();
19788         }
19789     },
19790
19791     /**
19792      * Displays this proxy
19793      */
19794     show : function(){
19795         this.el.show();
19796     },
19797
19798     /**
19799      * Force the Layer to sync its shadow and shim positions to the element
19800      */
19801     sync : function(){
19802         this.el.sync();
19803     },
19804
19805     /**
19806      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19807      * invalid drop operation by the item being dragged.
19808      * @param {Array} xy The XY position of the element ([x, y])
19809      * @param {Function} callback The function to call after the repair is complete
19810      * @param {Object} scope The scope in which to execute the callback
19811      */
19812     repair : function(xy, callback, scope){
19813         this.callback = callback;
19814         this.scope = scope;
19815         if(xy && this.animRepair !== false){
19816             this.el.addClass("x-dd-drag-repair");
19817             this.el.hideUnders(true);
19818             this.anim = this.el.shift({
19819                 duration: this.repairDuration || .5,
19820                 easing: 'easeOut',
19821                 xy: xy,
19822                 stopFx: true,
19823                 callback: this.afterRepair,
19824                 scope: this
19825             });
19826         }else{
19827             this.afterRepair();
19828         }
19829     },
19830
19831     // private
19832     afterRepair : function(){
19833         this.hide(true);
19834         if(typeof this.callback == "function"){
19835             this.callback.call(this.scope || this);
19836         }
19837         this.callback = null;
19838         this.scope = null;
19839     }
19840 };/*
19841  * Based on:
19842  * Ext JS Library 1.1.1
19843  * Copyright(c) 2006-2007, Ext JS, LLC.
19844  *
19845  * Originally Released Under LGPL - original licence link has changed is not relivant.
19846  *
19847  * Fork - LGPL
19848  * <script type="text/javascript">
19849  */
19850
19851 /**
19852  * @class Roo.dd.DragSource
19853  * @extends Roo.dd.DDProxy
19854  * A simple class that provides the basic implementation needed to make any element draggable.
19855  * @constructor
19856  * @param {String/HTMLElement/Element} el The container element
19857  * @param {Object} config
19858  */
19859 Roo.dd.DragSource = function(el, config){
19860     this.el = Roo.get(el);
19861     this.dragData = {};
19862     
19863     Roo.apply(this, config);
19864     
19865     if(!this.proxy){
19866         this.proxy = new Roo.dd.StatusProxy();
19867     }
19868
19869     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19870           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19871     
19872     this.dragging = false;
19873 };
19874
19875 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19876     /**
19877      * @cfg {String} dropAllowed
19878      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19879      */
19880     dropAllowed : "x-dd-drop-ok",
19881     /**
19882      * @cfg {String} dropNotAllowed
19883      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19884      */
19885     dropNotAllowed : "x-dd-drop-nodrop",
19886
19887     /**
19888      * Returns the data object associated with this drag source
19889      * @return {Object} data An object containing arbitrary data
19890      */
19891     getDragData : function(e){
19892         return this.dragData;
19893     },
19894
19895     // private
19896     onDragEnter : function(e, id){
19897         var target = Roo.dd.DragDropMgr.getDDById(id);
19898         this.cachedTarget = target;
19899         if(this.beforeDragEnter(target, e, id) !== false){
19900             if(target.isNotifyTarget){
19901                 var status = target.notifyEnter(this, e, this.dragData);
19902                 this.proxy.setStatus(status);
19903             }else{
19904                 this.proxy.setStatus(this.dropAllowed);
19905             }
19906             
19907             if(this.afterDragEnter){
19908                 /**
19909                  * An empty function by default, but provided so that you can perform a custom action
19910                  * when the dragged item enters the drop target by providing an implementation.
19911                  * @param {Roo.dd.DragDrop} target The drop target
19912                  * @param {Event} e The event object
19913                  * @param {String} id The id of the dragged element
19914                  * @method afterDragEnter
19915                  */
19916                 this.afterDragEnter(target, e, id);
19917             }
19918         }
19919     },
19920
19921     /**
19922      * An empty function by default, but provided so that you can perform a custom action
19923      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19924      * @param {Roo.dd.DragDrop} target The drop target
19925      * @param {Event} e The event object
19926      * @param {String} id The id of the dragged element
19927      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19928      */
19929     beforeDragEnter : function(target, e, id){
19930         return true;
19931     },
19932
19933     // private
19934     alignElWithMouse: function() {
19935         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19936         this.proxy.sync();
19937     },
19938
19939     // private
19940     onDragOver : function(e, id){
19941         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19942         if(this.beforeDragOver(target, e, id) !== false){
19943             if(target.isNotifyTarget){
19944                 var status = target.notifyOver(this, e, this.dragData);
19945                 this.proxy.setStatus(status);
19946             }
19947
19948             if(this.afterDragOver){
19949                 /**
19950                  * An empty function by default, but provided so that you can perform a custom action
19951                  * while the dragged item is over the drop target by providing an implementation.
19952                  * @param {Roo.dd.DragDrop} target The drop target
19953                  * @param {Event} e The event object
19954                  * @param {String} id The id of the dragged element
19955                  * @method afterDragOver
19956                  */
19957                 this.afterDragOver(target, e, id);
19958             }
19959         }
19960     },
19961
19962     /**
19963      * An empty function by default, but provided so that you can perform a custom action
19964      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19965      * @param {Roo.dd.DragDrop} target The drop target
19966      * @param {Event} e The event object
19967      * @param {String} id The id of the dragged element
19968      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19969      */
19970     beforeDragOver : function(target, e, id){
19971         return true;
19972     },
19973
19974     // private
19975     onDragOut : function(e, id){
19976         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19977         if(this.beforeDragOut(target, e, id) !== false){
19978             if(target.isNotifyTarget){
19979                 target.notifyOut(this, e, this.dragData);
19980             }
19981             this.proxy.reset();
19982             if(this.afterDragOut){
19983                 /**
19984                  * An empty function by default, but provided so that you can perform a custom action
19985                  * after the dragged item is dragged out of the target without dropping.
19986                  * @param {Roo.dd.DragDrop} target The drop target
19987                  * @param {Event} e The event object
19988                  * @param {String} id The id of the dragged element
19989                  * @method afterDragOut
19990                  */
19991                 this.afterDragOut(target, e, id);
19992             }
19993         }
19994         this.cachedTarget = null;
19995     },
19996
19997     /**
19998      * An empty function by default, but provided so that you can perform a custom action before the dragged
19999      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20000      * @param {Roo.dd.DragDrop} target The drop target
20001      * @param {Event} e The event object
20002      * @param {String} id The id of the dragged element
20003      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20004      */
20005     beforeDragOut : function(target, e, id){
20006         return true;
20007     },
20008     
20009     // private
20010     onDragDrop : function(e, id){
20011         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20012         if(this.beforeDragDrop(target, e, id) !== false){
20013             if(target.isNotifyTarget){
20014                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20015                     this.onValidDrop(target, e, id);
20016                 }else{
20017                     this.onInvalidDrop(target, e, id);
20018                 }
20019             }else{
20020                 this.onValidDrop(target, e, id);
20021             }
20022             
20023             if(this.afterDragDrop){
20024                 /**
20025                  * An empty function by default, but provided so that you can perform a custom action
20026                  * after a valid drag drop has occurred by providing an implementation.
20027                  * @param {Roo.dd.DragDrop} target The drop target
20028                  * @param {Event} e The event object
20029                  * @param {String} id The id of the dropped element
20030                  * @method afterDragDrop
20031                  */
20032                 this.afterDragDrop(target, e, id);
20033             }
20034         }
20035         delete this.cachedTarget;
20036     },
20037
20038     /**
20039      * An empty function by default, but provided so that you can perform a custom action before the dragged
20040      * item is dropped onto the target and optionally cancel the onDragDrop.
20041      * @param {Roo.dd.DragDrop} target The drop target
20042      * @param {Event} e The event object
20043      * @param {String} id The id of the dragged element
20044      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20045      */
20046     beforeDragDrop : function(target, e, id){
20047         return true;
20048     },
20049
20050     // private
20051     onValidDrop : function(target, e, id){
20052         this.hideProxy();
20053         if(this.afterValidDrop){
20054             /**
20055              * An empty function by default, but provided so that you can perform a custom action
20056              * after a valid drop has occurred by providing an implementation.
20057              * @param {Object} target The target DD 
20058              * @param {Event} e The event object
20059              * @param {String} id The id of the dropped element
20060              * @method afterInvalidDrop
20061              */
20062             this.afterValidDrop(target, e, id);
20063         }
20064     },
20065
20066     // private
20067     getRepairXY : function(e, data){
20068         return this.el.getXY();  
20069     },
20070
20071     // private
20072     onInvalidDrop : function(target, e, id){
20073         this.beforeInvalidDrop(target, e, id);
20074         if(this.cachedTarget){
20075             if(this.cachedTarget.isNotifyTarget){
20076                 this.cachedTarget.notifyOut(this, e, this.dragData);
20077             }
20078             this.cacheTarget = null;
20079         }
20080         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20081
20082         if(this.afterInvalidDrop){
20083             /**
20084              * An empty function by default, but provided so that you can perform a custom action
20085              * after an invalid drop has occurred by providing an implementation.
20086              * @param {Event} e The event object
20087              * @param {String} id The id of the dropped element
20088              * @method afterInvalidDrop
20089              */
20090             this.afterInvalidDrop(e, id);
20091         }
20092     },
20093
20094     // private
20095     afterRepair : function(){
20096         if(Roo.enableFx){
20097             this.el.highlight(this.hlColor || "c3daf9");
20098         }
20099         this.dragging = false;
20100     },
20101
20102     /**
20103      * An empty function by default, but provided so that you can perform a custom action after an invalid
20104      * drop has occurred.
20105      * @param {Roo.dd.DragDrop} target The drop target
20106      * @param {Event} e The event object
20107      * @param {String} id The id of the dragged element
20108      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20109      */
20110     beforeInvalidDrop : function(target, e, id){
20111         return true;
20112     },
20113
20114     // private
20115     handleMouseDown : function(e){
20116         if(this.dragging) {
20117             return;
20118         }
20119         var data = this.getDragData(e);
20120         if(data && this.onBeforeDrag(data, e) !== false){
20121             this.dragData = data;
20122             this.proxy.stop();
20123             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20124         } 
20125     },
20126
20127     /**
20128      * An empty function by default, but provided so that you can perform a custom action before the initial
20129      * drag event begins and optionally cancel it.
20130      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20131      * @param {Event} e The event object
20132      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20133      */
20134     onBeforeDrag : function(data, e){
20135         return true;
20136     },
20137
20138     /**
20139      * An empty function by default, but provided so that you can perform a custom action once the initial
20140      * drag event has begun.  The drag cannot be canceled from this function.
20141      * @param {Number} x The x position of the click on the dragged object
20142      * @param {Number} y The y position of the click on the dragged object
20143      */
20144     onStartDrag : Roo.emptyFn,
20145
20146     // private - YUI override
20147     startDrag : function(x, y){
20148         this.proxy.reset();
20149         this.dragging = true;
20150         this.proxy.update("");
20151         this.onInitDrag(x, y);
20152         this.proxy.show();
20153     },
20154
20155     // private
20156     onInitDrag : function(x, y){
20157         var clone = this.el.dom.cloneNode(true);
20158         clone.id = Roo.id(); // prevent duplicate ids
20159         this.proxy.update(clone);
20160         this.onStartDrag(x, y);
20161         return true;
20162     },
20163
20164     /**
20165      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20166      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20167      */
20168     getProxy : function(){
20169         return this.proxy;  
20170     },
20171
20172     /**
20173      * Hides the drag source's {@link Roo.dd.StatusProxy}
20174      */
20175     hideProxy : function(){
20176         this.proxy.hide();  
20177         this.proxy.reset(true);
20178         this.dragging = false;
20179     },
20180
20181     // private
20182     triggerCacheRefresh : function(){
20183         Roo.dd.DDM.refreshCache(this.groups);
20184     },
20185
20186     // private - override to prevent hiding
20187     b4EndDrag: function(e) {
20188     },
20189
20190     // private - override to prevent moving
20191     endDrag : function(e){
20192         this.onEndDrag(this.dragData, e);
20193     },
20194
20195     // private
20196     onEndDrag : function(data, e){
20197     },
20198     
20199     // private - pin to cursor
20200     autoOffset : function(x, y) {
20201         this.setDelta(-12, -20);
20202     }    
20203 });/*
20204  * Based on:
20205  * Ext JS Library 1.1.1
20206  * Copyright(c) 2006-2007, Ext JS, LLC.
20207  *
20208  * Originally Released Under LGPL - original licence link has changed is not relivant.
20209  *
20210  * Fork - LGPL
20211  * <script type="text/javascript">
20212  */
20213
20214
20215 /**
20216  * @class Roo.dd.DropTarget
20217  * @extends Roo.dd.DDTarget
20218  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20219  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20220  * @constructor
20221  * @param {String/HTMLElement/Element} el The container element
20222  * @param {Object} config
20223  */
20224 Roo.dd.DropTarget = function(el, config){
20225     this.el = Roo.get(el);
20226     
20227     var listeners = false; ;
20228     if (config && config.listeners) {
20229         listeners= config.listeners;
20230         delete config.listeners;
20231     }
20232     Roo.apply(this, config);
20233     
20234     if(this.containerScroll){
20235         Roo.dd.ScrollManager.register(this.el);
20236     }
20237     this.addEvents( {
20238          /**
20239          * @scope Roo.dd.DropTarget
20240          */
20241          
20242          /**
20243          * @event enter
20244          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20245          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20246          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20247          * 
20248          * IMPORTANT : it should set this.overClass and this.dropAllowed
20249          * 
20250          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20251          * @param {Event} e The event
20252          * @param {Object} data An object containing arbitrary data supplied by the drag source
20253          */
20254         "enter" : true,
20255         
20256          /**
20257          * @event over
20258          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20259          * This method will be called on every mouse movement while the drag source is over the drop target.
20260          * This default implementation simply returns the dropAllowed config value.
20261          * 
20262          * IMPORTANT : it should set this.dropAllowed
20263          * 
20264          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20265          * @param {Event} e The event
20266          * @param {Object} data An object containing arbitrary data supplied by the drag source
20267          
20268          */
20269         "over" : true,
20270         /**
20271          * @event out
20272          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20273          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20274          * overClass (if any) from the drop element.
20275          * 
20276          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20277          * @param {Event} e The event
20278          * @param {Object} data An object containing arbitrary data supplied by the drag source
20279          */
20280          "out" : true,
20281          
20282         /**
20283          * @event drop
20284          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20285          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20286          * implementation that does something to process the drop event and returns true so that the drag source's
20287          * repair action does not run.
20288          * 
20289          * IMPORTANT : it should set this.success
20290          * 
20291          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20292          * @param {Event} e The event
20293          * @param {Object} data An object containing arbitrary data supplied by the drag source
20294         */
20295          "drop" : true
20296     });
20297             
20298      
20299     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20300         this.el.dom, 
20301         this.ddGroup || this.group,
20302         {
20303             isTarget: true,
20304             listeners : listeners || {} 
20305            
20306         
20307         }
20308     );
20309
20310 };
20311
20312 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20313     /**
20314      * @cfg {String} overClass
20315      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20316      */
20317      /**
20318      * @cfg {String} ddGroup
20319      * The drag drop group to handle drop events for
20320      */
20321      
20322     /**
20323      * @cfg {String} dropAllowed
20324      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20325      */
20326     dropAllowed : "x-dd-drop-ok",
20327     /**
20328      * @cfg {String} dropNotAllowed
20329      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20330      */
20331     dropNotAllowed : "x-dd-drop-nodrop",
20332     /**
20333      * @cfg {boolean} success
20334      * set this after drop listener.. 
20335      */
20336     success : false,
20337     /**
20338      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20339      * if the drop point is valid for over/enter..
20340      */
20341     valid : false,
20342     // private
20343     isTarget : true,
20344
20345     // private
20346     isNotifyTarget : true,
20347     
20348     /**
20349      * @hide
20350      */
20351     notifyEnter : function(dd, e, data)
20352     {
20353         this.valid = true;
20354         this.fireEvent('enter', dd, e, data);
20355         if(this.overClass){
20356             this.el.addClass(this.overClass);
20357         }
20358         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20359             this.valid ? this.dropAllowed : this.dropNotAllowed
20360         );
20361     },
20362
20363     /**
20364      * @hide
20365      */
20366     notifyOver : function(dd, e, data)
20367     {
20368         this.valid = true;
20369         this.fireEvent('over', dd, e, data);
20370         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20371             this.valid ? this.dropAllowed : this.dropNotAllowed
20372         );
20373     },
20374
20375     /**
20376      * @hide
20377      */
20378     notifyOut : function(dd, e, data)
20379     {
20380         this.fireEvent('out', dd, e, data);
20381         if(this.overClass){
20382             this.el.removeClass(this.overClass);
20383         }
20384     },
20385
20386     /**
20387      * @hide
20388      */
20389     notifyDrop : function(dd, e, data)
20390     {
20391         this.success = false;
20392         this.fireEvent('drop', dd, e, data);
20393         return this.success;
20394     }
20395 });/*
20396  * Based on:
20397  * Ext JS Library 1.1.1
20398  * Copyright(c) 2006-2007, Ext JS, LLC.
20399  *
20400  * Originally Released Under LGPL - original licence link has changed is not relivant.
20401  *
20402  * Fork - LGPL
20403  * <script type="text/javascript">
20404  */
20405
20406
20407 /**
20408  * @class Roo.dd.DragZone
20409  * @extends Roo.dd.DragSource
20410  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20411  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20412  * @constructor
20413  * @param {String/HTMLElement/Element} el The container element
20414  * @param {Object} config
20415  */
20416 Roo.dd.DragZone = function(el, config){
20417     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20418     if(this.containerScroll){
20419         Roo.dd.ScrollManager.register(this.el);
20420     }
20421 };
20422
20423 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20424     /**
20425      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20426      * for auto scrolling during drag operations.
20427      */
20428     /**
20429      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20430      * method after a failed drop (defaults to "c3daf9" - light blue)
20431      */
20432
20433     /**
20434      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20435      * for a valid target to drag based on the mouse down. Override this method
20436      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20437      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20438      * @param {EventObject} e The mouse down event
20439      * @return {Object} The dragData
20440      */
20441     getDragData : function(e){
20442         return Roo.dd.Registry.getHandleFromEvent(e);
20443     },
20444     
20445     /**
20446      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20447      * this.dragData.ddel
20448      * @param {Number} x The x position of the click on the dragged object
20449      * @param {Number} y The y position of the click on the dragged object
20450      * @return {Boolean} true to continue the drag, false to cancel
20451      */
20452     onInitDrag : function(x, y){
20453         this.proxy.update(this.dragData.ddel.cloneNode(true));
20454         this.onStartDrag(x, y);
20455         return true;
20456     },
20457     
20458     /**
20459      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20460      */
20461     afterRepair : function(){
20462         if(Roo.enableFx){
20463             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20464         }
20465         this.dragging = false;
20466     },
20467
20468     /**
20469      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20470      * the XY of this.dragData.ddel
20471      * @param {EventObject} e The mouse up event
20472      * @return {Array} The xy location (e.g. [100, 200])
20473      */
20474     getRepairXY : function(e){
20475         return Roo.Element.fly(this.dragData.ddel).getXY();  
20476     }
20477 });/*
20478  * Based on:
20479  * Ext JS Library 1.1.1
20480  * Copyright(c) 2006-2007, Ext JS, LLC.
20481  *
20482  * Originally Released Under LGPL - original licence link has changed is not relivant.
20483  *
20484  * Fork - LGPL
20485  * <script type="text/javascript">
20486  */
20487 /**
20488  * @class Roo.dd.DropZone
20489  * @extends Roo.dd.DropTarget
20490  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20491  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20492  * @constructor
20493  * @param {String/HTMLElement/Element} el The container element
20494  * @param {Object} config
20495  */
20496 Roo.dd.DropZone = function(el, config){
20497     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20498 };
20499
20500 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20501     /**
20502      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20503      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20504      * provide your own custom lookup.
20505      * @param {Event} e The event
20506      * @return {Object} data The custom data
20507      */
20508     getTargetFromEvent : function(e){
20509         return Roo.dd.Registry.getTargetFromEvent(e);
20510     },
20511
20512     /**
20513      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20514      * that it has registered.  This method has no default implementation and should be overridden to provide
20515      * node-specific processing if necessary.
20516      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20517      * {@link #getTargetFromEvent} for this node)
20518      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20519      * @param {Event} e The event
20520      * @param {Object} data An object containing arbitrary data supplied by the drag source
20521      */
20522     onNodeEnter : function(n, dd, e, data){
20523         
20524     },
20525
20526     /**
20527      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20528      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20529      * overridden to provide the proper feedback.
20530      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20531      * {@link #getTargetFromEvent} for this node)
20532      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20533      * @param {Event} e The event
20534      * @param {Object} data An object containing arbitrary data supplied by the drag source
20535      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20536      * underlying {@link Roo.dd.StatusProxy} can be updated
20537      */
20538     onNodeOver : function(n, dd, e, data){
20539         return this.dropAllowed;
20540     },
20541
20542     /**
20543      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20544      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20545      * node-specific processing if necessary.
20546      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20547      * {@link #getTargetFromEvent} for this node)
20548      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20549      * @param {Event} e The event
20550      * @param {Object} data An object containing arbitrary data supplied by the drag source
20551      */
20552     onNodeOut : function(n, dd, e, data){
20553         
20554     },
20555
20556     /**
20557      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20558      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20559      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20560      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20561      * {@link #getTargetFromEvent} for this node)
20562      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20563      * @param {Event} e The event
20564      * @param {Object} data An object containing arbitrary data supplied by the drag source
20565      * @return {Boolean} True if the drop was valid, else false
20566      */
20567     onNodeDrop : function(n, dd, e, data){
20568         return false;
20569     },
20570
20571     /**
20572      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20573      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20574      * it should be overridden to provide the proper feedback if necessary.
20575      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20576      * @param {Event} e The event
20577      * @param {Object} data An object containing arbitrary data supplied by the drag source
20578      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20579      * underlying {@link Roo.dd.StatusProxy} can be updated
20580      */
20581     onContainerOver : function(dd, e, data){
20582         return this.dropNotAllowed;
20583     },
20584
20585     /**
20586      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20587      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20588      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20589      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20590      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20591      * @param {Event} e The event
20592      * @param {Object} data An object containing arbitrary data supplied by the drag source
20593      * @return {Boolean} True if the drop was valid, else false
20594      */
20595     onContainerDrop : function(dd, e, data){
20596         return false;
20597     },
20598
20599     /**
20600      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20601      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20602      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20603      * you should override this method and provide a custom implementation.
20604      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20605      * @param {Event} e The event
20606      * @param {Object} data An object containing arbitrary data supplied by the drag source
20607      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20608      * underlying {@link Roo.dd.StatusProxy} can be updated
20609      */
20610     notifyEnter : function(dd, e, data){
20611         return this.dropNotAllowed;
20612     },
20613
20614     /**
20615      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20616      * This method will be called on every mouse movement while the drag source is over the drop zone.
20617      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20618      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20619      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20620      * registered node, it will call {@link #onContainerOver}.
20621      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20622      * @param {Event} e The event
20623      * @param {Object} data An object containing arbitrary data supplied by the drag source
20624      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20625      * underlying {@link Roo.dd.StatusProxy} can be updated
20626      */
20627     notifyOver : function(dd, e, data){
20628         var n = this.getTargetFromEvent(e);
20629         if(!n){ // not over valid drop target
20630             if(this.lastOverNode){
20631                 this.onNodeOut(this.lastOverNode, dd, e, data);
20632                 this.lastOverNode = null;
20633             }
20634             return this.onContainerOver(dd, e, data);
20635         }
20636         if(this.lastOverNode != n){
20637             if(this.lastOverNode){
20638                 this.onNodeOut(this.lastOverNode, dd, e, data);
20639             }
20640             this.onNodeEnter(n, dd, e, data);
20641             this.lastOverNode = n;
20642         }
20643         return this.onNodeOver(n, dd, e, data);
20644     },
20645
20646     /**
20647      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20648      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20649      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20650      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20651      * @param {Event} e The event
20652      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20653      */
20654     notifyOut : function(dd, e, data){
20655         if(this.lastOverNode){
20656             this.onNodeOut(this.lastOverNode, dd, e, data);
20657             this.lastOverNode = null;
20658         }
20659     },
20660
20661     /**
20662      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20663      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20664      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20665      * otherwise it will call {@link #onContainerDrop}.
20666      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20667      * @param {Event} e The event
20668      * @param {Object} data An object containing arbitrary data supplied by the drag source
20669      * @return {Boolean} True if the drop was valid, else false
20670      */
20671     notifyDrop : function(dd, e, data){
20672         if(this.lastOverNode){
20673             this.onNodeOut(this.lastOverNode, dd, e, data);
20674             this.lastOverNode = null;
20675         }
20676         var n = this.getTargetFromEvent(e);
20677         return n ?
20678             this.onNodeDrop(n, dd, e, data) :
20679             this.onContainerDrop(dd, e, data);
20680     },
20681
20682     // private
20683     triggerCacheRefresh : function(){
20684         Roo.dd.DDM.refreshCache(this.groups);
20685     }  
20686 });/*
20687  * Based on:
20688  * Ext JS Library 1.1.1
20689  * Copyright(c) 2006-2007, Ext JS, LLC.
20690  *
20691  * Originally Released Under LGPL - original licence link has changed is not relivant.
20692  *
20693  * Fork - LGPL
20694  * <script type="text/javascript">
20695  */
20696
20697
20698 /**
20699  * @class Roo.data.SortTypes
20700  * @singleton
20701  * Defines the default sorting (casting?) comparison functions used when sorting data.
20702  */
20703 Roo.data.SortTypes = {
20704     /**
20705      * Default sort that does nothing
20706      * @param {Mixed} s The value being converted
20707      * @return {Mixed} The comparison value
20708      */
20709     none : function(s){
20710         return s;
20711     },
20712     
20713     /**
20714      * The regular expression used to strip tags
20715      * @type {RegExp}
20716      * @property
20717      */
20718     stripTagsRE : /<\/?[^>]+>/gi,
20719     
20720     /**
20721      * Strips all HTML tags to sort on text only
20722      * @param {Mixed} s The value being converted
20723      * @return {String} The comparison value
20724      */
20725     asText : function(s){
20726         return String(s).replace(this.stripTagsRE, "");
20727     },
20728     
20729     /**
20730      * Strips all HTML tags to sort on text only - Case insensitive
20731      * @param {Mixed} s The value being converted
20732      * @return {String} The comparison value
20733      */
20734     asUCText : function(s){
20735         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20736     },
20737     
20738     /**
20739      * Case insensitive string
20740      * @param {Mixed} s The value being converted
20741      * @return {String} The comparison value
20742      */
20743     asUCString : function(s) {
20744         return String(s).toUpperCase();
20745     },
20746     
20747     /**
20748      * Date sorting
20749      * @param {Mixed} s The value being converted
20750      * @return {Number} The comparison value
20751      */
20752     asDate : function(s) {
20753         if(!s){
20754             return 0;
20755         }
20756         if(s instanceof Date){
20757             return s.getTime();
20758         }
20759         return Date.parse(String(s));
20760     },
20761     
20762     /**
20763      * Float sorting
20764      * @param {Mixed} s The value being converted
20765      * @return {Float} The comparison value
20766      */
20767     asFloat : function(s) {
20768         var val = parseFloat(String(s).replace(/,/g, ""));
20769         if(isNaN(val)) val = 0;
20770         return val;
20771     },
20772     
20773     /**
20774      * Integer sorting
20775      * @param {Mixed} s The value being converted
20776      * @return {Number} The comparison value
20777      */
20778     asInt : function(s) {
20779         var val = parseInt(String(s).replace(/,/g, ""));
20780         if(isNaN(val)) val = 0;
20781         return val;
20782     }
20783 };/*
20784  * Based on:
20785  * Ext JS Library 1.1.1
20786  * Copyright(c) 2006-2007, Ext JS, LLC.
20787  *
20788  * Originally Released Under LGPL - original licence link has changed is not relivant.
20789  *
20790  * Fork - LGPL
20791  * <script type="text/javascript">
20792  */
20793
20794 /**
20795 * @class Roo.data.Record
20796  * Instances of this class encapsulate both record <em>definition</em> information, and record
20797  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20798  * to access Records cached in an {@link Roo.data.Store} object.<br>
20799  * <p>
20800  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20801  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20802  * objects.<br>
20803  * <p>
20804  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20805  * @constructor
20806  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20807  * {@link #create}. The parameters are the same.
20808  * @param {Array} data An associative Array of data values keyed by the field name.
20809  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20810  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20811  * not specified an integer id is generated.
20812  */
20813 Roo.data.Record = function(data, id){
20814     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20815     this.data = data;
20816 };
20817
20818 /**
20819  * Generate a constructor for a specific record layout.
20820  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20821  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20822  * Each field definition object may contain the following properties: <ul>
20823  * <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,
20824  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20825  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20826  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20827  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20828  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20829  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20830  * this may be omitted.</p></li>
20831  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20832  * <ul><li>auto (Default, implies no conversion)</li>
20833  * <li>string</li>
20834  * <li>int</li>
20835  * <li>float</li>
20836  * <li>boolean</li>
20837  * <li>date</li></ul></p></li>
20838  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20839  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20840  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20841  * by the Reader into an object that will be stored in the Record. It is passed the
20842  * following parameters:<ul>
20843  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20844  * </ul></p></li>
20845  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20846  * </ul>
20847  * <br>usage:<br><pre><code>
20848 var TopicRecord = Roo.data.Record.create(
20849     {name: 'title', mapping: 'topic_title'},
20850     {name: 'author', mapping: 'username'},
20851     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20852     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20853     {name: 'lastPoster', mapping: 'user2'},
20854     {name: 'excerpt', mapping: 'post_text'}
20855 );
20856
20857 var myNewRecord = new TopicRecord({
20858     title: 'Do my job please',
20859     author: 'noobie',
20860     totalPosts: 1,
20861     lastPost: new Date(),
20862     lastPoster: 'Animal',
20863     excerpt: 'No way dude!'
20864 });
20865 myStore.add(myNewRecord);
20866 </code></pre>
20867  * @method create
20868  * @static
20869  */
20870 Roo.data.Record.create = function(o){
20871     var f = function(){
20872         f.superclass.constructor.apply(this, arguments);
20873     };
20874     Roo.extend(f, Roo.data.Record);
20875     var p = f.prototype;
20876     p.fields = new Roo.util.MixedCollection(false, function(field){
20877         return field.name;
20878     });
20879     for(var i = 0, len = o.length; i < len; i++){
20880         p.fields.add(new Roo.data.Field(o[i]));
20881     }
20882     f.getField = function(name){
20883         return p.fields.get(name);  
20884     };
20885     return f;
20886 };
20887
20888 Roo.data.Record.AUTO_ID = 1000;
20889 Roo.data.Record.EDIT = 'edit';
20890 Roo.data.Record.REJECT = 'reject';
20891 Roo.data.Record.COMMIT = 'commit';
20892
20893 Roo.data.Record.prototype = {
20894     /**
20895      * Readonly flag - true if this record has been modified.
20896      * @type Boolean
20897      */
20898     dirty : false,
20899     editing : false,
20900     error: null,
20901     modified: null,
20902
20903     // private
20904     join : function(store){
20905         this.store = store;
20906     },
20907
20908     /**
20909      * Set the named field to the specified value.
20910      * @param {String} name The name of the field to set.
20911      * @param {Object} value The value to set the field to.
20912      */
20913     set : function(name, value){
20914         if(this.data[name] == value){
20915             return;
20916         }
20917         this.dirty = true;
20918         if(!this.modified){
20919             this.modified = {};
20920         }
20921         if(typeof this.modified[name] == 'undefined'){
20922             this.modified[name] = this.data[name];
20923         }
20924         this.data[name] = value;
20925         if(!this.editing && this.store){
20926             this.store.afterEdit(this);
20927         }       
20928     },
20929
20930     /**
20931      * Get the value of the named field.
20932      * @param {String} name The name of the field to get the value of.
20933      * @return {Object} The value of the field.
20934      */
20935     get : function(name){
20936         return this.data[name]; 
20937     },
20938
20939     // private
20940     beginEdit : function(){
20941         this.editing = true;
20942         this.modified = {}; 
20943     },
20944
20945     // private
20946     cancelEdit : function(){
20947         this.editing = false;
20948         delete this.modified;
20949     },
20950
20951     // private
20952     endEdit : function(){
20953         this.editing = false;
20954         if(this.dirty && this.store){
20955             this.store.afterEdit(this);
20956         }
20957     },
20958
20959     /**
20960      * Usually called by the {@link Roo.data.Store} which owns the Record.
20961      * Rejects all changes made to the Record since either creation, or the last commit operation.
20962      * Modified fields are reverted to their original values.
20963      * <p>
20964      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20965      * of reject operations.
20966      */
20967     reject : function(){
20968         var m = this.modified;
20969         for(var n in m){
20970             if(typeof m[n] != "function"){
20971                 this.data[n] = m[n];
20972             }
20973         }
20974         this.dirty = false;
20975         delete this.modified;
20976         this.editing = false;
20977         if(this.store){
20978             this.store.afterReject(this);
20979         }
20980     },
20981
20982     /**
20983      * Usually called by the {@link Roo.data.Store} which owns the Record.
20984      * Commits all changes made to the Record since either creation, or the last commit operation.
20985      * <p>
20986      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20987      * of commit operations.
20988      */
20989     commit : function(){
20990         this.dirty = false;
20991         delete this.modified;
20992         this.editing = false;
20993         if(this.store){
20994             this.store.afterCommit(this);
20995         }
20996     },
20997
20998     // private
20999     hasError : function(){
21000         return this.error != null;
21001     },
21002
21003     // private
21004     clearError : function(){
21005         this.error = null;
21006     },
21007
21008     /**
21009      * Creates a copy of this record.
21010      * @param {String} id (optional) A new record id if you don't want to use this record's id
21011      * @return {Record}
21012      */
21013     copy : function(newId) {
21014         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21015     }
21016 };/*
21017  * Based on:
21018  * Ext JS Library 1.1.1
21019  * Copyright(c) 2006-2007, Ext JS, LLC.
21020  *
21021  * Originally Released Under LGPL - original licence link has changed is not relivant.
21022  *
21023  * Fork - LGPL
21024  * <script type="text/javascript">
21025  */
21026
21027
21028
21029 /**
21030  * @class Roo.data.Store
21031  * @extends Roo.util.Observable
21032  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21033  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21034  * <p>
21035  * 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
21036  * has no knowledge of the format of the data returned by the Proxy.<br>
21037  * <p>
21038  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21039  * instances from the data object. These records are cached and made available through accessor functions.
21040  * @constructor
21041  * Creates a new Store.
21042  * @param {Object} config A config object containing the objects needed for the Store to access data,
21043  * and read the data into Records.
21044  */
21045 Roo.data.Store = function(config){
21046     this.data = new Roo.util.MixedCollection(false);
21047     this.data.getKey = function(o){
21048         return o.id;
21049     };
21050     this.baseParams = {};
21051     // private
21052     this.paramNames = {
21053         "start" : "start",
21054         "limit" : "limit",
21055         "sort" : "sort",
21056         "dir" : "dir",
21057         "multisort" : "_multisort"
21058     };
21059
21060     if(config && config.data){
21061         this.inlineData = config.data;
21062         delete config.data;
21063     }
21064
21065     Roo.apply(this, config);
21066     
21067     if(this.reader){ // reader passed
21068         this.reader = Roo.factory(this.reader, Roo.data);
21069         this.reader.xmodule = this.xmodule || false;
21070         if(!this.recordType){
21071             this.recordType = this.reader.recordType;
21072         }
21073         if(this.reader.onMetaChange){
21074             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21075         }
21076     }
21077
21078     if(this.recordType){
21079         this.fields = this.recordType.prototype.fields;
21080     }
21081     this.modified = [];
21082
21083     this.addEvents({
21084         /**
21085          * @event datachanged
21086          * Fires when the data cache has changed, and a widget which is using this Store
21087          * as a Record cache should refresh its view.
21088          * @param {Store} this
21089          */
21090         datachanged : true,
21091         /**
21092          * @event metachange
21093          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21094          * @param {Store} this
21095          * @param {Object} meta The JSON metadata
21096          */
21097         metachange : true,
21098         /**
21099          * @event add
21100          * Fires when Records have been added to the Store
21101          * @param {Store} this
21102          * @param {Roo.data.Record[]} records The array of Records added
21103          * @param {Number} index The index at which the record(s) were added
21104          */
21105         add : true,
21106         /**
21107          * @event remove
21108          * Fires when a Record has been removed from the Store
21109          * @param {Store} this
21110          * @param {Roo.data.Record} record The Record that was removed
21111          * @param {Number} index The index at which the record was removed
21112          */
21113         remove : true,
21114         /**
21115          * @event update
21116          * Fires when a Record has been updated
21117          * @param {Store} this
21118          * @param {Roo.data.Record} record The Record that was updated
21119          * @param {String} operation The update operation being performed.  Value may be one of:
21120          * <pre><code>
21121  Roo.data.Record.EDIT
21122  Roo.data.Record.REJECT
21123  Roo.data.Record.COMMIT
21124          * </code></pre>
21125          */
21126         update : true,
21127         /**
21128          * @event clear
21129          * Fires when the data cache has been cleared.
21130          * @param {Store} this
21131          */
21132         clear : true,
21133         /**
21134          * @event beforeload
21135          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21136          * the load action will be canceled.
21137          * @param {Store} this
21138          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21139          */
21140         beforeload : true,
21141         /**
21142          * @event beforeloadadd
21143          * Fires after a new set of Records has been loaded.
21144          * @param {Store} this
21145          * @param {Roo.data.Record[]} records The Records that were loaded
21146          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21147          */
21148         beforeloadadd : true,
21149         /**
21150          * @event load
21151          * Fires after a new set of Records has been loaded, before they are added to the store.
21152          * @param {Store} this
21153          * @param {Roo.data.Record[]} records The Records that were loaded
21154          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21155          * @params {Object} return from reader
21156          */
21157         load : true,
21158         /**
21159          * @event loadexception
21160          * Fires if an exception occurs in the Proxy during loading.
21161          * Called with the signature of the Proxy's "loadexception" event.
21162          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21163          * 
21164          * @param {Proxy} 
21165          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21166          * @param {Object} load options 
21167          * @param {Object} jsonData from your request (normally this contains the Exception)
21168          */
21169         loadexception : true
21170     });
21171     
21172     if(this.proxy){
21173         this.proxy = Roo.factory(this.proxy, Roo.data);
21174         this.proxy.xmodule = this.xmodule || false;
21175         this.relayEvents(this.proxy,  ["loadexception"]);
21176     }
21177     this.sortToggle = {};
21178     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21179
21180     Roo.data.Store.superclass.constructor.call(this);
21181
21182     if(this.inlineData){
21183         this.loadData(this.inlineData);
21184         delete this.inlineData;
21185     }
21186 };
21187
21188 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21189      /**
21190     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21191     * without a remote query - used by combo/forms at present.
21192     */
21193     
21194     /**
21195     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21196     */
21197     /**
21198     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21199     */
21200     /**
21201     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21202     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21203     */
21204     /**
21205     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21206     * on any HTTP request
21207     */
21208     /**
21209     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21210     */
21211     /**
21212     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21213     */
21214     multiSort: false,
21215     /**
21216     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21217     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21218     */
21219     remoteSort : false,
21220
21221     /**
21222     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21223      * loaded or when a record is removed. (defaults to false).
21224     */
21225     pruneModifiedRecords : false,
21226
21227     // private
21228     lastOptions : null,
21229
21230     /**
21231      * Add Records to the Store and fires the add event.
21232      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21233      */
21234     add : function(records){
21235         records = [].concat(records);
21236         for(var i = 0, len = records.length; i < len; i++){
21237             records[i].join(this);
21238         }
21239         var index = this.data.length;
21240         this.data.addAll(records);
21241         this.fireEvent("add", this, records, index);
21242     },
21243
21244     /**
21245      * Remove a Record from the Store and fires the remove event.
21246      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21247      */
21248     remove : function(record){
21249         var index = this.data.indexOf(record);
21250         this.data.removeAt(index);
21251         if(this.pruneModifiedRecords){
21252             this.modified.remove(record);
21253         }
21254         this.fireEvent("remove", this, record, index);
21255     },
21256
21257     /**
21258      * Remove all Records from the Store and fires the clear event.
21259      */
21260     removeAll : function(){
21261         this.data.clear();
21262         if(this.pruneModifiedRecords){
21263             this.modified = [];
21264         }
21265         this.fireEvent("clear", this);
21266     },
21267
21268     /**
21269      * Inserts Records to the Store at the given index and fires the add event.
21270      * @param {Number} index The start index at which to insert the passed Records.
21271      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21272      */
21273     insert : function(index, records){
21274         records = [].concat(records);
21275         for(var i = 0, len = records.length; i < len; i++){
21276             this.data.insert(index, records[i]);
21277             records[i].join(this);
21278         }
21279         this.fireEvent("add", this, records, index);
21280     },
21281
21282     /**
21283      * Get the index within the cache of the passed Record.
21284      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21285      * @return {Number} The index of the passed Record. Returns -1 if not found.
21286      */
21287     indexOf : function(record){
21288         return this.data.indexOf(record);
21289     },
21290
21291     /**
21292      * Get the index within the cache of the Record with the passed id.
21293      * @param {String} id The id of the Record to find.
21294      * @return {Number} The index of the Record. Returns -1 if not found.
21295      */
21296     indexOfId : function(id){
21297         return this.data.indexOfKey(id);
21298     },
21299
21300     /**
21301      * Get the Record with the specified id.
21302      * @param {String} id The id of the Record to find.
21303      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21304      */
21305     getById : function(id){
21306         return this.data.key(id);
21307     },
21308
21309     /**
21310      * Get the Record at the specified index.
21311      * @param {Number} index The index of the Record to find.
21312      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21313      */
21314     getAt : function(index){
21315         return this.data.itemAt(index);
21316     },
21317
21318     /**
21319      * Returns a range of Records between specified indices.
21320      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21321      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21322      * @return {Roo.data.Record[]} An array of Records
21323      */
21324     getRange : function(start, end){
21325         return this.data.getRange(start, end);
21326     },
21327
21328     // private
21329     storeOptions : function(o){
21330         o = Roo.apply({}, o);
21331         delete o.callback;
21332         delete o.scope;
21333         this.lastOptions = o;
21334     },
21335
21336     /**
21337      * Loads the Record cache from the configured Proxy using the configured Reader.
21338      * <p>
21339      * If using remote paging, then the first load call must specify the <em>start</em>
21340      * and <em>limit</em> properties in the options.params property to establish the initial
21341      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21342      * <p>
21343      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21344      * and this call will return before the new data has been loaded. Perform any post-processing
21345      * in a callback function, or in a "load" event handler.</strong>
21346      * <p>
21347      * @param {Object} options An object containing properties which control loading options:<ul>
21348      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21349      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21350      * passed the following arguments:<ul>
21351      * <li>r : Roo.data.Record[]</li>
21352      * <li>options: Options object from the load call</li>
21353      * <li>success: Boolean success indicator</li></ul></li>
21354      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21355      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21356      * </ul>
21357      */
21358     load : function(options){
21359         options = options || {};
21360         if(this.fireEvent("beforeload", this, options) !== false){
21361             this.storeOptions(options);
21362             var p = Roo.apply(options.params || {}, this.baseParams);
21363             // if meta was not loaded from remote source.. try requesting it.
21364             if (!this.reader.metaFromRemote) {
21365                 p._requestMeta = 1;
21366             }
21367             if(this.sortInfo && this.remoteSort){
21368                 var pn = this.paramNames;
21369                 p[pn["sort"]] = this.sortInfo.field;
21370                 p[pn["dir"]] = this.sortInfo.direction;
21371             }
21372             if (this.multiSort) {
21373                 var pn = this.paramNames;
21374                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21375             }
21376             
21377             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21378         }
21379     },
21380
21381     /**
21382      * Reloads the Record cache from the configured Proxy using the configured Reader and
21383      * the options from the last load operation performed.
21384      * @param {Object} options (optional) An object containing properties which may override the options
21385      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21386      * the most recently used options are reused).
21387      */
21388     reload : function(options){
21389         this.load(Roo.applyIf(options||{}, this.lastOptions));
21390     },
21391
21392     // private
21393     // Called as a callback by the Reader during a load operation.
21394     loadRecords : function(o, options, success){
21395         if(!o || success === false){
21396             if(success !== false){
21397                 this.fireEvent("load", this, [], options, o);
21398             }
21399             if(options.callback){
21400                 options.callback.call(options.scope || this, [], options, false);
21401             }
21402             return;
21403         }
21404         // if data returned failure - throw an exception.
21405         if (o.success === false) {
21406             // show a message if no listener is registered.
21407             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21408                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21409             }
21410             // loadmask wil be hooked into this..
21411             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21412             return;
21413         }
21414         var r = o.records, t = o.totalRecords || r.length;
21415         
21416         this.fireEvent("beforeloadadd", this, r, options, o);
21417         
21418         if(!options || options.add !== true){
21419             if(this.pruneModifiedRecords){
21420                 this.modified = [];
21421             }
21422             for(var i = 0, len = r.length; i < len; i++){
21423                 r[i].join(this);
21424             }
21425             if(this.snapshot){
21426                 this.data = this.snapshot;
21427                 delete this.snapshot;
21428             }
21429             this.data.clear();
21430             this.data.addAll(r);
21431             this.totalLength = t;
21432             this.applySort();
21433             this.fireEvent("datachanged", this);
21434         }else{
21435             this.totalLength = Math.max(t, this.data.length+r.length);
21436             this.add(r);
21437         }
21438         this.fireEvent("load", this, r, options, o);
21439         if(options.callback){
21440             options.callback.call(options.scope || this, r, options, true);
21441         }
21442     },
21443
21444
21445     /**
21446      * Loads data from a passed data block. A Reader which understands the format of the data
21447      * must have been configured in the constructor.
21448      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21449      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21450      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21451      */
21452     loadData : function(o, append){
21453         var r = this.reader.readRecords(o);
21454         this.loadRecords(r, {add: append}, true);
21455     },
21456
21457     /**
21458      * Gets the number of cached records.
21459      * <p>
21460      * <em>If using paging, this may not be the total size of the dataset. If the data object
21461      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21462      * the data set size</em>
21463      */
21464     getCount : function(){
21465         return this.data.length || 0;
21466     },
21467
21468     /**
21469      * Gets the total number of records in the dataset as returned by the server.
21470      * <p>
21471      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21472      * the dataset size</em>
21473      */
21474     getTotalCount : function(){
21475         return this.totalLength || 0;
21476     },
21477
21478     /**
21479      * Returns the sort state of the Store as an object with two properties:
21480      * <pre><code>
21481  field {String} The name of the field by which the Records are sorted
21482  direction {String} The sort order, "ASC" or "DESC"
21483      * </code></pre>
21484      */
21485     getSortState : function(){
21486         return this.sortInfo;
21487     },
21488
21489     // private
21490     applySort : function(){
21491         if(this.sortInfo && !this.remoteSort){
21492             var s = this.sortInfo, f = s.field;
21493             var st = this.fields.get(f).sortType;
21494             var fn = function(r1, r2){
21495                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21496                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21497             };
21498             this.data.sort(s.direction, fn);
21499             if(this.snapshot && this.snapshot != this.data){
21500                 this.snapshot.sort(s.direction, fn);
21501             }
21502         }
21503     },
21504
21505     /**
21506      * Sets the default sort column and order to be used by the next load operation.
21507      * @param {String} fieldName The name of the field to sort by.
21508      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21509      */
21510     setDefaultSort : function(field, dir){
21511         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21512     },
21513
21514     /**
21515      * Sort the Records.
21516      * If remote sorting is used, the sort is performed on the server, and the cache is
21517      * reloaded. If local sorting is used, the cache is sorted internally.
21518      * @param {String} fieldName The name of the field to sort by.
21519      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21520      */
21521     sort : function(fieldName, dir){
21522         var f = this.fields.get(fieldName);
21523         if(!dir){
21524             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21525             
21526             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21527                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21528             }else{
21529                 dir = f.sortDir;
21530             }
21531         }
21532         this.sortToggle[f.name] = dir;
21533         this.sortInfo = {field: f.name, direction: dir};
21534         if(!this.remoteSort){
21535             this.applySort();
21536             this.fireEvent("datachanged", this);
21537         }else{
21538             this.load(this.lastOptions);
21539         }
21540     },
21541
21542     /**
21543      * Calls the specified function for each of the Records in the cache.
21544      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21545      * Returning <em>false</em> aborts and exits the iteration.
21546      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21547      */
21548     each : function(fn, scope){
21549         this.data.each(fn, scope);
21550     },
21551
21552     /**
21553      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21554      * (e.g., during paging).
21555      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21556      */
21557     getModifiedRecords : function(){
21558         return this.modified;
21559     },
21560
21561     // private
21562     createFilterFn : function(property, value, anyMatch){
21563         if(!value.exec){ // not a regex
21564             value = String(value);
21565             if(value.length == 0){
21566                 return false;
21567             }
21568             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21569         }
21570         return function(r){
21571             return value.test(r.data[property]);
21572         };
21573     },
21574
21575     /**
21576      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21577      * @param {String} property A field on your records
21578      * @param {Number} start The record index to start at (defaults to 0)
21579      * @param {Number} end The last record index to include (defaults to length - 1)
21580      * @return {Number} The sum
21581      */
21582     sum : function(property, start, end){
21583         var rs = this.data.items, v = 0;
21584         start = start || 0;
21585         end = (end || end === 0) ? end : rs.length-1;
21586
21587         for(var i = start; i <= end; i++){
21588             v += (rs[i].data[property] || 0);
21589         }
21590         return v;
21591     },
21592
21593     /**
21594      * Filter the records by a specified property.
21595      * @param {String} field A field on your records
21596      * @param {String/RegExp} value Either a string that the field
21597      * should start with or a RegExp to test against the field
21598      * @param {Boolean} anyMatch True to match any part not just the beginning
21599      */
21600     filter : function(property, value, anyMatch){
21601         var fn = this.createFilterFn(property, value, anyMatch);
21602         return fn ? this.filterBy(fn) : this.clearFilter();
21603     },
21604
21605     /**
21606      * Filter by a function. The specified function will be called with each
21607      * record in this data source. If the function returns true the record is included,
21608      * otherwise it is filtered.
21609      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21610      * @param {Object} scope (optional) The scope of the function (defaults to this)
21611      */
21612     filterBy : function(fn, scope){
21613         this.snapshot = this.snapshot || this.data;
21614         this.data = this.queryBy(fn, scope||this);
21615         this.fireEvent("datachanged", this);
21616     },
21617
21618     /**
21619      * Query the records by a specified property.
21620      * @param {String} field A field on your records
21621      * @param {String/RegExp} value Either a string that the field
21622      * should start with or a RegExp to test against the field
21623      * @param {Boolean} anyMatch True to match any part not just the beginning
21624      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21625      */
21626     query : function(property, value, anyMatch){
21627         var fn = this.createFilterFn(property, value, anyMatch);
21628         return fn ? this.queryBy(fn) : this.data.clone();
21629     },
21630
21631     /**
21632      * Query by a function. The specified function will be called with each
21633      * record in this data source. If the function returns true the record is included
21634      * in the results.
21635      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21636      * @param {Object} scope (optional) The scope of the function (defaults to this)
21637       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21638      **/
21639     queryBy : function(fn, scope){
21640         var data = this.snapshot || this.data;
21641         return data.filterBy(fn, scope||this);
21642     },
21643
21644     /**
21645      * Collects unique values for a particular dataIndex from this store.
21646      * @param {String} dataIndex The property to collect
21647      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21648      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21649      * @return {Array} An array of the unique values
21650      **/
21651     collect : function(dataIndex, allowNull, bypassFilter){
21652         var d = (bypassFilter === true && this.snapshot) ?
21653                 this.snapshot.items : this.data.items;
21654         var v, sv, r = [], l = {};
21655         for(var i = 0, len = d.length; i < len; i++){
21656             v = d[i].data[dataIndex];
21657             sv = String(v);
21658             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21659                 l[sv] = true;
21660                 r[r.length] = v;
21661             }
21662         }
21663         return r;
21664     },
21665
21666     /**
21667      * Revert to a view of the Record cache with no filtering applied.
21668      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21669      */
21670     clearFilter : function(suppressEvent){
21671         if(this.snapshot && this.snapshot != this.data){
21672             this.data = this.snapshot;
21673             delete this.snapshot;
21674             if(suppressEvent !== true){
21675                 this.fireEvent("datachanged", this);
21676             }
21677         }
21678     },
21679
21680     // private
21681     afterEdit : function(record){
21682         if(this.modified.indexOf(record) == -1){
21683             this.modified.push(record);
21684         }
21685         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21686     },
21687     
21688     // private
21689     afterReject : function(record){
21690         this.modified.remove(record);
21691         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21692     },
21693
21694     // private
21695     afterCommit : function(record){
21696         this.modified.remove(record);
21697         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21698     },
21699
21700     /**
21701      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21702      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21703      */
21704     commitChanges : function(){
21705         var m = this.modified.slice(0);
21706         this.modified = [];
21707         for(var i = 0, len = m.length; i < len; i++){
21708             m[i].commit();
21709         }
21710     },
21711
21712     /**
21713      * Cancel outstanding changes on all changed records.
21714      */
21715     rejectChanges : function(){
21716         var m = this.modified.slice(0);
21717         this.modified = [];
21718         for(var i = 0, len = m.length; i < len; i++){
21719             m[i].reject();
21720         }
21721     },
21722
21723     onMetaChange : function(meta, rtype, o){
21724         this.recordType = rtype;
21725         this.fields = rtype.prototype.fields;
21726         delete this.snapshot;
21727         this.sortInfo = meta.sortInfo || this.sortInfo;
21728         this.modified = [];
21729         this.fireEvent('metachange', this, this.reader.meta);
21730     }
21731 });/*
21732  * Based on:
21733  * Ext JS Library 1.1.1
21734  * Copyright(c) 2006-2007, Ext JS, LLC.
21735  *
21736  * Originally Released Under LGPL - original licence link has changed is not relivant.
21737  *
21738  * Fork - LGPL
21739  * <script type="text/javascript">
21740  */
21741
21742 /**
21743  * @class Roo.data.SimpleStore
21744  * @extends Roo.data.Store
21745  * Small helper class to make creating Stores from Array data easier.
21746  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21747  * @cfg {Array} fields An array of field definition objects, or field name strings.
21748  * @cfg {Array} data The multi-dimensional array of data
21749  * @constructor
21750  * @param {Object} config
21751  */
21752 Roo.data.SimpleStore = function(config){
21753     Roo.data.SimpleStore.superclass.constructor.call(this, {
21754         isLocal : true,
21755         reader: new Roo.data.ArrayReader({
21756                 id: config.id
21757             },
21758             Roo.data.Record.create(config.fields)
21759         ),
21760         proxy : new Roo.data.MemoryProxy(config.data)
21761     });
21762     this.load();
21763 };
21764 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21765  * Based on:
21766  * Ext JS Library 1.1.1
21767  * Copyright(c) 2006-2007, Ext JS, LLC.
21768  *
21769  * Originally Released Under LGPL - original licence link has changed is not relivant.
21770  *
21771  * Fork - LGPL
21772  * <script type="text/javascript">
21773  */
21774
21775 /**
21776 /**
21777  * @extends Roo.data.Store
21778  * @class Roo.data.JsonStore
21779  * Small helper class to make creating Stores for JSON data easier. <br/>
21780 <pre><code>
21781 var store = new Roo.data.JsonStore({
21782     url: 'get-images.php',
21783     root: 'images',
21784     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21785 });
21786 </code></pre>
21787  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21788  * JsonReader and HttpProxy (unless inline data is provided).</b>
21789  * @cfg {Array} fields An array of field definition objects, or field name strings.
21790  * @constructor
21791  * @param {Object} config
21792  */
21793 Roo.data.JsonStore = function(c){
21794     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21795         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21796         reader: new Roo.data.JsonReader(c, c.fields)
21797     }));
21798 };
21799 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21800  * Based on:
21801  * Ext JS Library 1.1.1
21802  * Copyright(c) 2006-2007, Ext JS, LLC.
21803  *
21804  * Originally Released Under LGPL - original licence link has changed is not relivant.
21805  *
21806  * Fork - LGPL
21807  * <script type="text/javascript">
21808  */
21809
21810  
21811 Roo.data.Field = function(config){
21812     if(typeof config == "string"){
21813         config = {name: config};
21814     }
21815     Roo.apply(this, config);
21816     
21817     if(!this.type){
21818         this.type = "auto";
21819     }
21820     
21821     var st = Roo.data.SortTypes;
21822     // named sortTypes are supported, here we look them up
21823     if(typeof this.sortType == "string"){
21824         this.sortType = st[this.sortType];
21825     }
21826     
21827     // set default sortType for strings and dates
21828     if(!this.sortType){
21829         switch(this.type){
21830             case "string":
21831                 this.sortType = st.asUCString;
21832                 break;
21833             case "date":
21834                 this.sortType = st.asDate;
21835                 break;
21836             default:
21837                 this.sortType = st.none;
21838         }
21839     }
21840
21841     // define once
21842     var stripRe = /[\$,%]/g;
21843
21844     // prebuilt conversion function for this field, instead of
21845     // switching every time we're reading a value
21846     if(!this.convert){
21847         var cv, dateFormat = this.dateFormat;
21848         switch(this.type){
21849             case "":
21850             case "auto":
21851             case undefined:
21852                 cv = function(v){ return v; };
21853                 break;
21854             case "string":
21855                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21856                 break;
21857             case "int":
21858                 cv = function(v){
21859                     return v !== undefined && v !== null && v !== '' ?
21860                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21861                     };
21862                 break;
21863             case "float":
21864                 cv = function(v){
21865                     return v !== undefined && v !== null && v !== '' ?
21866                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21867                     };
21868                 break;
21869             case "bool":
21870             case "boolean":
21871                 cv = function(v){ return v === true || v === "true" || v == 1; };
21872                 break;
21873             case "date":
21874                 cv = function(v){
21875                     if(!v){
21876                         return '';
21877                     }
21878                     if(v instanceof Date){
21879                         return v;
21880                     }
21881                     if(dateFormat){
21882                         if(dateFormat == "timestamp"){
21883                             return new Date(v*1000);
21884                         }
21885                         return Date.parseDate(v, dateFormat);
21886                     }
21887                     var parsed = Date.parse(v);
21888                     return parsed ? new Date(parsed) : null;
21889                 };
21890              break;
21891             
21892         }
21893         this.convert = cv;
21894     }
21895 };
21896
21897 Roo.data.Field.prototype = {
21898     dateFormat: null,
21899     defaultValue: "",
21900     mapping: null,
21901     sortType : null,
21902     sortDir : "ASC"
21903 };/*
21904  * Based on:
21905  * Ext JS Library 1.1.1
21906  * Copyright(c) 2006-2007, Ext JS, LLC.
21907  *
21908  * Originally Released Under LGPL - original licence link has changed is not relivant.
21909  *
21910  * Fork - LGPL
21911  * <script type="text/javascript">
21912  */
21913  
21914 // Base class for reading structured data from a data source.  This class is intended to be
21915 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21916
21917 /**
21918  * @class Roo.data.DataReader
21919  * Base class for reading structured data from a data source.  This class is intended to be
21920  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21921  */
21922
21923 Roo.data.DataReader = function(meta, recordType){
21924     
21925     this.meta = meta;
21926     
21927     this.recordType = recordType instanceof Array ? 
21928         Roo.data.Record.create(recordType) : recordType;
21929 };
21930
21931 Roo.data.DataReader.prototype = {
21932      /**
21933      * Create an empty record
21934      * @param {Object} data (optional) - overlay some values
21935      * @return {Roo.data.Record} record created.
21936      */
21937     newRow :  function(d) {
21938         var da =  {};
21939         this.recordType.prototype.fields.each(function(c) {
21940             switch( c.type) {
21941                 case 'int' : da[c.name] = 0; break;
21942                 case 'date' : da[c.name] = new Date(); break;
21943                 case 'float' : da[c.name] = 0.0; break;
21944                 case 'boolean' : da[c.name] = false; break;
21945                 default : da[c.name] = ""; break;
21946             }
21947             
21948         });
21949         return new this.recordType(Roo.apply(da, d));
21950     }
21951     
21952 };/*
21953  * Based on:
21954  * Ext JS Library 1.1.1
21955  * Copyright(c) 2006-2007, Ext JS, LLC.
21956  *
21957  * Originally Released Under LGPL - original licence link has changed is not relivant.
21958  *
21959  * Fork - LGPL
21960  * <script type="text/javascript">
21961  */
21962
21963 /**
21964  * @class Roo.data.DataProxy
21965  * @extends Roo.data.Observable
21966  * This class is an abstract base class for implementations which provide retrieval of
21967  * unformatted data objects.<br>
21968  * <p>
21969  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21970  * (of the appropriate type which knows how to parse the data object) to provide a block of
21971  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21972  * <p>
21973  * Custom implementations must implement the load method as described in
21974  * {@link Roo.data.HttpProxy#load}.
21975  */
21976 Roo.data.DataProxy = function(){
21977     this.addEvents({
21978         /**
21979          * @event beforeload
21980          * Fires before a network request is made to retrieve a data object.
21981          * @param {Object} This DataProxy object.
21982          * @param {Object} params The params parameter to the load function.
21983          */
21984         beforeload : true,
21985         /**
21986          * @event load
21987          * Fires before the load method's callback is called.
21988          * @param {Object} This DataProxy object.
21989          * @param {Object} o The data object.
21990          * @param {Object} arg The callback argument object passed to the load function.
21991          */
21992         load : true,
21993         /**
21994          * @event loadexception
21995          * Fires if an Exception occurs during data retrieval.
21996          * @param {Object} This DataProxy object.
21997          * @param {Object} o The data object.
21998          * @param {Object} arg The callback argument object passed to the load function.
21999          * @param {Object} e The Exception.
22000          */
22001         loadexception : true
22002     });
22003     Roo.data.DataProxy.superclass.constructor.call(this);
22004 };
22005
22006 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22007
22008     /**
22009      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22010      */
22011 /*
22012  * Based on:
22013  * Ext JS Library 1.1.1
22014  * Copyright(c) 2006-2007, Ext JS, LLC.
22015  *
22016  * Originally Released Under LGPL - original licence link has changed is not relivant.
22017  *
22018  * Fork - LGPL
22019  * <script type="text/javascript">
22020  */
22021 /**
22022  * @class Roo.data.MemoryProxy
22023  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22024  * to the Reader when its load method is called.
22025  * @constructor
22026  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22027  */
22028 Roo.data.MemoryProxy = function(data){
22029     if (data.data) {
22030         data = data.data;
22031     }
22032     Roo.data.MemoryProxy.superclass.constructor.call(this);
22033     this.data = data;
22034 };
22035
22036 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22037     /**
22038      * Load data from the requested source (in this case an in-memory
22039      * data object passed to the constructor), read the data object into
22040      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22041      * process that block using the passed callback.
22042      * @param {Object} params This parameter is not used by the MemoryProxy class.
22043      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22044      * object into a block of Roo.data.Records.
22045      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22046      * The function must be passed <ul>
22047      * <li>The Record block object</li>
22048      * <li>The "arg" argument from the load function</li>
22049      * <li>A boolean success indicator</li>
22050      * </ul>
22051      * @param {Object} scope The scope in which to call the callback
22052      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22053      */
22054     load : function(params, reader, callback, scope, arg){
22055         params = params || {};
22056         var result;
22057         try {
22058             result = reader.readRecords(this.data);
22059         }catch(e){
22060             this.fireEvent("loadexception", this, arg, null, e);
22061             callback.call(scope, null, arg, false);
22062             return;
22063         }
22064         callback.call(scope, result, arg, true);
22065     },
22066     
22067     // private
22068     update : function(params, records){
22069         
22070     }
22071 });/*
22072  * Based on:
22073  * Ext JS Library 1.1.1
22074  * Copyright(c) 2006-2007, Ext JS, LLC.
22075  *
22076  * Originally Released Under LGPL - original licence link has changed is not relivant.
22077  *
22078  * Fork - LGPL
22079  * <script type="text/javascript">
22080  */
22081 /**
22082  * @class Roo.data.HttpProxy
22083  * @extends Roo.data.DataProxy
22084  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22085  * configured to reference a certain URL.<br><br>
22086  * <p>
22087  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22088  * from which the running page was served.<br><br>
22089  * <p>
22090  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22091  * <p>
22092  * Be aware that to enable the browser to parse an XML document, the server must set
22093  * the Content-Type header in the HTTP response to "text/xml".
22094  * @constructor
22095  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22096  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22097  * will be used to make the request.
22098  */
22099 Roo.data.HttpProxy = function(conn){
22100     Roo.data.HttpProxy.superclass.constructor.call(this);
22101     // is conn a conn config or a real conn?
22102     this.conn = conn;
22103     this.useAjax = !conn || !conn.events;
22104   
22105 };
22106
22107 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22108     // thse are take from connection...
22109     
22110     /**
22111      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22112      */
22113     /**
22114      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22115      * extra parameters to each request made by this object. (defaults to undefined)
22116      */
22117     /**
22118      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22119      *  to each request made by this object. (defaults to undefined)
22120      */
22121     /**
22122      * @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)
22123      */
22124     /**
22125      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22126      */
22127      /**
22128      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22129      * @type Boolean
22130      */
22131   
22132
22133     /**
22134      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22135      * @type Boolean
22136      */
22137     /**
22138      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22139      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22140      * a finer-grained basis than the DataProxy events.
22141      */
22142     getConnection : function(){
22143         return this.useAjax ? Roo.Ajax : this.conn;
22144     },
22145
22146     /**
22147      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22148      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22149      * process that block using the passed callback.
22150      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22151      * for the request to the remote server.
22152      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22153      * object into a block of Roo.data.Records.
22154      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22155      * The function must be passed <ul>
22156      * <li>The Record block object</li>
22157      * <li>The "arg" argument from the load function</li>
22158      * <li>A boolean success indicator</li>
22159      * </ul>
22160      * @param {Object} scope The scope in which to call the callback
22161      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22162      */
22163     load : function(params, reader, callback, scope, arg){
22164         if(this.fireEvent("beforeload", this, params) !== false){
22165             var  o = {
22166                 params : params || {},
22167                 request: {
22168                     callback : callback,
22169                     scope : scope,
22170                     arg : arg
22171                 },
22172                 reader: reader,
22173                 callback : this.loadResponse,
22174                 scope: this
22175             };
22176             if(this.useAjax){
22177                 Roo.applyIf(o, this.conn);
22178                 if(this.activeRequest){
22179                     Roo.Ajax.abort(this.activeRequest);
22180                 }
22181                 this.activeRequest = Roo.Ajax.request(o);
22182             }else{
22183                 this.conn.request(o);
22184             }
22185         }else{
22186             callback.call(scope||this, null, arg, false);
22187         }
22188     },
22189
22190     // private
22191     loadResponse : function(o, success, response){
22192         delete this.activeRequest;
22193         if(!success){
22194             this.fireEvent("loadexception", this, o, response);
22195             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22196             return;
22197         }
22198         var result;
22199         try {
22200             result = o.reader.read(response);
22201         }catch(e){
22202             this.fireEvent("loadexception", this, o, response, e);
22203             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22204             return;
22205         }
22206         
22207         this.fireEvent("load", this, o, o.request.arg);
22208         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22209     },
22210
22211     // private
22212     update : function(dataSet){
22213
22214     },
22215
22216     // private
22217     updateResponse : function(dataSet){
22218
22219     }
22220 });/*
22221  * Based on:
22222  * Ext JS Library 1.1.1
22223  * Copyright(c) 2006-2007, Ext JS, LLC.
22224  *
22225  * Originally Released Under LGPL - original licence link has changed is not relivant.
22226  *
22227  * Fork - LGPL
22228  * <script type="text/javascript">
22229  */
22230
22231 /**
22232  * @class Roo.data.ScriptTagProxy
22233  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22234  * other than the originating domain of the running page.<br><br>
22235  * <p>
22236  * <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
22237  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22238  * <p>
22239  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22240  * source code that is used as the source inside a &lt;script> tag.<br><br>
22241  * <p>
22242  * In order for the browser to process the returned data, the server must wrap the data object
22243  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22244  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22245  * depending on whether the callback name was passed:
22246  * <p>
22247  * <pre><code>
22248 boolean scriptTag = false;
22249 String cb = request.getParameter("callback");
22250 if (cb != null) {
22251     scriptTag = true;
22252     response.setContentType("text/javascript");
22253 } else {
22254     response.setContentType("application/x-json");
22255 }
22256 Writer out = response.getWriter();
22257 if (scriptTag) {
22258     out.write(cb + "(");
22259 }
22260 out.print(dataBlock.toJsonString());
22261 if (scriptTag) {
22262     out.write(");");
22263 }
22264 </pre></code>
22265  *
22266  * @constructor
22267  * @param {Object} config A configuration object.
22268  */
22269 Roo.data.ScriptTagProxy = function(config){
22270     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22271     Roo.apply(this, config);
22272     this.head = document.getElementsByTagName("head")[0];
22273 };
22274
22275 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22276
22277 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22278     /**
22279      * @cfg {String} url The URL from which to request the data object.
22280      */
22281     /**
22282      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22283      */
22284     timeout : 30000,
22285     /**
22286      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22287      * the server the name of the callback function set up by the load call to process the returned data object.
22288      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22289      * javascript output which calls this named function passing the data object as its only parameter.
22290      */
22291     callbackParam : "callback",
22292     /**
22293      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22294      * name to the request.
22295      */
22296     nocache : true,
22297
22298     /**
22299      * Load data from the configured URL, read the data object into
22300      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22301      * process that block using the passed callback.
22302      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22303      * for the request to the remote server.
22304      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22305      * object into a block of Roo.data.Records.
22306      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22307      * The function must be passed <ul>
22308      * <li>The Record block object</li>
22309      * <li>The "arg" argument from the load function</li>
22310      * <li>A boolean success indicator</li>
22311      * </ul>
22312      * @param {Object} scope The scope in which to call the callback
22313      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22314      */
22315     load : function(params, reader, callback, scope, arg){
22316         if(this.fireEvent("beforeload", this, params) !== false){
22317
22318             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22319
22320             var url = this.url;
22321             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22322             if(this.nocache){
22323                 url += "&_dc=" + (new Date().getTime());
22324             }
22325             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22326             var trans = {
22327                 id : transId,
22328                 cb : "stcCallback"+transId,
22329                 scriptId : "stcScript"+transId,
22330                 params : params,
22331                 arg : arg,
22332                 url : url,
22333                 callback : callback,
22334                 scope : scope,
22335                 reader : reader
22336             };
22337             var conn = this;
22338
22339             window[trans.cb] = function(o){
22340                 conn.handleResponse(o, trans);
22341             };
22342
22343             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22344
22345             if(this.autoAbort !== false){
22346                 this.abort();
22347             }
22348
22349             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22350
22351             var script = document.createElement("script");
22352             script.setAttribute("src", url);
22353             script.setAttribute("type", "text/javascript");
22354             script.setAttribute("id", trans.scriptId);
22355             this.head.appendChild(script);
22356
22357             this.trans = trans;
22358         }else{
22359             callback.call(scope||this, null, arg, false);
22360         }
22361     },
22362
22363     // private
22364     isLoading : function(){
22365         return this.trans ? true : false;
22366     },
22367
22368     /**
22369      * Abort the current server request.
22370      */
22371     abort : function(){
22372         if(this.isLoading()){
22373             this.destroyTrans(this.trans);
22374         }
22375     },
22376
22377     // private
22378     destroyTrans : function(trans, isLoaded){
22379         this.head.removeChild(document.getElementById(trans.scriptId));
22380         clearTimeout(trans.timeoutId);
22381         if(isLoaded){
22382             window[trans.cb] = undefined;
22383             try{
22384                 delete window[trans.cb];
22385             }catch(e){}
22386         }else{
22387             // if hasn't been loaded, wait for load to remove it to prevent script error
22388             window[trans.cb] = function(){
22389                 window[trans.cb] = undefined;
22390                 try{
22391                     delete window[trans.cb];
22392                 }catch(e){}
22393             };
22394         }
22395     },
22396
22397     // private
22398     handleResponse : function(o, trans){
22399         this.trans = false;
22400         this.destroyTrans(trans, true);
22401         var result;
22402         try {
22403             result = trans.reader.readRecords(o);
22404         }catch(e){
22405             this.fireEvent("loadexception", this, o, trans.arg, e);
22406             trans.callback.call(trans.scope||window, null, trans.arg, false);
22407             return;
22408         }
22409         this.fireEvent("load", this, o, trans.arg);
22410         trans.callback.call(trans.scope||window, result, trans.arg, true);
22411     },
22412
22413     // private
22414     handleFailure : function(trans){
22415         this.trans = false;
22416         this.destroyTrans(trans, false);
22417         this.fireEvent("loadexception", this, null, trans.arg);
22418         trans.callback.call(trans.scope||window, null, trans.arg, false);
22419     }
22420 });/*
22421  * Based on:
22422  * Ext JS Library 1.1.1
22423  * Copyright(c) 2006-2007, Ext JS, LLC.
22424  *
22425  * Originally Released Under LGPL - original licence link has changed is not relivant.
22426  *
22427  * Fork - LGPL
22428  * <script type="text/javascript">
22429  */
22430
22431 /**
22432  * @class Roo.data.JsonReader
22433  * @extends Roo.data.DataReader
22434  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22435  * based on mappings in a provided Roo.data.Record constructor.
22436  * 
22437  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22438  * in the reply previously. 
22439  * 
22440  * <p>
22441  * Example code:
22442  * <pre><code>
22443 var RecordDef = Roo.data.Record.create([
22444     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22445     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22446 ]);
22447 var myReader = new Roo.data.JsonReader({
22448     totalProperty: "results",    // The property which contains the total dataset size (optional)
22449     root: "rows",                // The property which contains an Array of row objects
22450     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22451 }, RecordDef);
22452 </code></pre>
22453  * <p>
22454  * This would consume a JSON file like this:
22455  * <pre><code>
22456 { 'results': 2, 'rows': [
22457     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22458     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22459 }
22460 </code></pre>
22461  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22462  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22463  * paged from the remote server.
22464  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22465  * @cfg {String} root name of the property which contains the Array of row objects.
22466  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22467  * @constructor
22468  * Create a new JsonReader
22469  * @param {Object} meta Metadata configuration options
22470  * @param {Object} recordType Either an Array of field definition objects,
22471  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22472  */
22473 Roo.data.JsonReader = function(meta, recordType){
22474     
22475     meta = meta || {};
22476     // set some defaults:
22477     Roo.applyIf(meta, {
22478         totalProperty: 'total',
22479         successProperty : 'success',
22480         root : 'data',
22481         id : 'id'
22482     });
22483     
22484     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22485 };
22486 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22487     
22488     /**
22489      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22490      * Used by Store query builder to append _requestMeta to params.
22491      * 
22492      */
22493     metaFromRemote : false,
22494     /**
22495      * This method is only used by a DataProxy which has retrieved data from a remote server.
22496      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22497      * @return {Object} data A data block which is used by an Roo.data.Store object as
22498      * a cache of Roo.data.Records.
22499      */
22500     read : function(response){
22501         var json = response.responseText;
22502        
22503         var o = /* eval:var:o */ eval("("+json+")");
22504         if(!o) {
22505             throw {message: "JsonReader.read: Json object not found"};
22506         }
22507         
22508         if(o.metaData){
22509             
22510             delete this.ef;
22511             this.metaFromRemote = true;
22512             this.meta = o.metaData;
22513             this.recordType = Roo.data.Record.create(o.metaData.fields);
22514             this.onMetaChange(this.meta, this.recordType, o);
22515         }
22516         return this.readRecords(o);
22517     },
22518
22519     // private function a store will implement
22520     onMetaChange : function(meta, recordType, o){
22521
22522     },
22523
22524     /**
22525          * @ignore
22526          */
22527     simpleAccess: function(obj, subsc) {
22528         return obj[subsc];
22529     },
22530
22531         /**
22532          * @ignore
22533          */
22534     getJsonAccessor: function(){
22535         var re = /[\[\.]/;
22536         return function(expr) {
22537             try {
22538                 return(re.test(expr))
22539                     ? new Function("obj", "return obj." + expr)
22540                     : function(obj){
22541                         return obj[expr];
22542                     };
22543             } catch(e){}
22544             return Roo.emptyFn;
22545         };
22546     }(),
22547
22548     /**
22549      * Create a data block containing Roo.data.Records from an XML document.
22550      * @param {Object} o An object which contains an Array of row objects in the property specified
22551      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22552      * which contains the total size of the dataset.
22553      * @return {Object} data A data block which is used by an Roo.data.Store object as
22554      * a cache of Roo.data.Records.
22555      */
22556     readRecords : function(o){
22557         /**
22558          * After any data loads, the raw JSON data is available for further custom processing.
22559          * @type Object
22560          */
22561         this.o = o;
22562         var s = this.meta, Record = this.recordType,
22563             f = Record.prototype.fields, fi = f.items, fl = f.length;
22564
22565 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22566         if (!this.ef) {
22567             if(s.totalProperty) {
22568                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22569                 }
22570                 if(s.successProperty) {
22571                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22572                 }
22573                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22574                 if (s.id) {
22575                         var g = this.getJsonAccessor(s.id);
22576                         this.getId = function(rec) {
22577                                 var r = g(rec);
22578                                 return (r === undefined || r === "") ? null : r;
22579                         };
22580                 } else {
22581                         this.getId = function(){return null;};
22582                 }
22583             this.ef = [];
22584             for(var jj = 0; jj < fl; jj++){
22585                 f = fi[jj];
22586                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22587                 this.ef[jj] = this.getJsonAccessor(map);
22588             }
22589         }
22590
22591         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22592         if(s.totalProperty){
22593             var vt = parseInt(this.getTotal(o), 10);
22594             if(!isNaN(vt)){
22595                 totalRecords = vt;
22596             }
22597         }
22598         if(s.successProperty){
22599             var vs = this.getSuccess(o);
22600             if(vs === false || vs === 'false'){
22601                 success = false;
22602             }
22603         }
22604         var records = [];
22605             for(var i = 0; i < c; i++){
22606                     var n = root[i];
22607                 var values = {};
22608                 var id = this.getId(n);
22609                 for(var j = 0; j < fl; j++){
22610                     f = fi[j];
22611                 var v = this.ef[j](n);
22612                 if (!f.convert) {
22613                     Roo.log('missing convert for ' + f.name);
22614                     Roo.log(f);
22615                     continue;
22616                 }
22617                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22618                 }
22619                 var record = new Record(values, id);
22620                 record.json = n;
22621                 records[i] = record;
22622             }
22623             return {
22624             raw : o,
22625                 success : success,
22626                 records : records,
22627                 totalRecords : totalRecords
22628             };
22629     }
22630 });/*
22631  * Based on:
22632  * Ext JS Library 1.1.1
22633  * Copyright(c) 2006-2007, Ext JS, LLC.
22634  *
22635  * Originally Released Under LGPL - original licence link has changed is not relivant.
22636  *
22637  * Fork - LGPL
22638  * <script type="text/javascript">
22639  */
22640
22641 /**
22642  * @class Roo.data.XmlReader
22643  * @extends Roo.data.DataReader
22644  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22645  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22646  * <p>
22647  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22648  * header in the HTTP response must be set to "text/xml".</em>
22649  * <p>
22650  * Example code:
22651  * <pre><code>
22652 var RecordDef = Roo.data.Record.create([
22653    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22654    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22655 ]);
22656 var myReader = new Roo.data.XmlReader({
22657    totalRecords: "results", // The element which contains the total dataset size (optional)
22658    record: "row",           // The repeated element which contains row information
22659    id: "id"                 // The element within the row that provides an ID for the record (optional)
22660 }, RecordDef);
22661 </code></pre>
22662  * <p>
22663  * This would consume an XML file like this:
22664  * <pre><code>
22665 &lt;?xml?>
22666 &lt;dataset>
22667  &lt;results>2&lt;/results>
22668  &lt;row>
22669    &lt;id>1&lt;/id>
22670    &lt;name>Bill&lt;/name>
22671    &lt;occupation>Gardener&lt;/occupation>
22672  &lt;/row>
22673  &lt;row>
22674    &lt;id>2&lt;/id>
22675    &lt;name>Ben&lt;/name>
22676    &lt;occupation>Horticulturalist&lt;/occupation>
22677  &lt;/row>
22678 &lt;/dataset>
22679 </code></pre>
22680  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22681  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22682  * paged from the remote server.
22683  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22684  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22685  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22686  * a record identifier value.
22687  * @constructor
22688  * Create a new XmlReader
22689  * @param {Object} meta Metadata configuration options
22690  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22691  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22692  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22693  */
22694 Roo.data.XmlReader = function(meta, recordType){
22695     meta = meta || {};
22696     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22697 };
22698 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22699     /**
22700      * This method is only used by a DataProxy which has retrieved data from a remote server.
22701          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22702          * to contain a method called 'responseXML' that returns an XML document object.
22703      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22704      * a cache of Roo.data.Records.
22705      */
22706     read : function(response){
22707         var doc = response.responseXML;
22708         if(!doc) {
22709             throw {message: "XmlReader.read: XML Document not available"};
22710         }
22711         return this.readRecords(doc);
22712     },
22713
22714     /**
22715      * Create a data block containing Roo.data.Records from an XML document.
22716          * @param {Object} doc A parsed XML document.
22717      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22718      * a cache of Roo.data.Records.
22719      */
22720     readRecords : function(doc){
22721         /**
22722          * After any data loads/reads, the raw XML Document is available for further custom processing.
22723          * @type XMLDocument
22724          */
22725         this.xmlData = doc;
22726         var root = doc.documentElement || doc;
22727         var q = Roo.DomQuery;
22728         var recordType = this.recordType, fields = recordType.prototype.fields;
22729         var sid = this.meta.id;
22730         var totalRecords = 0, success = true;
22731         if(this.meta.totalRecords){
22732             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22733         }
22734         
22735         if(this.meta.success){
22736             var sv = q.selectValue(this.meta.success, root, true);
22737             success = sv !== false && sv !== 'false';
22738         }
22739         var records = [];
22740         var ns = q.select(this.meta.record, root);
22741         for(var i = 0, len = ns.length; i < len; i++) {
22742                 var n = ns[i];
22743                 var values = {};
22744                 var id = sid ? q.selectValue(sid, n) : undefined;
22745                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22746                     var f = fields.items[j];
22747                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22748                     v = f.convert(v);
22749                     values[f.name] = v;
22750                 }
22751                 var record = new recordType(values, id);
22752                 record.node = n;
22753                 records[records.length] = record;
22754             }
22755
22756             return {
22757                 success : success,
22758                 records : records,
22759                 totalRecords : totalRecords || records.length
22760             };
22761     }
22762 });/*
22763  * Based on:
22764  * Ext JS Library 1.1.1
22765  * Copyright(c) 2006-2007, Ext JS, LLC.
22766  *
22767  * Originally Released Under LGPL - original licence link has changed is not relivant.
22768  *
22769  * Fork - LGPL
22770  * <script type="text/javascript">
22771  */
22772
22773 /**
22774  * @class Roo.data.ArrayReader
22775  * @extends Roo.data.DataReader
22776  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22777  * Each element of that Array represents a row of data fields. The
22778  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22779  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22780  * <p>
22781  * Example code:.
22782  * <pre><code>
22783 var RecordDef = Roo.data.Record.create([
22784     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22785     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22786 ]);
22787 var myReader = new Roo.data.ArrayReader({
22788     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22789 }, RecordDef);
22790 </code></pre>
22791  * <p>
22792  * This would consume an Array like this:
22793  * <pre><code>
22794 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22795   </code></pre>
22796  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22797  * @constructor
22798  * Create a new JsonReader
22799  * @param {Object} meta Metadata configuration options.
22800  * @param {Object} recordType Either an Array of field definition objects
22801  * as specified to {@link Roo.data.Record#create},
22802  * or an {@link Roo.data.Record} object
22803  * created using {@link Roo.data.Record#create}.
22804  */
22805 Roo.data.ArrayReader = function(meta, recordType){
22806     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22807 };
22808
22809 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22810     /**
22811      * Create a data block containing Roo.data.Records from an XML document.
22812      * @param {Object} o An Array of row objects which represents the dataset.
22813      * @return {Object} data A data block which is used by an Roo.data.Store object as
22814      * a cache of Roo.data.Records.
22815      */
22816     readRecords : function(o){
22817         var sid = this.meta ? this.meta.id : null;
22818         var recordType = this.recordType, fields = recordType.prototype.fields;
22819         var records = [];
22820         var root = o;
22821             for(var i = 0; i < root.length; i++){
22822                     var n = root[i];
22823                 var values = {};
22824                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22825                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22826                 var f = fields.items[j];
22827                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22828                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22829                 v = f.convert(v);
22830                 values[f.name] = v;
22831             }
22832                 var record = new recordType(values, id);
22833                 record.json = n;
22834                 records[records.length] = record;
22835             }
22836             return {
22837                 records : records,
22838                 totalRecords : records.length
22839             };
22840     }
22841 });/*
22842  * Based on:
22843  * Ext JS Library 1.1.1
22844  * Copyright(c) 2006-2007, Ext JS, LLC.
22845  *
22846  * Originally Released Under LGPL - original licence link has changed is not relivant.
22847  *
22848  * Fork - LGPL
22849  * <script type="text/javascript">
22850  */
22851
22852
22853 /**
22854  * @class Roo.data.Tree
22855  * @extends Roo.util.Observable
22856  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22857  * in the tree have most standard DOM functionality.
22858  * @constructor
22859  * @param {Node} root (optional) The root node
22860  */
22861 Roo.data.Tree = function(root){
22862    this.nodeHash = {};
22863    /**
22864     * The root node for this tree
22865     * @type Node
22866     */
22867    this.root = null;
22868    if(root){
22869        this.setRootNode(root);
22870    }
22871    this.addEvents({
22872        /**
22873         * @event append
22874         * Fires when a new child node is appended to a node in this tree.
22875         * @param {Tree} tree The owner tree
22876         * @param {Node} parent The parent node
22877         * @param {Node} node The newly appended node
22878         * @param {Number} index The index of the newly appended node
22879         */
22880        "append" : true,
22881        /**
22882         * @event remove
22883         * Fires when a child node is removed from a node in this tree.
22884         * @param {Tree} tree The owner tree
22885         * @param {Node} parent The parent node
22886         * @param {Node} node The child node removed
22887         */
22888        "remove" : true,
22889        /**
22890         * @event move
22891         * Fires when a node is moved to a new location in the tree
22892         * @param {Tree} tree The owner tree
22893         * @param {Node} node The node moved
22894         * @param {Node} oldParent The old parent of this node
22895         * @param {Node} newParent The new parent of this node
22896         * @param {Number} index The index it was moved to
22897         */
22898        "move" : true,
22899        /**
22900         * @event insert
22901         * Fires when a new child node is inserted in a node in this tree.
22902         * @param {Tree} tree The owner tree
22903         * @param {Node} parent The parent node
22904         * @param {Node} node The child node inserted
22905         * @param {Node} refNode The child node the node was inserted before
22906         */
22907        "insert" : true,
22908        /**
22909         * @event beforeappend
22910         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22911         * @param {Tree} tree The owner tree
22912         * @param {Node} parent The parent node
22913         * @param {Node} node The child node to be appended
22914         */
22915        "beforeappend" : true,
22916        /**
22917         * @event beforeremove
22918         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22919         * @param {Tree} tree The owner tree
22920         * @param {Node} parent The parent node
22921         * @param {Node} node The child node to be removed
22922         */
22923        "beforeremove" : true,
22924        /**
22925         * @event beforemove
22926         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22927         * @param {Tree} tree The owner tree
22928         * @param {Node} node The node being moved
22929         * @param {Node} oldParent The parent of the node
22930         * @param {Node} newParent The new parent the node is moving to
22931         * @param {Number} index The index it is being moved to
22932         */
22933        "beforemove" : true,
22934        /**
22935         * @event beforeinsert
22936         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22937         * @param {Tree} tree The owner tree
22938         * @param {Node} parent The parent node
22939         * @param {Node} node The child node to be inserted
22940         * @param {Node} refNode The child node the node is being inserted before
22941         */
22942        "beforeinsert" : true
22943    });
22944
22945     Roo.data.Tree.superclass.constructor.call(this);
22946 };
22947
22948 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22949     pathSeparator: "/",
22950
22951     proxyNodeEvent : function(){
22952         return this.fireEvent.apply(this, arguments);
22953     },
22954
22955     /**
22956      * Returns the root node for this tree.
22957      * @return {Node}
22958      */
22959     getRootNode : function(){
22960         return this.root;
22961     },
22962
22963     /**
22964      * Sets the root node for this tree.
22965      * @param {Node} node
22966      * @return {Node}
22967      */
22968     setRootNode : function(node){
22969         this.root = node;
22970         node.ownerTree = this;
22971         node.isRoot = true;
22972         this.registerNode(node);
22973         return node;
22974     },
22975
22976     /**
22977      * Gets a node in this tree by its id.
22978      * @param {String} id
22979      * @return {Node}
22980      */
22981     getNodeById : function(id){
22982         return this.nodeHash[id];
22983     },
22984
22985     registerNode : function(node){
22986         this.nodeHash[node.id] = node;
22987     },
22988
22989     unregisterNode : function(node){
22990         delete this.nodeHash[node.id];
22991     },
22992
22993     toString : function(){
22994         return "[Tree"+(this.id?" "+this.id:"")+"]";
22995     }
22996 });
22997
22998 /**
22999  * @class Roo.data.Node
23000  * @extends Roo.util.Observable
23001  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23002  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23003  * @constructor
23004  * @param {Object} attributes The attributes/config for the node
23005  */
23006 Roo.data.Node = function(attributes){
23007     /**
23008      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23009      * @type {Object}
23010      */
23011     this.attributes = attributes || {};
23012     this.leaf = this.attributes.leaf;
23013     /**
23014      * The node id. @type String
23015      */
23016     this.id = this.attributes.id;
23017     if(!this.id){
23018         this.id = Roo.id(null, "ynode-");
23019         this.attributes.id = this.id;
23020     }
23021      
23022     
23023     /**
23024      * All child nodes of this node. @type Array
23025      */
23026     this.childNodes = [];
23027     if(!this.childNodes.indexOf){ // indexOf is a must
23028         this.childNodes.indexOf = function(o){
23029             for(var i = 0, len = this.length; i < len; i++){
23030                 if(this[i] == o) {
23031                     return i;
23032                 }
23033             }
23034             return -1;
23035         };
23036     }
23037     /**
23038      * The parent node for this node. @type Node
23039      */
23040     this.parentNode = null;
23041     /**
23042      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23043      */
23044     this.firstChild = null;
23045     /**
23046      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23047      */
23048     this.lastChild = null;
23049     /**
23050      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23051      */
23052     this.previousSibling = null;
23053     /**
23054      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23055      */
23056     this.nextSibling = null;
23057
23058     this.addEvents({
23059        /**
23060         * @event append
23061         * Fires when a new child node is appended
23062         * @param {Tree} tree The owner tree
23063         * @param {Node} this This node
23064         * @param {Node} node The newly appended node
23065         * @param {Number} index The index of the newly appended node
23066         */
23067        "append" : true,
23068        /**
23069         * @event remove
23070         * Fires when a child node is removed
23071         * @param {Tree} tree The owner tree
23072         * @param {Node} this This node
23073         * @param {Node} node The removed node
23074         */
23075        "remove" : true,
23076        /**
23077         * @event move
23078         * Fires when this node is moved to a new location in the tree
23079         * @param {Tree} tree The owner tree
23080         * @param {Node} this This node
23081         * @param {Node} oldParent The old parent of this node
23082         * @param {Node} newParent The new parent of this node
23083         * @param {Number} index The index it was moved to
23084         */
23085        "move" : true,
23086        /**
23087         * @event insert
23088         * Fires when a new child node is inserted.
23089         * @param {Tree} tree The owner tree
23090         * @param {Node} this This node
23091         * @param {Node} node The child node inserted
23092         * @param {Node} refNode The child node the node was inserted before
23093         */
23094        "insert" : true,
23095        /**
23096         * @event beforeappend
23097         * Fires before a new child is appended, return false to cancel the append.
23098         * @param {Tree} tree The owner tree
23099         * @param {Node} this This node
23100         * @param {Node} node The child node to be appended
23101         */
23102        "beforeappend" : true,
23103        /**
23104         * @event beforeremove
23105         * Fires before a child is removed, return false to cancel the remove.
23106         * @param {Tree} tree The owner tree
23107         * @param {Node} this This node
23108         * @param {Node} node The child node to be removed
23109         */
23110        "beforeremove" : true,
23111        /**
23112         * @event beforemove
23113         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23114         * @param {Tree} tree The owner tree
23115         * @param {Node} this This node
23116         * @param {Node} oldParent The parent of this node
23117         * @param {Node} newParent The new parent this node is moving to
23118         * @param {Number} index The index it is being moved to
23119         */
23120        "beforemove" : true,
23121        /**
23122         * @event beforeinsert
23123         * Fires before a new child is inserted, return false to cancel the insert.
23124         * @param {Tree} tree The owner tree
23125         * @param {Node} this This node
23126         * @param {Node} node The child node to be inserted
23127         * @param {Node} refNode The child node the node is being inserted before
23128         */
23129        "beforeinsert" : true
23130    });
23131     this.listeners = this.attributes.listeners;
23132     Roo.data.Node.superclass.constructor.call(this);
23133 };
23134
23135 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23136     fireEvent : function(evtName){
23137         // first do standard event for this node
23138         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23139             return false;
23140         }
23141         // then bubble it up to the tree if the event wasn't cancelled
23142         var ot = this.getOwnerTree();
23143         if(ot){
23144             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23145                 return false;
23146             }
23147         }
23148         return true;
23149     },
23150
23151     /**
23152      * Returns true if this node is a leaf
23153      * @return {Boolean}
23154      */
23155     isLeaf : function(){
23156         return this.leaf === true;
23157     },
23158
23159     // private
23160     setFirstChild : function(node){
23161         this.firstChild = node;
23162     },
23163
23164     //private
23165     setLastChild : function(node){
23166         this.lastChild = node;
23167     },
23168
23169
23170     /**
23171      * Returns true if this node is the last child of its parent
23172      * @return {Boolean}
23173      */
23174     isLast : function(){
23175        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23176     },
23177
23178     /**
23179      * Returns true if this node is the first child of its parent
23180      * @return {Boolean}
23181      */
23182     isFirst : function(){
23183        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23184     },
23185
23186     hasChildNodes : function(){
23187         return !this.isLeaf() && this.childNodes.length > 0;
23188     },
23189
23190     /**
23191      * Insert node(s) as the last child node of this node.
23192      * @param {Node/Array} node The node or Array of nodes to append
23193      * @return {Node} The appended node if single append, or null if an array was passed
23194      */
23195     appendChild : function(node){
23196         var multi = false;
23197         if(node instanceof Array){
23198             multi = node;
23199         }else if(arguments.length > 1){
23200             multi = arguments;
23201         }
23202         // if passed an array or multiple args do them one by one
23203         if(multi){
23204             for(var i = 0, len = multi.length; i < len; i++) {
23205                 this.appendChild(multi[i]);
23206             }
23207         }else{
23208             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23209                 return false;
23210             }
23211             var index = this.childNodes.length;
23212             var oldParent = node.parentNode;
23213             // it's a move, make sure we move it cleanly
23214             if(oldParent){
23215                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23216                     return false;
23217                 }
23218                 oldParent.removeChild(node);
23219             }
23220             index = this.childNodes.length;
23221             if(index == 0){
23222                 this.setFirstChild(node);
23223             }
23224             this.childNodes.push(node);
23225             node.parentNode = this;
23226             var ps = this.childNodes[index-1];
23227             if(ps){
23228                 node.previousSibling = ps;
23229                 ps.nextSibling = node;
23230             }else{
23231                 node.previousSibling = null;
23232             }
23233             node.nextSibling = null;
23234             this.setLastChild(node);
23235             node.setOwnerTree(this.getOwnerTree());
23236             this.fireEvent("append", this.ownerTree, this, node, index);
23237             if(oldParent){
23238                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23239             }
23240             return node;
23241         }
23242     },
23243
23244     /**
23245      * Removes a child node from this node.
23246      * @param {Node} node The node to remove
23247      * @return {Node} The removed node
23248      */
23249     removeChild : function(node){
23250         var index = this.childNodes.indexOf(node);
23251         if(index == -1){
23252             return false;
23253         }
23254         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23255             return false;
23256         }
23257
23258         // remove it from childNodes collection
23259         this.childNodes.splice(index, 1);
23260
23261         // update siblings
23262         if(node.previousSibling){
23263             node.previousSibling.nextSibling = node.nextSibling;
23264         }
23265         if(node.nextSibling){
23266             node.nextSibling.previousSibling = node.previousSibling;
23267         }
23268
23269         // update child refs
23270         if(this.firstChild == node){
23271             this.setFirstChild(node.nextSibling);
23272         }
23273         if(this.lastChild == node){
23274             this.setLastChild(node.previousSibling);
23275         }
23276
23277         node.setOwnerTree(null);
23278         // clear any references from the node
23279         node.parentNode = null;
23280         node.previousSibling = null;
23281         node.nextSibling = null;
23282         this.fireEvent("remove", this.ownerTree, this, node);
23283         return node;
23284     },
23285
23286     /**
23287      * Inserts the first node before the second node in this nodes childNodes collection.
23288      * @param {Node} node The node to insert
23289      * @param {Node} refNode The node to insert before (if null the node is appended)
23290      * @return {Node} The inserted node
23291      */
23292     insertBefore : function(node, refNode){
23293         if(!refNode){ // like standard Dom, refNode can be null for append
23294             return this.appendChild(node);
23295         }
23296         // nothing to do
23297         if(node == refNode){
23298             return false;
23299         }
23300
23301         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23302             return false;
23303         }
23304         var index = this.childNodes.indexOf(refNode);
23305         var oldParent = node.parentNode;
23306         var refIndex = index;
23307
23308         // when moving internally, indexes will change after remove
23309         if(oldParent == this && this.childNodes.indexOf(node) < index){
23310             refIndex--;
23311         }
23312
23313         // it's a move, make sure we move it cleanly
23314         if(oldParent){
23315             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23316                 return false;
23317             }
23318             oldParent.removeChild(node);
23319         }
23320         if(refIndex == 0){
23321             this.setFirstChild(node);
23322         }
23323         this.childNodes.splice(refIndex, 0, node);
23324         node.parentNode = this;
23325         var ps = this.childNodes[refIndex-1];
23326         if(ps){
23327             node.previousSibling = ps;
23328             ps.nextSibling = node;
23329         }else{
23330             node.previousSibling = null;
23331         }
23332         node.nextSibling = refNode;
23333         refNode.previousSibling = node;
23334         node.setOwnerTree(this.getOwnerTree());
23335         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23336         if(oldParent){
23337             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23338         }
23339         return node;
23340     },
23341
23342     /**
23343      * Returns the child node at the specified index.
23344      * @param {Number} index
23345      * @return {Node}
23346      */
23347     item : function(index){
23348         return this.childNodes[index];
23349     },
23350
23351     /**
23352      * Replaces one child node in this node with another.
23353      * @param {Node} newChild The replacement node
23354      * @param {Node} oldChild The node to replace
23355      * @return {Node} The replaced node
23356      */
23357     replaceChild : function(newChild, oldChild){
23358         this.insertBefore(newChild, oldChild);
23359         this.removeChild(oldChild);
23360         return oldChild;
23361     },
23362
23363     /**
23364      * Returns the index of a child node
23365      * @param {Node} node
23366      * @return {Number} The index of the node or -1 if it was not found
23367      */
23368     indexOf : function(child){
23369         return this.childNodes.indexOf(child);
23370     },
23371
23372     /**
23373      * Returns the tree this node is in.
23374      * @return {Tree}
23375      */
23376     getOwnerTree : function(){
23377         // if it doesn't have one, look for one
23378         if(!this.ownerTree){
23379             var p = this;
23380             while(p){
23381                 if(p.ownerTree){
23382                     this.ownerTree = p.ownerTree;
23383                     break;
23384                 }
23385                 p = p.parentNode;
23386             }
23387         }
23388         return this.ownerTree;
23389     },
23390
23391     /**
23392      * Returns depth of this node (the root node has a depth of 0)
23393      * @return {Number}
23394      */
23395     getDepth : function(){
23396         var depth = 0;
23397         var p = this;
23398         while(p.parentNode){
23399             ++depth;
23400             p = p.parentNode;
23401         }
23402         return depth;
23403     },
23404
23405     // private
23406     setOwnerTree : function(tree){
23407         // if it's move, we need to update everyone
23408         if(tree != this.ownerTree){
23409             if(this.ownerTree){
23410                 this.ownerTree.unregisterNode(this);
23411             }
23412             this.ownerTree = tree;
23413             var cs = this.childNodes;
23414             for(var i = 0, len = cs.length; i < len; i++) {
23415                 cs[i].setOwnerTree(tree);
23416             }
23417             if(tree){
23418                 tree.registerNode(this);
23419             }
23420         }
23421     },
23422
23423     /**
23424      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23425      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23426      * @return {String} The path
23427      */
23428     getPath : function(attr){
23429         attr = attr || "id";
23430         var p = this.parentNode;
23431         var b = [this.attributes[attr]];
23432         while(p){
23433             b.unshift(p.attributes[attr]);
23434             p = p.parentNode;
23435         }
23436         var sep = this.getOwnerTree().pathSeparator;
23437         return sep + b.join(sep);
23438     },
23439
23440     /**
23441      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23442      * function call will be the scope provided or the current node. The arguments to the function
23443      * will be the args provided or the current node. If the function returns false at any point,
23444      * the bubble is stopped.
23445      * @param {Function} fn The function to call
23446      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23447      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23448      */
23449     bubble : function(fn, scope, args){
23450         var p = this;
23451         while(p){
23452             if(fn.call(scope || p, args || p) === false){
23453                 break;
23454             }
23455             p = p.parentNode;
23456         }
23457     },
23458
23459     /**
23460      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23461      * function call will be the scope provided or the current node. The arguments to the function
23462      * will be the args provided or the current node. If the function returns false at any point,
23463      * the cascade is stopped on that branch.
23464      * @param {Function} fn The function to call
23465      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23466      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23467      */
23468     cascade : function(fn, scope, args){
23469         if(fn.call(scope || this, args || this) !== false){
23470             var cs = this.childNodes;
23471             for(var i = 0, len = cs.length; i < len; i++) {
23472                 cs[i].cascade(fn, scope, args);
23473             }
23474         }
23475     },
23476
23477     /**
23478      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23479      * function call will be the scope provided or the current node. The arguments to the function
23480      * will be the args provided or the current node. If the function returns false at any point,
23481      * the iteration stops.
23482      * @param {Function} fn The function to call
23483      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23484      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23485      */
23486     eachChild : function(fn, scope, args){
23487         var cs = this.childNodes;
23488         for(var i = 0, len = cs.length; i < len; i++) {
23489                 if(fn.call(scope || this, args || cs[i]) === false){
23490                     break;
23491                 }
23492         }
23493     },
23494
23495     /**
23496      * Finds the first child that has the attribute with the specified value.
23497      * @param {String} attribute The attribute name
23498      * @param {Mixed} value The value to search for
23499      * @return {Node} The found child or null if none was found
23500      */
23501     findChild : function(attribute, value){
23502         var cs = this.childNodes;
23503         for(var i = 0, len = cs.length; i < len; i++) {
23504                 if(cs[i].attributes[attribute] == value){
23505                     return cs[i];
23506                 }
23507         }
23508         return null;
23509     },
23510
23511     /**
23512      * Finds the first child by a custom function. The child matches if the function passed
23513      * returns true.
23514      * @param {Function} fn
23515      * @param {Object} scope (optional)
23516      * @return {Node} The found child or null if none was found
23517      */
23518     findChildBy : function(fn, scope){
23519         var cs = this.childNodes;
23520         for(var i = 0, len = cs.length; i < len; i++) {
23521                 if(fn.call(scope||cs[i], cs[i]) === true){
23522                     return cs[i];
23523                 }
23524         }
23525         return null;
23526     },
23527
23528     /**
23529      * Sorts this nodes children using the supplied sort function
23530      * @param {Function} fn
23531      * @param {Object} scope (optional)
23532      */
23533     sort : function(fn, scope){
23534         var cs = this.childNodes;
23535         var len = cs.length;
23536         if(len > 0){
23537             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23538             cs.sort(sortFn);
23539             for(var i = 0; i < len; i++){
23540                 var n = cs[i];
23541                 n.previousSibling = cs[i-1];
23542                 n.nextSibling = cs[i+1];
23543                 if(i == 0){
23544                     this.setFirstChild(n);
23545                 }
23546                 if(i == len-1){
23547                     this.setLastChild(n);
23548                 }
23549             }
23550         }
23551     },
23552
23553     /**
23554      * Returns true if this node is an ancestor (at any point) of the passed node.
23555      * @param {Node} node
23556      * @return {Boolean}
23557      */
23558     contains : function(node){
23559         return node.isAncestor(this);
23560     },
23561
23562     /**
23563      * Returns true if the passed node is an ancestor (at any point) of this node.
23564      * @param {Node} node
23565      * @return {Boolean}
23566      */
23567     isAncestor : function(node){
23568         var p = this.parentNode;
23569         while(p){
23570             if(p == node){
23571                 return true;
23572             }
23573             p = p.parentNode;
23574         }
23575         return false;
23576     },
23577
23578     toString : function(){
23579         return "[Node"+(this.id?" "+this.id:"")+"]";
23580     }
23581 });/*
23582  * Based on:
23583  * Ext JS Library 1.1.1
23584  * Copyright(c) 2006-2007, Ext JS, LLC.
23585  *
23586  * Originally Released Under LGPL - original licence link has changed is not relivant.
23587  *
23588  * Fork - LGPL
23589  * <script type="text/javascript">
23590  */
23591  (function(){ 
23592 /**
23593  * @class Roo.Layer
23594  * @extends Roo.Element
23595  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23596  * automatic maintaining of shadow/shim positions.
23597  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23598  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23599  * you can pass a string with a CSS class name. False turns off the shadow.
23600  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23601  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23602  * @cfg {String} cls CSS class to add to the element
23603  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23604  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23605  * @constructor
23606  * @param {Object} config An object with config options.
23607  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23608  */
23609
23610 Roo.Layer = function(config, existingEl){
23611     config = config || {};
23612     var dh = Roo.DomHelper;
23613     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23614     if(existingEl){
23615         this.dom = Roo.getDom(existingEl);
23616     }
23617     if(!this.dom){
23618         var o = config.dh || {tag: "div", cls: "x-layer"};
23619         this.dom = dh.append(pel, o);
23620     }
23621     if(config.cls){
23622         this.addClass(config.cls);
23623     }
23624     this.constrain = config.constrain !== false;
23625     this.visibilityMode = Roo.Element.VISIBILITY;
23626     if(config.id){
23627         this.id = this.dom.id = config.id;
23628     }else{
23629         this.id = Roo.id(this.dom);
23630     }
23631     this.zindex = config.zindex || this.getZIndex();
23632     this.position("absolute", this.zindex);
23633     if(config.shadow){
23634         this.shadowOffset = config.shadowOffset || 4;
23635         this.shadow = new Roo.Shadow({
23636             offset : this.shadowOffset,
23637             mode : config.shadow
23638         });
23639     }else{
23640         this.shadowOffset = 0;
23641     }
23642     this.useShim = config.shim !== false && Roo.useShims;
23643     this.useDisplay = config.useDisplay;
23644     this.hide();
23645 };
23646
23647 var supr = Roo.Element.prototype;
23648
23649 // shims are shared among layer to keep from having 100 iframes
23650 var shims = [];
23651
23652 Roo.extend(Roo.Layer, Roo.Element, {
23653
23654     getZIndex : function(){
23655         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23656     },
23657
23658     getShim : function(){
23659         if(!this.useShim){
23660             return null;
23661         }
23662         if(this.shim){
23663             return this.shim;
23664         }
23665         var shim = shims.shift();
23666         if(!shim){
23667             shim = this.createShim();
23668             shim.enableDisplayMode('block');
23669             shim.dom.style.display = 'none';
23670             shim.dom.style.visibility = 'visible';
23671         }
23672         var pn = this.dom.parentNode;
23673         if(shim.dom.parentNode != pn){
23674             pn.insertBefore(shim.dom, this.dom);
23675         }
23676         shim.setStyle('z-index', this.getZIndex()-2);
23677         this.shim = shim;
23678         return shim;
23679     },
23680
23681     hideShim : function(){
23682         if(this.shim){
23683             this.shim.setDisplayed(false);
23684             shims.push(this.shim);
23685             delete this.shim;
23686         }
23687     },
23688
23689     disableShadow : function(){
23690         if(this.shadow){
23691             this.shadowDisabled = true;
23692             this.shadow.hide();
23693             this.lastShadowOffset = this.shadowOffset;
23694             this.shadowOffset = 0;
23695         }
23696     },
23697
23698     enableShadow : function(show){
23699         if(this.shadow){
23700             this.shadowDisabled = false;
23701             this.shadowOffset = this.lastShadowOffset;
23702             delete this.lastShadowOffset;
23703             if(show){
23704                 this.sync(true);
23705             }
23706         }
23707     },
23708
23709     // private
23710     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23711     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23712     sync : function(doShow){
23713         var sw = this.shadow;
23714         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23715             var sh = this.getShim();
23716
23717             var w = this.getWidth(),
23718                 h = this.getHeight();
23719
23720             var l = this.getLeft(true),
23721                 t = this.getTop(true);
23722
23723             if(sw && !this.shadowDisabled){
23724                 if(doShow && !sw.isVisible()){
23725                     sw.show(this);
23726                 }else{
23727                     sw.realign(l, t, w, h);
23728                 }
23729                 if(sh){
23730                     if(doShow){
23731                        sh.show();
23732                     }
23733                     // fit the shim behind the shadow, so it is shimmed too
23734                     var a = sw.adjusts, s = sh.dom.style;
23735                     s.left = (Math.min(l, l+a.l))+"px";
23736                     s.top = (Math.min(t, t+a.t))+"px";
23737                     s.width = (w+a.w)+"px";
23738                     s.height = (h+a.h)+"px";
23739                 }
23740             }else if(sh){
23741                 if(doShow){
23742                    sh.show();
23743                 }
23744                 sh.setSize(w, h);
23745                 sh.setLeftTop(l, t);
23746             }
23747             
23748         }
23749     },
23750
23751     // private
23752     destroy : function(){
23753         this.hideShim();
23754         if(this.shadow){
23755             this.shadow.hide();
23756         }
23757         this.removeAllListeners();
23758         var pn = this.dom.parentNode;
23759         if(pn){
23760             pn.removeChild(this.dom);
23761         }
23762         Roo.Element.uncache(this.id);
23763     },
23764
23765     remove : function(){
23766         this.destroy();
23767     },
23768
23769     // private
23770     beginUpdate : function(){
23771         this.updating = true;
23772     },
23773
23774     // private
23775     endUpdate : function(){
23776         this.updating = false;
23777         this.sync(true);
23778     },
23779
23780     // private
23781     hideUnders : function(negOffset){
23782         if(this.shadow){
23783             this.shadow.hide();
23784         }
23785         this.hideShim();
23786     },
23787
23788     // private
23789     constrainXY : function(){
23790         if(this.constrain){
23791             var vw = Roo.lib.Dom.getViewWidth(),
23792                 vh = Roo.lib.Dom.getViewHeight();
23793             var s = Roo.get(document).getScroll();
23794
23795             var xy = this.getXY();
23796             var x = xy[0], y = xy[1];   
23797             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23798             // only move it if it needs it
23799             var moved = false;
23800             // first validate right/bottom
23801             if((x + w) > vw+s.left){
23802                 x = vw - w - this.shadowOffset;
23803                 moved = true;
23804             }
23805             if((y + h) > vh+s.top){
23806                 y = vh - h - this.shadowOffset;
23807                 moved = true;
23808             }
23809             // then make sure top/left isn't negative
23810             if(x < s.left){
23811                 x = s.left;
23812                 moved = true;
23813             }
23814             if(y < s.top){
23815                 y = s.top;
23816                 moved = true;
23817             }
23818             if(moved){
23819                 if(this.avoidY){
23820                     var ay = this.avoidY;
23821                     if(y <= ay && (y+h) >= ay){
23822                         y = ay-h-5;   
23823                     }
23824                 }
23825                 xy = [x, y];
23826                 this.storeXY(xy);
23827                 supr.setXY.call(this, xy);
23828                 this.sync();
23829             }
23830         }
23831     },
23832
23833     isVisible : function(){
23834         return this.visible;    
23835     },
23836
23837     // private
23838     showAction : function(){
23839         this.visible = true; // track visibility to prevent getStyle calls
23840         if(this.useDisplay === true){
23841             this.setDisplayed("");
23842         }else if(this.lastXY){
23843             supr.setXY.call(this, this.lastXY);
23844         }else if(this.lastLT){
23845             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23846         }
23847     },
23848
23849     // private
23850     hideAction : function(){
23851         this.visible = false;
23852         if(this.useDisplay === true){
23853             this.setDisplayed(false);
23854         }else{
23855             this.setLeftTop(-10000,-10000);
23856         }
23857     },
23858
23859     // overridden Element method
23860     setVisible : function(v, a, d, c, e){
23861         if(v){
23862             this.showAction();
23863         }
23864         if(a && v){
23865             var cb = function(){
23866                 this.sync(true);
23867                 if(c){
23868                     c();
23869                 }
23870             }.createDelegate(this);
23871             supr.setVisible.call(this, true, true, d, cb, e);
23872         }else{
23873             if(!v){
23874                 this.hideUnders(true);
23875             }
23876             var cb = c;
23877             if(a){
23878                 cb = function(){
23879                     this.hideAction();
23880                     if(c){
23881                         c();
23882                     }
23883                 }.createDelegate(this);
23884             }
23885             supr.setVisible.call(this, v, a, d, cb, e);
23886             if(v){
23887                 this.sync(true);
23888             }else if(!a){
23889                 this.hideAction();
23890             }
23891         }
23892     },
23893
23894     storeXY : function(xy){
23895         delete this.lastLT;
23896         this.lastXY = xy;
23897     },
23898
23899     storeLeftTop : function(left, top){
23900         delete this.lastXY;
23901         this.lastLT = [left, top];
23902     },
23903
23904     // private
23905     beforeFx : function(){
23906         this.beforeAction();
23907         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23908     },
23909
23910     // private
23911     afterFx : function(){
23912         Roo.Layer.superclass.afterFx.apply(this, arguments);
23913         this.sync(this.isVisible());
23914     },
23915
23916     // private
23917     beforeAction : function(){
23918         if(!this.updating && this.shadow){
23919             this.shadow.hide();
23920         }
23921     },
23922
23923     // overridden Element method
23924     setLeft : function(left){
23925         this.storeLeftTop(left, this.getTop(true));
23926         supr.setLeft.apply(this, arguments);
23927         this.sync();
23928     },
23929
23930     setTop : function(top){
23931         this.storeLeftTop(this.getLeft(true), top);
23932         supr.setTop.apply(this, arguments);
23933         this.sync();
23934     },
23935
23936     setLeftTop : function(left, top){
23937         this.storeLeftTop(left, top);
23938         supr.setLeftTop.apply(this, arguments);
23939         this.sync();
23940     },
23941
23942     setXY : function(xy, a, d, c, e){
23943         this.fixDisplay();
23944         this.beforeAction();
23945         this.storeXY(xy);
23946         var cb = this.createCB(c);
23947         supr.setXY.call(this, xy, a, d, cb, e);
23948         if(!a){
23949             cb();
23950         }
23951     },
23952
23953     // private
23954     createCB : function(c){
23955         var el = this;
23956         return function(){
23957             el.constrainXY();
23958             el.sync(true);
23959             if(c){
23960                 c();
23961             }
23962         };
23963     },
23964
23965     // overridden Element method
23966     setX : function(x, a, d, c, e){
23967         this.setXY([x, this.getY()], a, d, c, e);
23968     },
23969
23970     // overridden Element method
23971     setY : function(y, a, d, c, e){
23972         this.setXY([this.getX(), y], a, d, c, e);
23973     },
23974
23975     // overridden Element method
23976     setSize : function(w, h, a, d, c, e){
23977         this.beforeAction();
23978         var cb = this.createCB(c);
23979         supr.setSize.call(this, w, h, a, d, cb, e);
23980         if(!a){
23981             cb();
23982         }
23983     },
23984
23985     // overridden Element method
23986     setWidth : function(w, a, d, c, e){
23987         this.beforeAction();
23988         var cb = this.createCB(c);
23989         supr.setWidth.call(this, w, a, d, cb, e);
23990         if(!a){
23991             cb();
23992         }
23993     },
23994
23995     // overridden Element method
23996     setHeight : function(h, a, d, c, e){
23997         this.beforeAction();
23998         var cb = this.createCB(c);
23999         supr.setHeight.call(this, h, a, d, cb, e);
24000         if(!a){
24001             cb();
24002         }
24003     },
24004
24005     // overridden Element method
24006     setBounds : function(x, y, w, h, a, d, c, e){
24007         this.beforeAction();
24008         var cb = this.createCB(c);
24009         if(!a){
24010             this.storeXY([x, y]);
24011             supr.setXY.call(this, [x, y]);
24012             supr.setSize.call(this, w, h, a, d, cb, e);
24013             cb();
24014         }else{
24015             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24016         }
24017         return this;
24018     },
24019     
24020     /**
24021      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24022      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24023      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24024      * @param {Number} zindex The new z-index to set
24025      * @return {this} The Layer
24026      */
24027     setZIndex : function(zindex){
24028         this.zindex = zindex;
24029         this.setStyle("z-index", zindex + 2);
24030         if(this.shadow){
24031             this.shadow.setZIndex(zindex + 1);
24032         }
24033         if(this.shim){
24034             this.shim.setStyle("z-index", zindex);
24035         }
24036     }
24037 });
24038 })();/*
24039  * Based on:
24040  * Ext JS Library 1.1.1
24041  * Copyright(c) 2006-2007, Ext JS, LLC.
24042  *
24043  * Originally Released Under LGPL - original licence link has changed is not relivant.
24044  *
24045  * Fork - LGPL
24046  * <script type="text/javascript">
24047  */
24048
24049
24050 /**
24051  * @class Roo.Shadow
24052  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24053  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24054  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24055  * @constructor
24056  * Create a new Shadow
24057  * @param {Object} config The config object
24058  */
24059 Roo.Shadow = function(config){
24060     Roo.apply(this, config);
24061     if(typeof this.mode != "string"){
24062         this.mode = this.defaultMode;
24063     }
24064     var o = this.offset, a = {h: 0};
24065     var rad = Math.floor(this.offset/2);
24066     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24067         case "drop":
24068             a.w = 0;
24069             a.l = a.t = o;
24070             a.t -= 1;
24071             if(Roo.isIE){
24072                 a.l -= this.offset + rad;
24073                 a.t -= this.offset + rad;
24074                 a.w -= rad;
24075                 a.h -= rad;
24076                 a.t += 1;
24077             }
24078         break;
24079         case "sides":
24080             a.w = (o*2);
24081             a.l = -o;
24082             a.t = o-1;
24083             if(Roo.isIE){
24084                 a.l -= (this.offset - rad);
24085                 a.t -= this.offset + rad;
24086                 a.l += 1;
24087                 a.w -= (this.offset - rad)*2;
24088                 a.w -= rad + 1;
24089                 a.h -= 1;
24090             }
24091         break;
24092         case "frame":
24093             a.w = a.h = (o*2);
24094             a.l = a.t = -o;
24095             a.t += 1;
24096             a.h -= 2;
24097             if(Roo.isIE){
24098                 a.l -= (this.offset - rad);
24099                 a.t -= (this.offset - rad);
24100                 a.l += 1;
24101                 a.w -= (this.offset + rad + 1);
24102                 a.h -= (this.offset + rad);
24103                 a.h += 1;
24104             }
24105         break;
24106     };
24107
24108     this.adjusts = a;
24109 };
24110
24111 Roo.Shadow.prototype = {
24112     /**
24113      * @cfg {String} mode
24114      * The shadow display mode.  Supports the following options:<br />
24115      * sides: Shadow displays on both sides and bottom only<br />
24116      * frame: Shadow displays equally on all four sides<br />
24117      * drop: Traditional bottom-right drop shadow (default)
24118      */
24119     /**
24120      * @cfg {String} offset
24121      * The number of pixels to offset the shadow from the element (defaults to 4)
24122      */
24123     offset: 4,
24124
24125     // private
24126     defaultMode: "drop",
24127
24128     /**
24129      * Displays the shadow under the target element
24130      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24131      */
24132     show : function(target){
24133         target = Roo.get(target);
24134         if(!this.el){
24135             this.el = Roo.Shadow.Pool.pull();
24136             if(this.el.dom.nextSibling != target.dom){
24137                 this.el.insertBefore(target);
24138             }
24139         }
24140         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24141         if(Roo.isIE){
24142             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24143         }
24144         this.realign(
24145             target.getLeft(true),
24146             target.getTop(true),
24147             target.getWidth(),
24148             target.getHeight()
24149         );
24150         this.el.dom.style.display = "block";
24151     },
24152
24153     /**
24154      * Returns true if the shadow is visible, else false
24155      */
24156     isVisible : function(){
24157         return this.el ? true : false;  
24158     },
24159
24160     /**
24161      * Direct alignment when values are already available. Show must be called at least once before
24162      * calling this method to ensure it is initialized.
24163      * @param {Number} left The target element left position
24164      * @param {Number} top The target element top position
24165      * @param {Number} width The target element width
24166      * @param {Number} height The target element height
24167      */
24168     realign : function(l, t, w, h){
24169         if(!this.el){
24170             return;
24171         }
24172         var a = this.adjusts, d = this.el.dom, s = d.style;
24173         var iea = 0;
24174         s.left = (l+a.l)+"px";
24175         s.top = (t+a.t)+"px";
24176         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24177  
24178         if(s.width != sws || s.height != shs){
24179             s.width = sws;
24180             s.height = shs;
24181             if(!Roo.isIE){
24182                 var cn = d.childNodes;
24183                 var sww = Math.max(0, (sw-12))+"px";
24184                 cn[0].childNodes[1].style.width = sww;
24185                 cn[1].childNodes[1].style.width = sww;
24186                 cn[2].childNodes[1].style.width = sww;
24187                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24188             }
24189         }
24190     },
24191
24192     /**
24193      * Hides this shadow
24194      */
24195     hide : function(){
24196         if(this.el){
24197             this.el.dom.style.display = "none";
24198             Roo.Shadow.Pool.push(this.el);
24199             delete this.el;
24200         }
24201     },
24202
24203     /**
24204      * Adjust the z-index of this shadow
24205      * @param {Number} zindex The new z-index
24206      */
24207     setZIndex : function(z){
24208         this.zIndex = z;
24209         if(this.el){
24210             this.el.setStyle("z-index", z);
24211         }
24212     }
24213 };
24214
24215 // Private utility class that manages the internal Shadow cache
24216 Roo.Shadow.Pool = function(){
24217     var p = [];
24218     var markup = Roo.isIE ?
24219                  '<div class="x-ie-shadow"></div>' :
24220                  '<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>';
24221     return {
24222         pull : function(){
24223             var sh = p.shift();
24224             if(!sh){
24225                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24226                 sh.autoBoxAdjust = false;
24227             }
24228             return sh;
24229         },
24230
24231         push : function(sh){
24232             p.push(sh);
24233         }
24234     };
24235 }();/*
24236  * Based on:
24237  * Ext JS Library 1.1.1
24238  * Copyright(c) 2006-2007, Ext JS, LLC.
24239  *
24240  * Originally Released Under LGPL - original licence link has changed is not relivant.
24241  *
24242  * Fork - LGPL
24243  * <script type="text/javascript">
24244  */
24245
24246
24247 /**
24248  * @class Roo.SplitBar
24249  * @extends Roo.util.Observable
24250  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24251  * <br><br>
24252  * Usage:
24253  * <pre><code>
24254 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24255                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24256 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24257 split.minSize = 100;
24258 split.maxSize = 600;
24259 split.animate = true;
24260 split.on('moved', splitterMoved);
24261 </code></pre>
24262  * @constructor
24263  * Create a new SplitBar
24264  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24265  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24266  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24267  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24268                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24269                         position of the SplitBar).
24270  */
24271 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24272     
24273     /** @private */
24274     this.el = Roo.get(dragElement, true);
24275     this.el.dom.unselectable = "on";
24276     /** @private */
24277     this.resizingEl = Roo.get(resizingElement, true);
24278
24279     /**
24280      * @private
24281      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24282      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24283      * @type Number
24284      */
24285     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24286     
24287     /**
24288      * The minimum size of the resizing element. (Defaults to 0)
24289      * @type Number
24290      */
24291     this.minSize = 0;
24292     
24293     /**
24294      * The maximum size of the resizing element. (Defaults to 2000)
24295      * @type Number
24296      */
24297     this.maxSize = 2000;
24298     
24299     /**
24300      * Whether to animate the transition to the new size
24301      * @type Boolean
24302      */
24303     this.animate = false;
24304     
24305     /**
24306      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24307      * @type Boolean
24308      */
24309     this.useShim = false;
24310     
24311     /** @private */
24312     this.shim = null;
24313     
24314     if(!existingProxy){
24315         /** @private */
24316         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24317     }else{
24318         this.proxy = Roo.get(existingProxy).dom;
24319     }
24320     /** @private */
24321     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24322     
24323     /** @private */
24324     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24325     
24326     /** @private */
24327     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24328     
24329     /** @private */
24330     this.dragSpecs = {};
24331     
24332     /**
24333      * @private The adapter to use to positon and resize elements
24334      */
24335     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24336     this.adapter.init(this);
24337     
24338     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24339         /** @private */
24340         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24341         this.el.addClass("x-splitbar-h");
24342     }else{
24343         /** @private */
24344         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24345         this.el.addClass("x-splitbar-v");
24346     }
24347     
24348     this.addEvents({
24349         /**
24350          * @event resize
24351          * Fires when the splitter is moved (alias for {@link #event-moved})
24352          * @param {Roo.SplitBar} this
24353          * @param {Number} newSize the new width or height
24354          */
24355         "resize" : true,
24356         /**
24357          * @event moved
24358          * Fires when the splitter is moved
24359          * @param {Roo.SplitBar} this
24360          * @param {Number} newSize the new width or height
24361          */
24362         "moved" : true,
24363         /**
24364          * @event beforeresize
24365          * Fires before the splitter is dragged
24366          * @param {Roo.SplitBar} this
24367          */
24368         "beforeresize" : true,
24369
24370         "beforeapply" : true
24371     });
24372
24373     Roo.util.Observable.call(this);
24374 };
24375
24376 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24377     onStartProxyDrag : function(x, y){
24378         this.fireEvent("beforeresize", this);
24379         if(!this.overlay){
24380             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24381             o.unselectable();
24382             o.enableDisplayMode("block");
24383             // all splitbars share the same overlay
24384             Roo.SplitBar.prototype.overlay = o;
24385         }
24386         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24387         this.overlay.show();
24388         Roo.get(this.proxy).setDisplayed("block");
24389         var size = this.adapter.getElementSize(this);
24390         this.activeMinSize = this.getMinimumSize();;
24391         this.activeMaxSize = this.getMaximumSize();;
24392         var c1 = size - this.activeMinSize;
24393         var c2 = Math.max(this.activeMaxSize - size, 0);
24394         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24395             this.dd.resetConstraints();
24396             this.dd.setXConstraint(
24397                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24398                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24399             );
24400             this.dd.setYConstraint(0, 0);
24401         }else{
24402             this.dd.resetConstraints();
24403             this.dd.setXConstraint(0, 0);
24404             this.dd.setYConstraint(
24405                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24406                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24407             );
24408          }
24409         this.dragSpecs.startSize = size;
24410         this.dragSpecs.startPoint = [x, y];
24411         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24412     },
24413     
24414     /** 
24415      * @private Called after the drag operation by the DDProxy
24416      */
24417     onEndProxyDrag : function(e){
24418         Roo.get(this.proxy).setDisplayed(false);
24419         var endPoint = Roo.lib.Event.getXY(e);
24420         if(this.overlay){
24421             this.overlay.hide();
24422         }
24423         var newSize;
24424         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24425             newSize = this.dragSpecs.startSize + 
24426                 (this.placement == Roo.SplitBar.LEFT ?
24427                     endPoint[0] - this.dragSpecs.startPoint[0] :
24428                     this.dragSpecs.startPoint[0] - endPoint[0]
24429                 );
24430         }else{
24431             newSize = this.dragSpecs.startSize + 
24432                 (this.placement == Roo.SplitBar.TOP ?
24433                     endPoint[1] - this.dragSpecs.startPoint[1] :
24434                     this.dragSpecs.startPoint[1] - endPoint[1]
24435                 );
24436         }
24437         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24438         if(newSize != this.dragSpecs.startSize){
24439             if(this.fireEvent('beforeapply', this, newSize) !== false){
24440                 this.adapter.setElementSize(this, newSize);
24441                 this.fireEvent("moved", this, newSize);
24442                 this.fireEvent("resize", this, newSize);
24443             }
24444         }
24445     },
24446     
24447     /**
24448      * Get the adapter this SplitBar uses
24449      * @return The adapter object
24450      */
24451     getAdapter : function(){
24452         return this.adapter;
24453     },
24454     
24455     /**
24456      * Set the adapter this SplitBar uses
24457      * @param {Object} adapter A SplitBar adapter object
24458      */
24459     setAdapter : function(adapter){
24460         this.adapter = adapter;
24461         this.adapter.init(this);
24462     },
24463     
24464     /**
24465      * Gets the minimum size for the resizing element
24466      * @return {Number} The minimum size
24467      */
24468     getMinimumSize : function(){
24469         return this.minSize;
24470     },
24471     
24472     /**
24473      * Sets the minimum size for the resizing element
24474      * @param {Number} minSize The minimum size
24475      */
24476     setMinimumSize : function(minSize){
24477         this.minSize = minSize;
24478     },
24479     
24480     /**
24481      * Gets the maximum size for the resizing element
24482      * @return {Number} The maximum size
24483      */
24484     getMaximumSize : function(){
24485         return this.maxSize;
24486     },
24487     
24488     /**
24489      * Sets the maximum size for the resizing element
24490      * @param {Number} maxSize The maximum size
24491      */
24492     setMaximumSize : function(maxSize){
24493         this.maxSize = maxSize;
24494     },
24495     
24496     /**
24497      * Sets the initialize size for the resizing element
24498      * @param {Number} size The initial size
24499      */
24500     setCurrentSize : function(size){
24501         var oldAnimate = this.animate;
24502         this.animate = false;
24503         this.adapter.setElementSize(this, size);
24504         this.animate = oldAnimate;
24505     },
24506     
24507     /**
24508      * Destroy this splitbar. 
24509      * @param {Boolean} removeEl True to remove the element
24510      */
24511     destroy : function(removeEl){
24512         if(this.shim){
24513             this.shim.remove();
24514         }
24515         this.dd.unreg();
24516         this.proxy.parentNode.removeChild(this.proxy);
24517         if(removeEl){
24518             this.el.remove();
24519         }
24520     }
24521 });
24522
24523 /**
24524  * @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.
24525  */
24526 Roo.SplitBar.createProxy = function(dir){
24527     var proxy = new Roo.Element(document.createElement("div"));
24528     proxy.unselectable();
24529     var cls = 'x-splitbar-proxy';
24530     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24531     document.body.appendChild(proxy.dom);
24532     return proxy.dom;
24533 };
24534
24535 /** 
24536  * @class Roo.SplitBar.BasicLayoutAdapter
24537  * Default Adapter. It assumes the splitter and resizing element are not positioned
24538  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24539  */
24540 Roo.SplitBar.BasicLayoutAdapter = function(){
24541 };
24542
24543 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24544     // do nothing for now
24545     init : function(s){
24546     
24547     },
24548     /**
24549      * Called before drag operations to get the current size of the resizing element. 
24550      * @param {Roo.SplitBar} s The SplitBar using this adapter
24551      */
24552      getElementSize : function(s){
24553         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24554             return s.resizingEl.getWidth();
24555         }else{
24556             return s.resizingEl.getHeight();
24557         }
24558     },
24559     
24560     /**
24561      * Called after drag operations to set the size of the resizing element.
24562      * @param {Roo.SplitBar} s The SplitBar using this adapter
24563      * @param {Number} newSize The new size to set
24564      * @param {Function} onComplete A function to be invoked when resizing is complete
24565      */
24566     setElementSize : function(s, newSize, onComplete){
24567         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24568             if(!s.animate){
24569                 s.resizingEl.setWidth(newSize);
24570                 if(onComplete){
24571                     onComplete(s, newSize);
24572                 }
24573             }else{
24574                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24575             }
24576         }else{
24577             
24578             if(!s.animate){
24579                 s.resizingEl.setHeight(newSize);
24580                 if(onComplete){
24581                     onComplete(s, newSize);
24582                 }
24583             }else{
24584                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24585             }
24586         }
24587     }
24588 };
24589
24590 /** 
24591  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24592  * @extends Roo.SplitBar.BasicLayoutAdapter
24593  * Adapter that  moves the splitter element to align with the resized sizing element. 
24594  * Used with an absolute positioned SplitBar.
24595  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24596  * document.body, make sure you assign an id to the body element.
24597  */
24598 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24599     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24600     this.container = Roo.get(container);
24601 };
24602
24603 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24604     init : function(s){
24605         this.basic.init(s);
24606     },
24607     
24608     getElementSize : function(s){
24609         return this.basic.getElementSize(s);
24610     },
24611     
24612     setElementSize : function(s, newSize, onComplete){
24613         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24614     },
24615     
24616     moveSplitter : function(s){
24617         var yes = Roo.SplitBar;
24618         switch(s.placement){
24619             case yes.LEFT:
24620                 s.el.setX(s.resizingEl.getRight());
24621                 break;
24622             case yes.RIGHT:
24623                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24624                 break;
24625             case yes.TOP:
24626                 s.el.setY(s.resizingEl.getBottom());
24627                 break;
24628             case yes.BOTTOM:
24629                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24630                 break;
24631         }
24632     }
24633 };
24634
24635 /**
24636  * Orientation constant - Create a vertical SplitBar
24637  * @static
24638  * @type Number
24639  */
24640 Roo.SplitBar.VERTICAL = 1;
24641
24642 /**
24643  * Orientation constant - Create a horizontal SplitBar
24644  * @static
24645  * @type Number
24646  */
24647 Roo.SplitBar.HORIZONTAL = 2;
24648
24649 /**
24650  * Placement constant - The resizing element is to the left of the splitter element
24651  * @static
24652  * @type Number
24653  */
24654 Roo.SplitBar.LEFT = 1;
24655
24656 /**
24657  * Placement constant - The resizing element is to the right of the splitter element
24658  * @static
24659  * @type Number
24660  */
24661 Roo.SplitBar.RIGHT = 2;
24662
24663 /**
24664  * Placement constant - The resizing element is positioned above the splitter element
24665  * @static
24666  * @type Number
24667  */
24668 Roo.SplitBar.TOP = 3;
24669
24670 /**
24671  * Placement constant - The resizing element is positioned under splitter element
24672  * @static
24673  * @type Number
24674  */
24675 Roo.SplitBar.BOTTOM = 4;
24676 /*
24677  * Based on:
24678  * Ext JS Library 1.1.1
24679  * Copyright(c) 2006-2007, Ext JS, LLC.
24680  *
24681  * Originally Released Under LGPL - original licence link has changed is not relivant.
24682  *
24683  * Fork - LGPL
24684  * <script type="text/javascript">
24685  */
24686
24687 /**
24688  * @class Roo.View
24689  * @extends Roo.util.Observable
24690  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24691  * This class also supports single and multi selection modes. <br>
24692  * Create a data model bound view:
24693  <pre><code>
24694  var store = new Roo.data.Store(...);
24695
24696  var view = new Roo.View({
24697     el : "my-element",
24698     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24699  
24700     singleSelect: true,
24701     selectedClass: "ydataview-selected",
24702     store: store
24703  });
24704
24705  // listen for node click?
24706  view.on("click", function(vw, index, node, e){
24707  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24708  });
24709
24710  // load XML data
24711  dataModel.load("foobar.xml");
24712  </code></pre>
24713  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24714  * <br><br>
24715  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24716  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24717  * 
24718  * Note: old style constructor is still suported (container, template, config)
24719  * 
24720  * @constructor
24721  * Create a new View
24722  * @param {Object} config The config object
24723  * 
24724  */
24725 Roo.View = function(config, depreciated_tpl, depreciated_config){
24726     
24727     if (typeof(depreciated_tpl) == 'undefined') {
24728         // new way.. - universal constructor.
24729         Roo.apply(this, config);
24730         this.el  = Roo.get(this.el);
24731     } else {
24732         // old format..
24733         this.el  = Roo.get(config);
24734         this.tpl = depreciated_tpl;
24735         Roo.apply(this, depreciated_config);
24736     }
24737     this.wrapEl  = this.el.wrap().wrap();
24738     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24739     
24740     
24741     if(typeof(this.tpl) == "string"){
24742         this.tpl = new Roo.Template(this.tpl);
24743     } else {
24744         // support xtype ctors..
24745         this.tpl = new Roo.factory(this.tpl, Roo);
24746     }
24747     
24748     
24749     this.tpl.compile();
24750    
24751   
24752     
24753      
24754     /** @private */
24755     this.addEvents({
24756         /**
24757          * @event beforeclick
24758          * Fires before a click is processed. Returns false to cancel the default action.
24759          * @param {Roo.View} this
24760          * @param {Number} index The index of the target node
24761          * @param {HTMLElement} node The target node
24762          * @param {Roo.EventObject} e The raw event object
24763          */
24764             "beforeclick" : true,
24765         /**
24766          * @event click
24767          * Fires when a template node is clicked.
24768          * @param {Roo.View} this
24769          * @param {Number} index The index of the target node
24770          * @param {HTMLElement} node The target node
24771          * @param {Roo.EventObject} e The raw event object
24772          */
24773             "click" : true,
24774         /**
24775          * @event dblclick
24776          * Fires when a template node is double clicked.
24777          * @param {Roo.View} this
24778          * @param {Number} index The index of the target node
24779          * @param {HTMLElement} node The target node
24780          * @param {Roo.EventObject} e The raw event object
24781          */
24782             "dblclick" : true,
24783         /**
24784          * @event contextmenu
24785          * Fires when a template node is right clicked.
24786          * @param {Roo.View} this
24787          * @param {Number} index The index of the target node
24788          * @param {HTMLElement} node The target node
24789          * @param {Roo.EventObject} e The raw event object
24790          */
24791             "contextmenu" : true,
24792         /**
24793          * @event selectionchange
24794          * Fires when the selected nodes change.
24795          * @param {Roo.View} this
24796          * @param {Array} selections Array of the selected nodes
24797          */
24798             "selectionchange" : true,
24799     
24800         /**
24801          * @event beforeselect
24802          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24803          * @param {Roo.View} this
24804          * @param {HTMLElement} node The node to be selected
24805          * @param {Array} selections Array of currently selected nodes
24806          */
24807             "beforeselect" : true,
24808         /**
24809          * @event preparedata
24810          * Fires on every row to render, to allow you to change the data.
24811          * @param {Roo.View} this
24812          * @param {Object} data to be rendered (change this)
24813          */
24814           "preparedata" : true
24815           
24816           
24817         });
24818
24819
24820
24821     this.el.on({
24822         "click": this.onClick,
24823         "dblclick": this.onDblClick,
24824         "contextmenu": this.onContextMenu,
24825         scope:this
24826     });
24827
24828     this.selections = [];
24829     this.nodes = [];
24830     this.cmp = new Roo.CompositeElementLite([]);
24831     if(this.store){
24832         this.store = Roo.factory(this.store, Roo.data);
24833         this.setStore(this.store, true);
24834     }
24835     
24836     if ( this.footer && this.footer.xtype) {
24837            
24838          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24839         
24840         this.footer.dataSource = this.store
24841         this.footer.container = fctr;
24842         this.footer = Roo.factory(this.footer, Roo);
24843         fctr.insertFirst(this.el);
24844         
24845         // this is a bit insane - as the paging toolbar seems to detach the el..
24846 //        dom.parentNode.parentNode.parentNode
24847          // they get detached?
24848     }
24849     
24850     
24851     Roo.View.superclass.constructor.call(this);
24852     
24853     
24854 };
24855
24856 Roo.extend(Roo.View, Roo.util.Observable, {
24857     
24858      /**
24859      * @cfg {Roo.data.Store} store Data store to load data from.
24860      */
24861     store : false,
24862     
24863     /**
24864      * @cfg {String|Roo.Element} el The container element.
24865      */
24866     el : '',
24867     
24868     /**
24869      * @cfg {String|Roo.Template} tpl The template used by this View 
24870      */
24871     tpl : false,
24872     /**
24873      * @cfg {String} dataName the named area of the template to use as the data area
24874      *                          Works with domtemplates roo-name="name"
24875      */
24876     dataName: false,
24877     /**
24878      * @cfg {String} selectedClass The css class to add to selected nodes
24879      */
24880     selectedClass : "x-view-selected",
24881      /**
24882      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24883      */
24884     emptyText : "",
24885     
24886     /**
24887      * @cfg {String} text to display on mask (default Loading)
24888      */
24889     mask : false,
24890     /**
24891      * @cfg {Boolean} multiSelect Allow multiple selection
24892      */
24893     multiSelect : false,
24894     /**
24895      * @cfg {Boolean} singleSelect Allow single selection
24896      */
24897     singleSelect:  false,
24898     
24899     /**
24900      * @cfg {Boolean} toggleSelect - selecting 
24901      */
24902     toggleSelect : false,
24903     
24904     /**
24905      * Returns the element this view is bound to.
24906      * @return {Roo.Element}
24907      */
24908     getEl : function(){
24909         return this.wrapEl;
24910     },
24911     
24912     
24913
24914     /**
24915      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24916      */
24917     refresh : function(){
24918         var t = this.tpl;
24919         
24920         // if we are using something like 'domtemplate', then
24921         // the what gets used is:
24922         // t.applySubtemplate(NAME, data, wrapping data..)
24923         // the outer template then get' applied with
24924         //     the store 'extra data'
24925         // and the body get's added to the
24926         //      roo-name="data" node?
24927         //      <span class='roo-tpl-{name}'></span> ?????
24928         
24929         
24930         
24931         this.clearSelections();
24932         this.el.update("");
24933         var html = [];
24934         var records = this.store.getRange();
24935         if(records.length < 1) {
24936             
24937             // is this valid??  = should it render a template??
24938             
24939             this.el.update(this.emptyText);
24940             return;
24941         }
24942         var el = this.el;
24943         if (this.dataName) {
24944             this.el.update(t.apply(this.store.meta)); //????
24945             el = this.el.child('.roo-tpl-' + this.dataName);
24946         }
24947         
24948         for(var i = 0, len = records.length; i < len; i++){
24949             var data = this.prepareData(records[i].data, i, records[i]);
24950             this.fireEvent("preparedata", this, data, i, records[i]);
24951             html[html.length] = Roo.util.Format.trim(
24952                 this.dataName ?
24953                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24954                     t.apply(data)
24955             );
24956         }
24957         
24958         
24959         
24960         el.update(html.join(""));
24961         this.nodes = el.dom.childNodes;
24962         this.updateIndexes(0);
24963     },
24964
24965     /**
24966      * Function to override to reformat the data that is sent to
24967      * the template for each node.
24968      * DEPRICATED - use the preparedata event handler.
24969      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24970      * a JSON object for an UpdateManager bound view).
24971      */
24972     prepareData : function(data, index, record)
24973     {
24974         this.fireEvent("preparedata", this, data, index, record);
24975         return data;
24976     },
24977
24978     onUpdate : function(ds, record){
24979         this.clearSelections();
24980         var index = this.store.indexOf(record);
24981         var n = this.nodes[index];
24982         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24983         n.parentNode.removeChild(n);
24984         this.updateIndexes(index, index);
24985     },
24986
24987     
24988     
24989 // --------- FIXME     
24990     onAdd : function(ds, records, index)
24991     {
24992         this.clearSelections();
24993         if(this.nodes.length == 0){
24994             this.refresh();
24995             return;
24996         }
24997         var n = this.nodes[index];
24998         for(var i = 0, len = records.length; i < len; i++){
24999             var d = this.prepareData(records[i].data, i, records[i]);
25000             if(n){
25001                 this.tpl.insertBefore(n, d);
25002             }else{
25003                 
25004                 this.tpl.append(this.el, d);
25005             }
25006         }
25007         this.updateIndexes(index);
25008     },
25009
25010     onRemove : function(ds, record, index){
25011         this.clearSelections();
25012         var el = this.dataName  ?
25013             this.el.child('.roo-tpl-' + this.dataName) :
25014             this.el; 
25015         el.dom.removeChild(this.nodes[index]);
25016         this.updateIndexes(index);
25017     },
25018
25019     /**
25020      * Refresh an individual node.
25021      * @param {Number} index
25022      */
25023     refreshNode : function(index){
25024         this.onUpdate(this.store, this.store.getAt(index));
25025     },
25026
25027     updateIndexes : function(startIndex, endIndex){
25028         var ns = this.nodes;
25029         startIndex = startIndex || 0;
25030         endIndex = endIndex || ns.length - 1;
25031         for(var i = startIndex; i <= endIndex; i++){
25032             ns[i].nodeIndex = i;
25033         }
25034     },
25035
25036     /**
25037      * Changes the data store this view uses and refresh the view.
25038      * @param {Store} store
25039      */
25040     setStore : function(store, initial){
25041         if(!initial && this.store){
25042             this.store.un("datachanged", this.refresh);
25043             this.store.un("add", this.onAdd);
25044             this.store.un("remove", this.onRemove);
25045             this.store.un("update", this.onUpdate);
25046             this.store.un("clear", this.refresh);
25047             this.store.un("beforeload", this.onBeforeLoad);
25048             this.store.un("load", this.onLoad);
25049             this.store.un("loadexception", this.onLoad);
25050         }
25051         if(store){
25052           
25053             store.on("datachanged", this.refresh, this);
25054             store.on("add", this.onAdd, this);
25055             store.on("remove", this.onRemove, this);
25056             store.on("update", this.onUpdate, this);
25057             store.on("clear", this.refresh, this);
25058             store.on("beforeload", this.onBeforeLoad, this);
25059             store.on("load", this.onLoad, this);
25060             store.on("loadexception", this.onLoad, this);
25061         }
25062         
25063         if(store){
25064             this.refresh();
25065         }
25066     },
25067     /**
25068      * onbeforeLoad - masks the loading area.
25069      *
25070      */
25071     onBeforeLoad : function()
25072     {
25073         this.el.update("");
25074         this.el.mask(this.mask ? this.mask : "Loading" ); 
25075     },
25076     onLoad : function ()
25077     {
25078         this.el.unmask();
25079     },
25080     
25081
25082     /**
25083      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25084      * @param {HTMLElement} node
25085      * @return {HTMLElement} The template node
25086      */
25087     findItemFromChild : function(node){
25088         var el = this.dataName  ?
25089             this.el.child('.roo-tpl-' + this.dataName,true) :
25090             this.el.dom; 
25091         
25092         if(!node || node.parentNode == el){
25093                     return node;
25094             }
25095             var p = node.parentNode;
25096             while(p && p != el){
25097             if(p.parentNode == el){
25098                 return p;
25099             }
25100             p = p.parentNode;
25101         }
25102             return null;
25103     },
25104
25105     /** @ignore */
25106     onClick : function(e){
25107         var item = this.findItemFromChild(e.getTarget());
25108         if(item){
25109             var index = this.indexOf(item);
25110             if(this.onItemClick(item, index, e) !== false){
25111                 this.fireEvent("click", this, index, item, e);
25112             }
25113         }else{
25114             this.clearSelections();
25115         }
25116     },
25117
25118     /** @ignore */
25119     onContextMenu : function(e){
25120         var item = this.findItemFromChild(e.getTarget());
25121         if(item){
25122             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25123         }
25124     },
25125
25126     /** @ignore */
25127     onDblClick : function(e){
25128         var item = this.findItemFromChild(e.getTarget());
25129         if(item){
25130             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25131         }
25132     },
25133
25134     onItemClick : function(item, index, e)
25135     {
25136         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25137             return false;
25138         }
25139         if (this.toggleSelect) {
25140             var m = this.isSelected(item) ? 'unselect' : 'select';
25141             Roo.log(m);
25142             var _t = this;
25143             _t[m](item, true, false);
25144             return true;
25145         }
25146         if(this.multiSelect || this.singleSelect){
25147             if(this.multiSelect && e.shiftKey && this.lastSelection){
25148                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25149             }else{
25150                 this.select(item, this.multiSelect && e.ctrlKey);
25151                 this.lastSelection = item;
25152             }
25153             e.preventDefault();
25154         }
25155         return true;
25156     },
25157
25158     /**
25159      * Get the number of selected nodes.
25160      * @return {Number}
25161      */
25162     getSelectionCount : function(){
25163         return this.selections.length;
25164     },
25165
25166     /**
25167      * Get the currently selected nodes.
25168      * @return {Array} An array of HTMLElements
25169      */
25170     getSelectedNodes : function(){
25171         return this.selections;
25172     },
25173
25174     /**
25175      * Get the indexes of the selected nodes.
25176      * @return {Array}
25177      */
25178     getSelectedIndexes : function(){
25179         var indexes = [], s = this.selections;
25180         for(var i = 0, len = s.length; i < len; i++){
25181             indexes.push(s[i].nodeIndex);
25182         }
25183         return indexes;
25184     },
25185
25186     /**
25187      * Clear all selections
25188      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25189      */
25190     clearSelections : function(suppressEvent){
25191         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25192             this.cmp.elements = this.selections;
25193             this.cmp.removeClass(this.selectedClass);
25194             this.selections = [];
25195             if(!suppressEvent){
25196                 this.fireEvent("selectionchange", this, this.selections);
25197             }
25198         }
25199     },
25200
25201     /**
25202      * Returns true if the passed node is selected
25203      * @param {HTMLElement/Number} node The node or node index
25204      * @return {Boolean}
25205      */
25206     isSelected : function(node){
25207         var s = this.selections;
25208         if(s.length < 1){
25209             return false;
25210         }
25211         node = this.getNode(node);
25212         return s.indexOf(node) !== -1;
25213     },
25214
25215     /**
25216      * Selects nodes.
25217      * @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
25218      * @param {Boolean} keepExisting (optional) true to keep existing selections
25219      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25220      */
25221     select : function(nodeInfo, keepExisting, suppressEvent){
25222         if(nodeInfo instanceof Array){
25223             if(!keepExisting){
25224                 this.clearSelections(true);
25225             }
25226             for(var i = 0, len = nodeInfo.length; i < len; i++){
25227                 this.select(nodeInfo[i], true, true);
25228             }
25229             return;
25230         } 
25231         var node = this.getNode(nodeInfo);
25232         if(!node || this.isSelected(node)){
25233             return; // already selected.
25234         }
25235         if(!keepExisting){
25236             this.clearSelections(true);
25237         }
25238         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25239             Roo.fly(node).addClass(this.selectedClass);
25240             this.selections.push(node);
25241             if(!suppressEvent){
25242                 this.fireEvent("selectionchange", this, this.selections);
25243             }
25244         }
25245         
25246         
25247     },
25248       /**
25249      * Unselects nodes.
25250      * @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
25251      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25252      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25253      */
25254     unselect : function(nodeInfo, keepExisting, suppressEvent)
25255     {
25256         if(nodeInfo instanceof Array){
25257             Roo.each(this.selections, function(s) {
25258                 this.unselect(s, nodeInfo);
25259             }, this);
25260             return;
25261         }
25262         var node = this.getNode(nodeInfo);
25263         if(!node || !this.isSelected(node)){
25264             Roo.log("not selected");
25265             return; // not selected.
25266         }
25267         // fireevent???
25268         var ns = [];
25269         Roo.each(this.selections, function(s) {
25270             if (s == node ) {
25271                 Roo.fly(node).removeClass(this.selectedClass);
25272
25273                 return;
25274             }
25275             ns.push(s);
25276         },this);
25277         
25278         this.selections= ns;
25279         this.fireEvent("selectionchange", this, this.selections);
25280     },
25281
25282     /**
25283      * Gets a template node.
25284      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25285      * @return {HTMLElement} The node or null if it wasn't found
25286      */
25287     getNode : function(nodeInfo){
25288         if(typeof nodeInfo == "string"){
25289             return document.getElementById(nodeInfo);
25290         }else if(typeof nodeInfo == "number"){
25291             return this.nodes[nodeInfo];
25292         }
25293         return nodeInfo;
25294     },
25295
25296     /**
25297      * Gets a range template nodes.
25298      * @param {Number} startIndex
25299      * @param {Number} endIndex
25300      * @return {Array} An array of nodes
25301      */
25302     getNodes : function(start, end){
25303         var ns = this.nodes;
25304         start = start || 0;
25305         end = typeof end == "undefined" ? ns.length - 1 : end;
25306         var nodes = [];
25307         if(start <= end){
25308             for(var i = start; i <= end; i++){
25309                 nodes.push(ns[i]);
25310             }
25311         } else{
25312             for(var i = start; i >= end; i--){
25313                 nodes.push(ns[i]);
25314             }
25315         }
25316         return nodes;
25317     },
25318
25319     /**
25320      * Finds the index of the passed node
25321      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25322      * @return {Number} The index of the node or -1
25323      */
25324     indexOf : function(node){
25325         node = this.getNode(node);
25326         if(typeof node.nodeIndex == "number"){
25327             return node.nodeIndex;
25328         }
25329         var ns = this.nodes;
25330         for(var i = 0, len = ns.length; i < len; i++){
25331             if(ns[i] == node){
25332                 return i;
25333             }
25334         }
25335         return -1;
25336     }
25337 });
25338 /*
25339  * Based on:
25340  * Ext JS Library 1.1.1
25341  * Copyright(c) 2006-2007, Ext JS, LLC.
25342  *
25343  * Originally Released Under LGPL - original licence link has changed is not relivant.
25344  *
25345  * Fork - LGPL
25346  * <script type="text/javascript">
25347  */
25348
25349 /**
25350  * @class Roo.JsonView
25351  * @extends Roo.View
25352  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25353 <pre><code>
25354 var view = new Roo.JsonView({
25355     container: "my-element",
25356     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25357     multiSelect: true, 
25358     jsonRoot: "data" 
25359 });
25360
25361 // listen for node click?
25362 view.on("click", function(vw, index, node, e){
25363     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25364 });
25365
25366 // direct load of JSON data
25367 view.load("foobar.php");
25368
25369 // Example from my blog list
25370 var tpl = new Roo.Template(
25371     '&lt;div class="entry"&gt;' +
25372     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25373     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25374     "&lt;/div&gt;&lt;hr /&gt;"
25375 );
25376
25377 var moreView = new Roo.JsonView({
25378     container :  "entry-list", 
25379     template : tpl,
25380     jsonRoot: "posts"
25381 });
25382 moreView.on("beforerender", this.sortEntries, this);
25383 moreView.load({
25384     url: "/blog/get-posts.php",
25385     params: "allposts=true",
25386     text: "Loading Blog Entries..."
25387 });
25388 </code></pre>
25389
25390 * Note: old code is supported with arguments : (container, template, config)
25391
25392
25393  * @constructor
25394  * Create a new JsonView
25395  * 
25396  * @param {Object} config The config object
25397  * 
25398  */
25399 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25400     
25401     
25402     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25403
25404     var um = this.el.getUpdateManager();
25405     um.setRenderer(this);
25406     um.on("update", this.onLoad, this);
25407     um.on("failure", this.onLoadException, this);
25408
25409     /**
25410      * @event beforerender
25411      * Fires before rendering of the downloaded JSON data.
25412      * @param {Roo.JsonView} this
25413      * @param {Object} data The JSON data loaded
25414      */
25415     /**
25416      * @event load
25417      * Fires when data is loaded.
25418      * @param {Roo.JsonView} this
25419      * @param {Object} data The JSON data loaded
25420      * @param {Object} response The raw Connect response object
25421      */
25422     /**
25423      * @event loadexception
25424      * Fires when loading fails.
25425      * @param {Roo.JsonView} this
25426      * @param {Object} response The raw Connect response object
25427      */
25428     this.addEvents({
25429         'beforerender' : true,
25430         'load' : true,
25431         'loadexception' : true
25432     });
25433 };
25434 Roo.extend(Roo.JsonView, Roo.View, {
25435     /**
25436      * @type {String} The root property in the loaded JSON object that contains the data
25437      */
25438     jsonRoot : "",
25439
25440     /**
25441      * Refreshes the view.
25442      */
25443     refresh : function(){
25444         this.clearSelections();
25445         this.el.update("");
25446         var html = [];
25447         var o = this.jsonData;
25448         if(o && o.length > 0){
25449             for(var i = 0, len = o.length; i < len; i++){
25450                 var data = this.prepareData(o[i], i, o);
25451                 html[html.length] = this.tpl.apply(data);
25452             }
25453         }else{
25454             html.push(this.emptyText);
25455         }
25456         this.el.update(html.join(""));
25457         this.nodes = this.el.dom.childNodes;
25458         this.updateIndexes(0);
25459     },
25460
25461     /**
25462      * 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.
25463      * @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:
25464      <pre><code>
25465      view.load({
25466          url: "your-url.php",
25467          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25468          callback: yourFunction,
25469          scope: yourObject, //(optional scope)
25470          discardUrl: false,
25471          nocache: false,
25472          text: "Loading...",
25473          timeout: 30,
25474          scripts: false
25475      });
25476      </code></pre>
25477      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25478      * 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.
25479      * @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}
25480      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25481      * @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.
25482      */
25483     load : function(){
25484         var um = this.el.getUpdateManager();
25485         um.update.apply(um, arguments);
25486     },
25487
25488     render : function(el, response){
25489         this.clearSelections();
25490         this.el.update("");
25491         var o;
25492         try{
25493             o = Roo.util.JSON.decode(response.responseText);
25494             if(this.jsonRoot){
25495                 
25496                 o = o[this.jsonRoot];
25497             }
25498         } catch(e){
25499         }
25500         /**
25501          * The current JSON data or null
25502          */
25503         this.jsonData = o;
25504         this.beforeRender();
25505         this.refresh();
25506     },
25507
25508 /**
25509  * Get the number of records in the current JSON dataset
25510  * @return {Number}
25511  */
25512     getCount : function(){
25513         return this.jsonData ? this.jsonData.length : 0;
25514     },
25515
25516 /**
25517  * Returns the JSON object for the specified node(s)
25518  * @param {HTMLElement/Array} node The node or an array of nodes
25519  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25520  * you get the JSON object for the node
25521  */
25522     getNodeData : function(node){
25523         if(node instanceof Array){
25524             var data = [];
25525             for(var i = 0, len = node.length; i < len; i++){
25526                 data.push(this.getNodeData(node[i]));
25527             }
25528             return data;
25529         }
25530         return this.jsonData[this.indexOf(node)] || null;
25531     },
25532
25533     beforeRender : function(){
25534         this.snapshot = this.jsonData;
25535         if(this.sortInfo){
25536             this.sort.apply(this, this.sortInfo);
25537         }
25538         this.fireEvent("beforerender", this, this.jsonData);
25539     },
25540
25541     onLoad : function(el, o){
25542         this.fireEvent("load", this, this.jsonData, o);
25543     },
25544
25545     onLoadException : function(el, o){
25546         this.fireEvent("loadexception", this, o);
25547     },
25548
25549 /**
25550  * Filter the data by a specific property.
25551  * @param {String} property A property on your JSON objects
25552  * @param {String/RegExp} value Either string that the property values
25553  * should start with, or a RegExp to test against the property
25554  */
25555     filter : function(property, value){
25556         if(this.jsonData){
25557             var data = [];
25558             var ss = this.snapshot;
25559             if(typeof value == "string"){
25560                 var vlen = value.length;
25561                 if(vlen == 0){
25562                     this.clearFilter();
25563                     return;
25564                 }
25565                 value = value.toLowerCase();
25566                 for(var i = 0, len = ss.length; i < len; i++){
25567                     var o = ss[i];
25568                     if(o[property].substr(0, vlen).toLowerCase() == value){
25569                         data.push(o);
25570                     }
25571                 }
25572             } else if(value.exec){ // regex?
25573                 for(var i = 0, len = ss.length; i < len; i++){
25574                     var o = ss[i];
25575                     if(value.test(o[property])){
25576                         data.push(o);
25577                     }
25578                 }
25579             } else{
25580                 return;
25581             }
25582             this.jsonData = data;
25583             this.refresh();
25584         }
25585     },
25586
25587 /**
25588  * Filter by a function. The passed function will be called with each
25589  * object in the current dataset. If the function returns true the value is kept,
25590  * otherwise it is filtered.
25591  * @param {Function} fn
25592  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25593  */
25594     filterBy : function(fn, scope){
25595         if(this.jsonData){
25596             var data = [];
25597             var ss = this.snapshot;
25598             for(var i = 0, len = ss.length; i < len; i++){
25599                 var o = ss[i];
25600                 if(fn.call(scope || this, o)){
25601                     data.push(o);
25602                 }
25603             }
25604             this.jsonData = data;
25605             this.refresh();
25606         }
25607     },
25608
25609 /**
25610  * Clears the current filter.
25611  */
25612     clearFilter : function(){
25613         if(this.snapshot && this.jsonData != this.snapshot){
25614             this.jsonData = this.snapshot;
25615             this.refresh();
25616         }
25617     },
25618
25619
25620 /**
25621  * Sorts the data for this view and refreshes it.
25622  * @param {String} property A property on your JSON objects to sort on
25623  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25624  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25625  */
25626     sort : function(property, dir, sortType){
25627         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25628         if(this.jsonData){
25629             var p = property;
25630             var dsc = dir && dir.toLowerCase() == "desc";
25631             var f = function(o1, o2){
25632                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25633                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25634                 ;
25635                 if(v1 < v2){
25636                     return dsc ? +1 : -1;
25637                 } else if(v1 > v2){
25638                     return dsc ? -1 : +1;
25639                 } else{
25640                     return 0;
25641                 }
25642             };
25643             this.jsonData.sort(f);
25644             this.refresh();
25645             if(this.jsonData != this.snapshot){
25646                 this.snapshot.sort(f);
25647             }
25648         }
25649     }
25650 });/*
25651  * Based on:
25652  * Ext JS Library 1.1.1
25653  * Copyright(c) 2006-2007, Ext JS, LLC.
25654  *
25655  * Originally Released Under LGPL - original licence link has changed is not relivant.
25656  *
25657  * Fork - LGPL
25658  * <script type="text/javascript">
25659  */
25660  
25661
25662 /**
25663  * @class Roo.ColorPalette
25664  * @extends Roo.Component
25665  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25666  * Here's an example of typical usage:
25667  * <pre><code>
25668 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25669 cp.render('my-div');
25670
25671 cp.on('select', function(palette, selColor){
25672     // do something with selColor
25673 });
25674 </code></pre>
25675  * @constructor
25676  * Create a new ColorPalette
25677  * @param {Object} config The config object
25678  */
25679 Roo.ColorPalette = function(config){
25680     Roo.ColorPalette.superclass.constructor.call(this, config);
25681     this.addEvents({
25682         /**
25683              * @event select
25684              * Fires when a color is selected
25685              * @param {ColorPalette} this
25686              * @param {String} color The 6-digit color hex code (without the # symbol)
25687              */
25688         select: true
25689     });
25690
25691     if(this.handler){
25692         this.on("select", this.handler, this.scope, true);
25693     }
25694 };
25695 Roo.extend(Roo.ColorPalette, Roo.Component, {
25696     /**
25697      * @cfg {String} itemCls
25698      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25699      */
25700     itemCls : "x-color-palette",
25701     /**
25702      * @cfg {String} value
25703      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25704      * the hex codes are case-sensitive.
25705      */
25706     value : null,
25707     clickEvent:'click',
25708     // private
25709     ctype: "Roo.ColorPalette",
25710
25711     /**
25712      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25713      */
25714     allowReselect : false,
25715
25716     /**
25717      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25718      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25719      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25720      * of colors with the width setting until the box is symmetrical.</p>
25721      * <p>You can override individual colors if needed:</p>
25722      * <pre><code>
25723 var cp = new Roo.ColorPalette();
25724 cp.colors[0] = "FF0000";  // change the first box to red
25725 </code></pre>
25726
25727 Or you can provide a custom array of your own for complete control:
25728 <pre><code>
25729 var cp = new Roo.ColorPalette();
25730 cp.colors = ["000000", "993300", "333300"];
25731 </code></pre>
25732      * @type Array
25733      */
25734     colors : [
25735         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25736         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25737         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25738         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25739         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25740     ],
25741
25742     // private
25743     onRender : function(container, position){
25744         var t = new Roo.MasterTemplate(
25745             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25746         );
25747         var c = this.colors;
25748         for(var i = 0, len = c.length; i < len; i++){
25749             t.add([c[i]]);
25750         }
25751         var el = document.createElement("div");
25752         el.className = this.itemCls;
25753         t.overwrite(el);
25754         container.dom.insertBefore(el, position);
25755         this.el = Roo.get(el);
25756         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25757         if(this.clickEvent != 'click'){
25758             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25759         }
25760     },
25761
25762     // private
25763     afterRender : function(){
25764         Roo.ColorPalette.superclass.afterRender.call(this);
25765         if(this.value){
25766             var s = this.value;
25767             this.value = null;
25768             this.select(s);
25769         }
25770     },
25771
25772     // private
25773     handleClick : function(e, t){
25774         e.preventDefault();
25775         if(!this.disabled){
25776             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25777             this.select(c.toUpperCase());
25778         }
25779     },
25780
25781     /**
25782      * Selects the specified color in the palette (fires the select event)
25783      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25784      */
25785     select : function(color){
25786         color = color.replace("#", "");
25787         if(color != this.value || this.allowReselect){
25788             var el = this.el;
25789             if(this.value){
25790                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25791             }
25792             el.child("a.color-"+color).addClass("x-color-palette-sel");
25793             this.value = color;
25794             this.fireEvent("select", this, color);
25795         }
25796     }
25797 });/*
25798  * Based on:
25799  * Ext JS Library 1.1.1
25800  * Copyright(c) 2006-2007, Ext JS, LLC.
25801  *
25802  * Originally Released Under LGPL - original licence link has changed is not relivant.
25803  *
25804  * Fork - LGPL
25805  * <script type="text/javascript">
25806  */
25807  
25808 /**
25809  * @class Roo.DatePicker
25810  * @extends Roo.Component
25811  * Simple date picker class.
25812  * @constructor
25813  * Create a new DatePicker
25814  * @param {Object} config The config object
25815  */
25816 Roo.DatePicker = function(config){
25817     Roo.DatePicker.superclass.constructor.call(this, config);
25818
25819     this.value = config && config.value ?
25820                  config.value.clearTime() : new Date().clearTime();
25821
25822     this.addEvents({
25823         /**
25824              * @event select
25825              * Fires when a date is selected
25826              * @param {DatePicker} this
25827              * @param {Date} date The selected date
25828              */
25829         'select': true,
25830         /**
25831              * @event monthchange
25832              * Fires when the displayed month changes 
25833              * @param {DatePicker} this
25834              * @param {Date} date The selected month
25835              */
25836         'monthchange': true
25837     });
25838
25839     if(this.handler){
25840         this.on("select", this.handler,  this.scope || this);
25841     }
25842     // build the disabledDatesRE
25843     if(!this.disabledDatesRE && this.disabledDates){
25844         var dd = this.disabledDates;
25845         var re = "(?:";
25846         for(var i = 0; i < dd.length; i++){
25847             re += dd[i];
25848             if(i != dd.length-1) re += "|";
25849         }
25850         this.disabledDatesRE = new RegExp(re + ")");
25851     }
25852 };
25853
25854 Roo.extend(Roo.DatePicker, Roo.Component, {
25855     /**
25856      * @cfg {String} todayText
25857      * The text to display on the button that selects the current date (defaults to "Today")
25858      */
25859     todayText : "Today",
25860     /**
25861      * @cfg {String} okText
25862      * The text to display on the ok button
25863      */
25864     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25865     /**
25866      * @cfg {String} cancelText
25867      * The text to display on the cancel button
25868      */
25869     cancelText : "Cancel",
25870     /**
25871      * @cfg {String} todayTip
25872      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25873      */
25874     todayTip : "{0} (Spacebar)",
25875     /**
25876      * @cfg {Date} minDate
25877      * Minimum allowable date (JavaScript date object, defaults to null)
25878      */
25879     minDate : null,
25880     /**
25881      * @cfg {Date} maxDate
25882      * Maximum allowable date (JavaScript date object, defaults to null)
25883      */
25884     maxDate : null,
25885     /**
25886      * @cfg {String} minText
25887      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25888      */
25889     minText : "This date is before the minimum date",
25890     /**
25891      * @cfg {String} maxText
25892      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25893      */
25894     maxText : "This date is after the maximum date",
25895     /**
25896      * @cfg {String} format
25897      * The default date format string which can be overriden for localization support.  The format must be
25898      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25899      */
25900     format : "m/d/y",
25901     /**
25902      * @cfg {Array} disabledDays
25903      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25904      */
25905     disabledDays : null,
25906     /**
25907      * @cfg {String} disabledDaysText
25908      * The tooltip to display when the date falls on a disabled day (defaults to "")
25909      */
25910     disabledDaysText : "",
25911     /**
25912      * @cfg {RegExp} disabledDatesRE
25913      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25914      */
25915     disabledDatesRE : null,
25916     /**
25917      * @cfg {String} disabledDatesText
25918      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25919      */
25920     disabledDatesText : "",
25921     /**
25922      * @cfg {Boolean} constrainToViewport
25923      * True to constrain the date picker to the viewport (defaults to true)
25924      */
25925     constrainToViewport : true,
25926     /**
25927      * @cfg {Array} monthNames
25928      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25929      */
25930     monthNames : Date.monthNames,
25931     /**
25932      * @cfg {Array} dayNames
25933      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25934      */
25935     dayNames : Date.dayNames,
25936     /**
25937      * @cfg {String} nextText
25938      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25939      */
25940     nextText: 'Next Month (Control+Right)',
25941     /**
25942      * @cfg {String} prevText
25943      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25944      */
25945     prevText: 'Previous Month (Control+Left)',
25946     /**
25947      * @cfg {String} monthYearText
25948      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25949      */
25950     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25951     /**
25952      * @cfg {Number} startDay
25953      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25954      */
25955     startDay : 0,
25956     /**
25957      * @cfg {Bool} showClear
25958      * Show a clear button (usefull for date form elements that can be blank.)
25959      */
25960     
25961     showClear: false,
25962     
25963     /**
25964      * Sets the value of the date field
25965      * @param {Date} value The date to set
25966      */
25967     setValue : function(value){
25968         var old = this.value;
25969         
25970         if (typeof(value) == 'string') {
25971          
25972             value = Date.parseDate(value, this.format);
25973         }
25974         if (!value) {
25975             value = new Date();
25976         }
25977         
25978         this.value = value.clearTime(true);
25979         if(this.el){
25980             this.update(this.value);
25981         }
25982     },
25983
25984     /**
25985      * Gets the current selected value of the date field
25986      * @return {Date} The selected date
25987      */
25988     getValue : function(){
25989         return this.value;
25990     },
25991
25992     // private
25993     focus : function(){
25994         if(this.el){
25995             this.update(this.activeDate);
25996         }
25997     },
25998
25999     // privateval
26000     onRender : function(container, position){
26001         
26002         var m = [
26003              '<table cellspacing="0">',
26004                 '<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>',
26005                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26006         var dn = this.dayNames;
26007         for(var i = 0; i < 7; i++){
26008             var d = this.startDay+i;
26009             if(d > 6){
26010                 d = d-7;
26011             }
26012             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26013         }
26014         m[m.length] = "</tr></thead><tbody><tr>";
26015         for(var i = 0; i < 42; i++) {
26016             if(i % 7 == 0 && i != 0){
26017                 m[m.length] = "</tr><tr>";
26018             }
26019             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26020         }
26021         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26022             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26023
26024         var el = document.createElement("div");
26025         el.className = "x-date-picker";
26026         el.innerHTML = m.join("");
26027
26028         container.dom.insertBefore(el, position);
26029
26030         this.el = Roo.get(el);
26031         this.eventEl = Roo.get(el.firstChild);
26032
26033         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26034             handler: this.showPrevMonth,
26035             scope: this,
26036             preventDefault:true,
26037             stopDefault:true
26038         });
26039
26040         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26041             handler: this.showNextMonth,
26042             scope: this,
26043             preventDefault:true,
26044             stopDefault:true
26045         });
26046
26047         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26048
26049         this.monthPicker = this.el.down('div.x-date-mp');
26050         this.monthPicker.enableDisplayMode('block');
26051         
26052         var kn = new Roo.KeyNav(this.eventEl, {
26053             "left" : function(e){
26054                 e.ctrlKey ?
26055                     this.showPrevMonth() :
26056                     this.update(this.activeDate.add("d", -1));
26057             },
26058
26059             "right" : function(e){
26060                 e.ctrlKey ?
26061                     this.showNextMonth() :
26062                     this.update(this.activeDate.add("d", 1));
26063             },
26064
26065             "up" : function(e){
26066                 e.ctrlKey ?
26067                     this.showNextYear() :
26068                     this.update(this.activeDate.add("d", -7));
26069             },
26070
26071             "down" : function(e){
26072                 e.ctrlKey ?
26073                     this.showPrevYear() :
26074                     this.update(this.activeDate.add("d", 7));
26075             },
26076
26077             "pageUp" : function(e){
26078                 this.showNextMonth();
26079             },
26080
26081             "pageDown" : function(e){
26082                 this.showPrevMonth();
26083             },
26084
26085             "enter" : function(e){
26086                 e.stopPropagation();
26087                 return true;
26088             },
26089
26090             scope : this
26091         });
26092
26093         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26094
26095         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26096
26097         this.el.unselectable();
26098         
26099         this.cells = this.el.select("table.x-date-inner tbody td");
26100         this.textNodes = this.el.query("table.x-date-inner tbody span");
26101
26102         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26103             text: "&#160;",
26104             tooltip: this.monthYearText
26105         });
26106
26107         this.mbtn.on('click', this.showMonthPicker, this);
26108         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26109
26110
26111         var today = (new Date()).dateFormat(this.format);
26112         
26113         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26114         if (this.showClear) {
26115             baseTb.add( new Roo.Toolbar.Fill());
26116         }
26117         baseTb.add({
26118             text: String.format(this.todayText, today),
26119             tooltip: String.format(this.todayTip, today),
26120             handler: this.selectToday,
26121             scope: this
26122         });
26123         
26124         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26125             
26126         //});
26127         if (this.showClear) {
26128             
26129             baseTb.add( new Roo.Toolbar.Fill());
26130             baseTb.add({
26131                 text: '&#160;',
26132                 cls: 'x-btn-icon x-btn-clear',
26133                 handler: function() {
26134                     //this.value = '';
26135                     this.fireEvent("select", this, '');
26136                 },
26137                 scope: this
26138             });
26139         }
26140         
26141         
26142         if(Roo.isIE){
26143             this.el.repaint();
26144         }
26145         this.update(this.value);
26146     },
26147
26148     createMonthPicker : function(){
26149         if(!this.monthPicker.dom.firstChild){
26150             var buf = ['<table border="0" cellspacing="0">'];
26151             for(var i = 0; i < 6; i++){
26152                 buf.push(
26153                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26154                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26155                     i == 0 ?
26156                     '<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>' :
26157                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26158                 );
26159             }
26160             buf.push(
26161                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26162                     this.okText,
26163                     '</button><button type="button" class="x-date-mp-cancel">',
26164                     this.cancelText,
26165                     '</button></td></tr>',
26166                 '</table>'
26167             );
26168             this.monthPicker.update(buf.join(''));
26169             this.monthPicker.on('click', this.onMonthClick, this);
26170             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26171
26172             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26173             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26174
26175             this.mpMonths.each(function(m, a, i){
26176                 i += 1;
26177                 if((i%2) == 0){
26178                     m.dom.xmonth = 5 + Math.round(i * .5);
26179                 }else{
26180                     m.dom.xmonth = Math.round((i-1) * .5);
26181                 }
26182             });
26183         }
26184     },
26185
26186     showMonthPicker : function(){
26187         this.createMonthPicker();
26188         var size = this.el.getSize();
26189         this.monthPicker.setSize(size);
26190         this.monthPicker.child('table').setSize(size);
26191
26192         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26193         this.updateMPMonth(this.mpSelMonth);
26194         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26195         this.updateMPYear(this.mpSelYear);
26196
26197         this.monthPicker.slideIn('t', {duration:.2});
26198     },
26199
26200     updateMPYear : function(y){
26201         this.mpyear = y;
26202         var ys = this.mpYears.elements;
26203         for(var i = 1; i <= 10; i++){
26204             var td = ys[i-1], y2;
26205             if((i%2) == 0){
26206                 y2 = y + Math.round(i * .5);
26207                 td.firstChild.innerHTML = y2;
26208                 td.xyear = y2;
26209             }else{
26210                 y2 = y - (5-Math.round(i * .5));
26211                 td.firstChild.innerHTML = y2;
26212                 td.xyear = y2;
26213             }
26214             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26215         }
26216     },
26217
26218     updateMPMonth : function(sm){
26219         this.mpMonths.each(function(m, a, i){
26220             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26221         });
26222     },
26223
26224     selectMPMonth: function(m){
26225         
26226     },
26227
26228     onMonthClick : function(e, t){
26229         e.stopEvent();
26230         var el = new Roo.Element(t), pn;
26231         if(el.is('button.x-date-mp-cancel')){
26232             this.hideMonthPicker();
26233         }
26234         else if(el.is('button.x-date-mp-ok')){
26235             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26236             this.hideMonthPicker();
26237         }
26238         else if(pn = el.up('td.x-date-mp-month', 2)){
26239             this.mpMonths.removeClass('x-date-mp-sel');
26240             pn.addClass('x-date-mp-sel');
26241             this.mpSelMonth = pn.dom.xmonth;
26242         }
26243         else if(pn = el.up('td.x-date-mp-year', 2)){
26244             this.mpYears.removeClass('x-date-mp-sel');
26245             pn.addClass('x-date-mp-sel');
26246             this.mpSelYear = pn.dom.xyear;
26247         }
26248         else if(el.is('a.x-date-mp-prev')){
26249             this.updateMPYear(this.mpyear-10);
26250         }
26251         else if(el.is('a.x-date-mp-next')){
26252             this.updateMPYear(this.mpyear+10);
26253         }
26254     },
26255
26256     onMonthDblClick : function(e, t){
26257         e.stopEvent();
26258         var el = new Roo.Element(t), pn;
26259         if(pn = el.up('td.x-date-mp-month', 2)){
26260             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26261             this.hideMonthPicker();
26262         }
26263         else if(pn = el.up('td.x-date-mp-year', 2)){
26264             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26265             this.hideMonthPicker();
26266         }
26267     },
26268
26269     hideMonthPicker : function(disableAnim){
26270         if(this.monthPicker){
26271             if(disableAnim === true){
26272                 this.monthPicker.hide();
26273             }else{
26274                 this.monthPicker.slideOut('t', {duration:.2});
26275             }
26276         }
26277     },
26278
26279     // private
26280     showPrevMonth : function(e){
26281         this.update(this.activeDate.add("mo", -1));
26282     },
26283
26284     // private
26285     showNextMonth : function(e){
26286         this.update(this.activeDate.add("mo", 1));
26287     },
26288
26289     // private
26290     showPrevYear : function(){
26291         this.update(this.activeDate.add("y", -1));
26292     },
26293
26294     // private
26295     showNextYear : function(){
26296         this.update(this.activeDate.add("y", 1));
26297     },
26298
26299     // private
26300     handleMouseWheel : function(e){
26301         var delta = e.getWheelDelta();
26302         if(delta > 0){
26303             this.showPrevMonth();
26304             e.stopEvent();
26305         } else if(delta < 0){
26306             this.showNextMonth();
26307             e.stopEvent();
26308         }
26309     },
26310
26311     // private
26312     handleDateClick : function(e, t){
26313         e.stopEvent();
26314         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26315             this.setValue(new Date(t.dateValue));
26316             this.fireEvent("select", this, this.value);
26317         }
26318     },
26319
26320     // private
26321     selectToday : function(){
26322         this.setValue(new Date().clearTime());
26323         this.fireEvent("select", this, this.value);
26324     },
26325
26326     // private
26327     update : function(date)
26328     {
26329         var vd = this.activeDate;
26330         this.activeDate = date;
26331         if(vd && this.el){
26332             var t = date.getTime();
26333             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26334                 this.cells.removeClass("x-date-selected");
26335                 this.cells.each(function(c){
26336                    if(c.dom.firstChild.dateValue == t){
26337                        c.addClass("x-date-selected");
26338                        setTimeout(function(){
26339                             try{c.dom.firstChild.focus();}catch(e){}
26340                        }, 50);
26341                        return false;
26342                    }
26343                 });
26344                 return;
26345             }
26346         }
26347         
26348         var days = date.getDaysInMonth();
26349         var firstOfMonth = date.getFirstDateOfMonth();
26350         var startingPos = firstOfMonth.getDay()-this.startDay;
26351
26352         if(startingPos <= this.startDay){
26353             startingPos += 7;
26354         }
26355
26356         var pm = date.add("mo", -1);
26357         var prevStart = pm.getDaysInMonth()-startingPos;
26358
26359         var cells = this.cells.elements;
26360         var textEls = this.textNodes;
26361         days += startingPos;
26362
26363         // convert everything to numbers so it's fast
26364         var day = 86400000;
26365         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26366         var today = new Date().clearTime().getTime();
26367         var sel = date.clearTime().getTime();
26368         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26369         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26370         var ddMatch = this.disabledDatesRE;
26371         var ddText = this.disabledDatesText;
26372         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26373         var ddaysText = this.disabledDaysText;
26374         var format = this.format;
26375
26376         var setCellClass = function(cal, cell){
26377             cell.title = "";
26378             var t = d.getTime();
26379             cell.firstChild.dateValue = t;
26380             if(t == today){
26381                 cell.className += " x-date-today";
26382                 cell.title = cal.todayText;
26383             }
26384             if(t == sel){
26385                 cell.className += " x-date-selected";
26386                 setTimeout(function(){
26387                     try{cell.firstChild.focus();}catch(e){}
26388                 }, 50);
26389             }
26390             // disabling
26391             if(t < min) {
26392                 cell.className = " x-date-disabled";
26393                 cell.title = cal.minText;
26394                 return;
26395             }
26396             if(t > max) {
26397                 cell.className = " x-date-disabled";
26398                 cell.title = cal.maxText;
26399                 return;
26400             }
26401             if(ddays){
26402                 if(ddays.indexOf(d.getDay()) != -1){
26403                     cell.title = ddaysText;
26404                     cell.className = " x-date-disabled";
26405                 }
26406             }
26407             if(ddMatch && format){
26408                 var fvalue = d.dateFormat(format);
26409                 if(ddMatch.test(fvalue)){
26410                     cell.title = ddText.replace("%0", fvalue);
26411                     cell.className = " x-date-disabled";
26412                 }
26413             }
26414         };
26415
26416         var i = 0;
26417         for(; i < startingPos; i++) {
26418             textEls[i].innerHTML = (++prevStart);
26419             d.setDate(d.getDate()+1);
26420             cells[i].className = "x-date-prevday";
26421             setCellClass(this, cells[i]);
26422         }
26423         for(; i < days; i++){
26424             intDay = i - startingPos + 1;
26425             textEls[i].innerHTML = (intDay);
26426             d.setDate(d.getDate()+1);
26427             cells[i].className = "x-date-active";
26428             setCellClass(this, cells[i]);
26429         }
26430         var extraDays = 0;
26431         for(; i < 42; i++) {
26432              textEls[i].innerHTML = (++extraDays);
26433              d.setDate(d.getDate()+1);
26434              cells[i].className = "x-date-nextday";
26435              setCellClass(this, cells[i]);
26436         }
26437
26438         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26439         this.fireEvent('monthchange', this, date);
26440         
26441         if(!this.internalRender){
26442             var main = this.el.dom.firstChild;
26443             var w = main.offsetWidth;
26444             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26445             Roo.fly(main).setWidth(w);
26446             this.internalRender = true;
26447             // opera does not respect the auto grow header center column
26448             // then, after it gets a width opera refuses to recalculate
26449             // without a second pass
26450             if(Roo.isOpera && !this.secondPass){
26451                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26452                 this.secondPass = true;
26453                 this.update.defer(10, this, [date]);
26454             }
26455         }
26456         
26457         
26458     }
26459 });        /*
26460  * Based on:
26461  * Ext JS Library 1.1.1
26462  * Copyright(c) 2006-2007, Ext JS, LLC.
26463  *
26464  * Originally Released Under LGPL - original licence link has changed is not relivant.
26465  *
26466  * Fork - LGPL
26467  * <script type="text/javascript">
26468  */
26469 /**
26470  * @class Roo.TabPanel
26471  * @extends Roo.util.Observable
26472  * A lightweight tab container.
26473  * <br><br>
26474  * Usage:
26475  * <pre><code>
26476 // basic tabs 1, built from existing content
26477 var tabs = new Roo.TabPanel("tabs1");
26478 tabs.addTab("script", "View Script");
26479 tabs.addTab("markup", "View Markup");
26480 tabs.activate("script");
26481
26482 // more advanced tabs, built from javascript
26483 var jtabs = new Roo.TabPanel("jtabs");
26484 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26485
26486 // set up the UpdateManager
26487 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26488 var updater = tab2.getUpdateManager();
26489 updater.setDefaultUrl("ajax1.htm");
26490 tab2.on('activate', updater.refresh, updater, true);
26491
26492 // Use setUrl for Ajax loading
26493 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26494 tab3.setUrl("ajax2.htm", null, true);
26495
26496 // Disabled tab
26497 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26498 tab4.disable();
26499
26500 jtabs.activate("jtabs-1");
26501  * </code></pre>
26502  * @constructor
26503  * Create a new TabPanel.
26504  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26505  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26506  */
26507 Roo.TabPanel = function(container, config){
26508     /**
26509     * The container element for this TabPanel.
26510     * @type Roo.Element
26511     */
26512     this.el = Roo.get(container, true);
26513     if(config){
26514         if(typeof config == "boolean"){
26515             this.tabPosition = config ? "bottom" : "top";
26516         }else{
26517             Roo.apply(this, config);
26518         }
26519     }
26520     if(this.tabPosition == "bottom"){
26521         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26522         this.el.addClass("x-tabs-bottom");
26523     }
26524     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26525     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26526     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26527     if(Roo.isIE){
26528         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26529     }
26530     if(this.tabPosition != "bottom"){
26531         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26532          * @type Roo.Element
26533          */
26534         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26535         this.el.addClass("x-tabs-top");
26536     }
26537     this.items = [];
26538
26539     this.bodyEl.setStyle("position", "relative");
26540
26541     this.active = null;
26542     this.activateDelegate = this.activate.createDelegate(this);
26543
26544     this.addEvents({
26545         /**
26546          * @event tabchange
26547          * Fires when the active tab changes
26548          * @param {Roo.TabPanel} this
26549          * @param {Roo.TabPanelItem} activePanel The new active tab
26550          */
26551         "tabchange": true,
26552         /**
26553          * @event beforetabchange
26554          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26555          * @param {Roo.TabPanel} this
26556          * @param {Object} e Set cancel to true on this object to cancel the tab change
26557          * @param {Roo.TabPanelItem} tab The tab being changed to
26558          */
26559         "beforetabchange" : true
26560     });
26561
26562     Roo.EventManager.onWindowResize(this.onResize, this);
26563     this.cpad = this.el.getPadding("lr");
26564     this.hiddenCount = 0;
26565
26566
26567     // toolbar on the tabbar support...
26568     if (this.toolbar) {
26569         var tcfg = this.toolbar;
26570         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26571         this.toolbar = new Roo.Toolbar(tcfg);
26572         if (Roo.isSafari) {
26573             var tbl = tcfg.container.child('table', true);
26574             tbl.setAttribute('width', '100%');
26575         }
26576         
26577     }
26578    
26579
26580
26581     Roo.TabPanel.superclass.constructor.call(this);
26582 };
26583
26584 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26585     /*
26586      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26587      */
26588     tabPosition : "top",
26589     /*
26590      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26591      */
26592     currentTabWidth : 0,
26593     /*
26594      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26595      */
26596     minTabWidth : 40,
26597     /*
26598      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26599      */
26600     maxTabWidth : 250,
26601     /*
26602      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26603      */
26604     preferredTabWidth : 175,
26605     /*
26606      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26607      */
26608     resizeTabs : false,
26609     /*
26610      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26611      */
26612     monitorResize : true,
26613     /*
26614      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26615      */
26616     toolbar : false,
26617
26618     /**
26619      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26620      * @param {String} id The id of the div to use <b>or create</b>
26621      * @param {String} text The text for the tab
26622      * @param {String} content (optional) Content to put in the TabPanelItem body
26623      * @param {Boolean} closable (optional) True to create a close icon on the tab
26624      * @return {Roo.TabPanelItem} The created TabPanelItem
26625      */
26626     addTab : function(id, text, content, closable){
26627         var item = new Roo.TabPanelItem(this, id, text, closable);
26628         this.addTabItem(item);
26629         if(content){
26630             item.setContent(content);
26631         }
26632         return item;
26633     },
26634
26635     /**
26636      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26637      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26638      * @return {Roo.TabPanelItem}
26639      */
26640     getTab : function(id){
26641         return this.items[id];
26642     },
26643
26644     /**
26645      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26646      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26647      */
26648     hideTab : function(id){
26649         var t = this.items[id];
26650         if(!t.isHidden()){
26651            t.setHidden(true);
26652            this.hiddenCount++;
26653            this.autoSizeTabs();
26654         }
26655     },
26656
26657     /**
26658      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26659      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26660      */
26661     unhideTab : function(id){
26662         var t = this.items[id];
26663         if(t.isHidden()){
26664            t.setHidden(false);
26665            this.hiddenCount--;
26666            this.autoSizeTabs();
26667         }
26668     },
26669
26670     /**
26671      * Adds an existing {@link Roo.TabPanelItem}.
26672      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26673      */
26674     addTabItem : function(item){
26675         this.items[item.id] = item;
26676         this.items.push(item);
26677         if(this.resizeTabs){
26678            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26679            this.autoSizeTabs();
26680         }else{
26681             item.autoSize();
26682         }
26683     },
26684
26685     /**
26686      * Removes a {@link Roo.TabPanelItem}.
26687      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26688      */
26689     removeTab : function(id){
26690         var items = this.items;
26691         var tab = items[id];
26692         if(!tab) { return; }
26693         var index = items.indexOf(tab);
26694         if(this.active == tab && items.length > 1){
26695             var newTab = this.getNextAvailable(index);
26696             if(newTab) {
26697                 newTab.activate();
26698             }
26699         }
26700         this.stripEl.dom.removeChild(tab.pnode.dom);
26701         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26702             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26703         }
26704         items.splice(index, 1);
26705         delete this.items[tab.id];
26706         tab.fireEvent("close", tab);
26707         tab.purgeListeners();
26708         this.autoSizeTabs();
26709     },
26710
26711     getNextAvailable : function(start){
26712         var items = this.items;
26713         var index = start;
26714         // look for a next tab that will slide over to
26715         // replace the one being removed
26716         while(index < items.length){
26717             var item = items[++index];
26718             if(item && !item.isHidden()){
26719                 return item;
26720             }
26721         }
26722         // if one isn't found select the previous tab (on the left)
26723         index = start;
26724         while(index >= 0){
26725             var item = items[--index];
26726             if(item && !item.isHidden()){
26727                 return item;
26728             }
26729         }
26730         return null;
26731     },
26732
26733     /**
26734      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26735      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26736      */
26737     disableTab : function(id){
26738         var tab = this.items[id];
26739         if(tab && this.active != tab){
26740             tab.disable();
26741         }
26742     },
26743
26744     /**
26745      * Enables a {@link Roo.TabPanelItem} that is disabled.
26746      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26747      */
26748     enableTab : function(id){
26749         var tab = this.items[id];
26750         tab.enable();
26751     },
26752
26753     /**
26754      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26755      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26756      * @return {Roo.TabPanelItem} The TabPanelItem.
26757      */
26758     activate : function(id){
26759         var tab = this.items[id];
26760         if(!tab){
26761             return null;
26762         }
26763         if(tab == this.active || tab.disabled){
26764             return tab;
26765         }
26766         var e = {};
26767         this.fireEvent("beforetabchange", this, e, tab);
26768         if(e.cancel !== true && !tab.disabled){
26769             if(this.active){
26770                 this.active.hide();
26771             }
26772             this.active = this.items[id];
26773             this.active.show();
26774             this.fireEvent("tabchange", this, this.active);
26775         }
26776         return tab;
26777     },
26778
26779     /**
26780      * Gets the active {@link Roo.TabPanelItem}.
26781      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26782      */
26783     getActiveTab : function(){
26784         return this.active;
26785     },
26786
26787     /**
26788      * Updates the tab body element to fit the height of the container element
26789      * for overflow scrolling
26790      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26791      */
26792     syncHeight : function(targetHeight){
26793         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26794         var bm = this.bodyEl.getMargins();
26795         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26796         this.bodyEl.setHeight(newHeight);
26797         return newHeight;
26798     },
26799
26800     onResize : function(){
26801         if(this.monitorResize){
26802             this.autoSizeTabs();
26803         }
26804     },
26805
26806     /**
26807      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26808      */
26809     beginUpdate : function(){
26810         this.updating = true;
26811     },
26812
26813     /**
26814      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26815      */
26816     endUpdate : function(){
26817         this.updating = false;
26818         this.autoSizeTabs();
26819     },
26820
26821     /**
26822      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26823      */
26824     autoSizeTabs : function(){
26825         var count = this.items.length;
26826         var vcount = count - this.hiddenCount;
26827         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26828         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26829         var availWidth = Math.floor(w / vcount);
26830         var b = this.stripBody;
26831         if(b.getWidth() > w){
26832             var tabs = this.items;
26833             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26834             if(availWidth < this.minTabWidth){
26835                 /*if(!this.sleft){    // incomplete scrolling code
26836                     this.createScrollButtons();
26837                 }
26838                 this.showScroll();
26839                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26840             }
26841         }else{
26842             if(this.currentTabWidth < this.preferredTabWidth){
26843                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26844             }
26845         }
26846     },
26847
26848     /**
26849      * Returns the number of tabs in this TabPanel.
26850      * @return {Number}
26851      */
26852      getCount : function(){
26853          return this.items.length;
26854      },
26855
26856     /**
26857      * Resizes all the tabs to the passed width
26858      * @param {Number} The new width
26859      */
26860     setTabWidth : function(width){
26861         this.currentTabWidth = width;
26862         for(var i = 0, len = this.items.length; i < len; i++) {
26863                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26864         }
26865     },
26866
26867     /**
26868      * Destroys this TabPanel
26869      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26870      */
26871     destroy : function(removeEl){
26872         Roo.EventManager.removeResizeListener(this.onResize, this);
26873         for(var i = 0, len = this.items.length; i < len; i++){
26874             this.items[i].purgeListeners();
26875         }
26876         if(removeEl === true){
26877             this.el.update("");
26878             this.el.remove();
26879         }
26880     }
26881 });
26882
26883 /**
26884  * @class Roo.TabPanelItem
26885  * @extends Roo.util.Observable
26886  * Represents an individual item (tab plus body) in a TabPanel.
26887  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26888  * @param {String} id The id of this TabPanelItem
26889  * @param {String} text The text for the tab of this TabPanelItem
26890  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26891  */
26892 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26893     /**
26894      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26895      * @type Roo.TabPanel
26896      */
26897     this.tabPanel = tabPanel;
26898     /**
26899      * The id for this TabPanelItem
26900      * @type String
26901      */
26902     this.id = id;
26903     /** @private */
26904     this.disabled = false;
26905     /** @private */
26906     this.text = text;
26907     /** @private */
26908     this.loaded = false;
26909     this.closable = closable;
26910
26911     /**
26912      * The body element for this TabPanelItem.
26913      * @type Roo.Element
26914      */
26915     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26916     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26917     this.bodyEl.setStyle("display", "block");
26918     this.bodyEl.setStyle("zoom", "1");
26919     this.hideAction();
26920
26921     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26922     /** @private */
26923     this.el = Roo.get(els.el, true);
26924     this.inner = Roo.get(els.inner, true);
26925     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26926     this.pnode = Roo.get(els.el.parentNode, true);
26927     this.el.on("mousedown", this.onTabMouseDown, this);
26928     this.el.on("click", this.onTabClick, this);
26929     /** @private */
26930     if(closable){
26931         var c = Roo.get(els.close, true);
26932         c.dom.title = this.closeText;
26933         c.addClassOnOver("close-over");
26934         c.on("click", this.closeClick, this);
26935      }
26936
26937     this.addEvents({
26938          /**
26939          * @event activate
26940          * Fires when this tab becomes the active tab.
26941          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26942          * @param {Roo.TabPanelItem} this
26943          */
26944         "activate": true,
26945         /**
26946          * @event beforeclose
26947          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26948          * @param {Roo.TabPanelItem} this
26949          * @param {Object} e Set cancel to true on this object to cancel the close.
26950          */
26951         "beforeclose": true,
26952         /**
26953          * @event close
26954          * Fires when this tab is closed.
26955          * @param {Roo.TabPanelItem} this
26956          */
26957          "close": true,
26958         /**
26959          * @event deactivate
26960          * Fires when this tab is no longer the active tab.
26961          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26962          * @param {Roo.TabPanelItem} this
26963          */
26964          "deactivate" : true
26965     });
26966     this.hidden = false;
26967
26968     Roo.TabPanelItem.superclass.constructor.call(this);
26969 };
26970
26971 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26972     purgeListeners : function(){
26973        Roo.util.Observable.prototype.purgeListeners.call(this);
26974        this.el.removeAllListeners();
26975     },
26976     /**
26977      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26978      */
26979     show : function(){
26980         this.pnode.addClass("on");
26981         this.showAction();
26982         if(Roo.isOpera){
26983             this.tabPanel.stripWrap.repaint();
26984         }
26985         this.fireEvent("activate", this.tabPanel, this);
26986     },
26987
26988     /**
26989      * Returns true if this tab is the active tab.
26990      * @return {Boolean}
26991      */
26992     isActive : function(){
26993         return this.tabPanel.getActiveTab() == this;
26994     },
26995
26996     /**
26997      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26998      */
26999     hide : function(){
27000         this.pnode.removeClass("on");
27001         this.hideAction();
27002         this.fireEvent("deactivate", this.tabPanel, this);
27003     },
27004
27005     hideAction : function(){
27006         this.bodyEl.hide();
27007         this.bodyEl.setStyle("position", "absolute");
27008         this.bodyEl.setLeft("-20000px");
27009         this.bodyEl.setTop("-20000px");
27010     },
27011
27012     showAction : function(){
27013         this.bodyEl.setStyle("position", "relative");
27014         this.bodyEl.setTop("");
27015         this.bodyEl.setLeft("");
27016         this.bodyEl.show();
27017     },
27018
27019     /**
27020      * Set the tooltip for the tab.
27021      * @param {String} tooltip The tab's tooltip
27022      */
27023     setTooltip : function(text){
27024         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27025             this.textEl.dom.qtip = text;
27026             this.textEl.dom.removeAttribute('title');
27027         }else{
27028             this.textEl.dom.title = text;
27029         }
27030     },
27031
27032     onTabClick : function(e){
27033         e.preventDefault();
27034         this.tabPanel.activate(this.id);
27035     },
27036
27037     onTabMouseDown : function(e){
27038         e.preventDefault();
27039         this.tabPanel.activate(this.id);
27040     },
27041
27042     getWidth : function(){
27043         return this.inner.getWidth();
27044     },
27045
27046     setWidth : function(width){
27047         var iwidth = width - this.pnode.getPadding("lr");
27048         this.inner.setWidth(iwidth);
27049         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27050         this.pnode.setWidth(width);
27051     },
27052
27053     /**
27054      * Show or hide the tab
27055      * @param {Boolean} hidden True to hide or false to show.
27056      */
27057     setHidden : function(hidden){
27058         this.hidden = hidden;
27059         this.pnode.setStyle("display", hidden ? "none" : "");
27060     },
27061
27062     /**
27063      * Returns true if this tab is "hidden"
27064      * @return {Boolean}
27065      */
27066     isHidden : function(){
27067         return this.hidden;
27068     },
27069
27070     /**
27071      * Returns the text for this tab
27072      * @return {String}
27073      */
27074     getText : function(){
27075         return this.text;
27076     },
27077
27078     autoSize : function(){
27079         //this.el.beginMeasure();
27080         this.textEl.setWidth(1);
27081         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27082         //this.el.endMeasure();
27083     },
27084
27085     /**
27086      * Sets the text for the tab (Note: this also sets the tooltip text)
27087      * @param {String} text The tab's text and tooltip
27088      */
27089     setText : function(text){
27090         this.text = text;
27091         this.textEl.update(text);
27092         this.setTooltip(text);
27093         if(!this.tabPanel.resizeTabs){
27094             this.autoSize();
27095         }
27096     },
27097     /**
27098      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27099      */
27100     activate : function(){
27101         this.tabPanel.activate(this.id);
27102     },
27103
27104     /**
27105      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27106      */
27107     disable : function(){
27108         if(this.tabPanel.active != this){
27109             this.disabled = true;
27110             this.pnode.addClass("disabled");
27111         }
27112     },
27113
27114     /**
27115      * Enables this TabPanelItem if it was previously disabled.
27116      */
27117     enable : function(){
27118         this.disabled = false;
27119         this.pnode.removeClass("disabled");
27120     },
27121
27122     /**
27123      * Sets the content for this TabPanelItem.
27124      * @param {String} content The content
27125      * @param {Boolean} loadScripts true to look for and load scripts
27126      */
27127     setContent : function(content, loadScripts){
27128         this.bodyEl.update(content, loadScripts);
27129     },
27130
27131     /**
27132      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27133      * @return {Roo.UpdateManager} The UpdateManager
27134      */
27135     getUpdateManager : function(){
27136         return this.bodyEl.getUpdateManager();
27137     },
27138
27139     /**
27140      * Set a URL to be used to load the content for this TabPanelItem.
27141      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27142      * @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)
27143      * @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)
27144      * @return {Roo.UpdateManager} The UpdateManager
27145      */
27146     setUrl : function(url, params, loadOnce){
27147         if(this.refreshDelegate){
27148             this.un('activate', this.refreshDelegate);
27149         }
27150         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27151         this.on("activate", this.refreshDelegate);
27152         return this.bodyEl.getUpdateManager();
27153     },
27154
27155     /** @private */
27156     _handleRefresh : function(url, params, loadOnce){
27157         if(!loadOnce || !this.loaded){
27158             var updater = this.bodyEl.getUpdateManager();
27159             updater.update(url, params, this._setLoaded.createDelegate(this));
27160         }
27161     },
27162
27163     /**
27164      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27165      *   Will fail silently if the setUrl method has not been called.
27166      *   This does not activate the panel, just updates its content.
27167      */
27168     refresh : function(){
27169         if(this.refreshDelegate){
27170            this.loaded = false;
27171            this.refreshDelegate();
27172         }
27173     },
27174
27175     /** @private */
27176     _setLoaded : function(){
27177         this.loaded = true;
27178     },
27179
27180     /** @private */
27181     closeClick : function(e){
27182         var o = {};
27183         e.stopEvent();
27184         this.fireEvent("beforeclose", this, o);
27185         if(o.cancel !== true){
27186             this.tabPanel.removeTab(this.id);
27187         }
27188     },
27189     /**
27190      * The text displayed in the tooltip for the close icon.
27191      * @type String
27192      */
27193     closeText : "Close this tab"
27194 });
27195
27196 /** @private */
27197 Roo.TabPanel.prototype.createStrip = function(container){
27198     var strip = document.createElement("div");
27199     strip.className = "x-tabs-wrap";
27200     container.appendChild(strip);
27201     return strip;
27202 };
27203 /** @private */
27204 Roo.TabPanel.prototype.createStripList = function(strip){
27205     // div wrapper for retard IE
27206     // returns the "tr" element.
27207     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27208         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27209         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27210     return strip.firstChild.firstChild.firstChild.firstChild;
27211 };
27212 /** @private */
27213 Roo.TabPanel.prototype.createBody = function(container){
27214     var body = document.createElement("div");
27215     Roo.id(body, "tab-body");
27216     Roo.fly(body).addClass("x-tabs-body");
27217     container.appendChild(body);
27218     return body;
27219 };
27220 /** @private */
27221 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27222     var body = Roo.getDom(id);
27223     if(!body){
27224         body = document.createElement("div");
27225         body.id = id;
27226     }
27227     Roo.fly(body).addClass("x-tabs-item-body");
27228     bodyEl.insertBefore(body, bodyEl.firstChild);
27229     return body;
27230 };
27231 /** @private */
27232 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27233     var td = document.createElement("td");
27234     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27235     //stripEl.appendChild(td);
27236     if(closable){
27237         td.className = "x-tabs-closable";
27238         if(!this.closeTpl){
27239             this.closeTpl = new Roo.Template(
27240                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27241                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27242                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27243             );
27244         }
27245         var el = this.closeTpl.overwrite(td, {"text": text});
27246         var close = el.getElementsByTagName("div")[0];
27247         var inner = el.getElementsByTagName("em")[0];
27248         return {"el": el, "close": close, "inner": inner};
27249     } else {
27250         if(!this.tabTpl){
27251             this.tabTpl = new Roo.Template(
27252                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27253                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27254             );
27255         }
27256         var el = this.tabTpl.overwrite(td, {"text": text});
27257         var inner = el.getElementsByTagName("em")[0];
27258         return {"el": el, "inner": inner};
27259     }
27260 };/*
27261  * Based on:
27262  * Ext JS Library 1.1.1
27263  * Copyright(c) 2006-2007, Ext JS, LLC.
27264  *
27265  * Originally Released Under LGPL - original licence link has changed is not relivant.
27266  *
27267  * Fork - LGPL
27268  * <script type="text/javascript">
27269  */
27270
27271 /**
27272  * @class Roo.Button
27273  * @extends Roo.util.Observable
27274  * Simple Button class
27275  * @cfg {String} text The button text
27276  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27277  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27278  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27279  * @cfg {Object} scope The scope of the handler
27280  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27281  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27282  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27283  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27284  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27285  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27286    applies if enableToggle = true)
27287  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27288  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27289   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27290  * @constructor
27291  * Create a new button
27292  * @param {Object} config The config object
27293  */
27294 Roo.Button = function(renderTo, config)
27295 {
27296     if (!config) {
27297         config = renderTo;
27298         renderTo = config.renderTo || false;
27299     }
27300     
27301     Roo.apply(this, config);
27302     this.addEvents({
27303         /**
27304              * @event click
27305              * Fires when this button is clicked
27306              * @param {Button} this
27307              * @param {EventObject} e The click event
27308              */
27309             "click" : true,
27310         /**
27311              * @event toggle
27312              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27313              * @param {Button} this
27314              * @param {Boolean} pressed
27315              */
27316             "toggle" : true,
27317         /**
27318              * @event mouseover
27319              * Fires when the mouse hovers over the button
27320              * @param {Button} this
27321              * @param {Event} e The event object
27322              */
27323         'mouseover' : true,
27324         /**
27325              * @event mouseout
27326              * Fires when the mouse exits the button
27327              * @param {Button} this
27328              * @param {Event} e The event object
27329              */
27330         'mouseout': true,
27331          /**
27332              * @event render
27333              * Fires when the button is rendered
27334              * @param {Button} this
27335              */
27336         'render': true
27337     });
27338     if(this.menu){
27339         this.menu = Roo.menu.MenuMgr.get(this.menu);
27340     }
27341     // register listeners first!!  - so render can be captured..
27342     Roo.util.Observable.call(this);
27343     if(renderTo){
27344         this.render(renderTo);
27345     }
27346     
27347   
27348 };
27349
27350 Roo.extend(Roo.Button, Roo.util.Observable, {
27351     /**
27352      * 
27353      */
27354     
27355     /**
27356      * Read-only. True if this button is hidden
27357      * @type Boolean
27358      */
27359     hidden : false,
27360     /**
27361      * Read-only. True if this button is disabled
27362      * @type Boolean
27363      */
27364     disabled : false,
27365     /**
27366      * Read-only. True if this button is pressed (only if enableToggle = true)
27367      * @type Boolean
27368      */
27369     pressed : false,
27370
27371     /**
27372      * @cfg {Number} tabIndex 
27373      * The DOM tabIndex for this button (defaults to undefined)
27374      */
27375     tabIndex : undefined,
27376
27377     /**
27378      * @cfg {Boolean} enableToggle
27379      * True to enable pressed/not pressed toggling (defaults to false)
27380      */
27381     enableToggle: false,
27382     /**
27383      * @cfg {Mixed} menu
27384      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27385      */
27386     menu : undefined,
27387     /**
27388      * @cfg {String} menuAlign
27389      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27390      */
27391     menuAlign : "tl-bl?",
27392
27393     /**
27394      * @cfg {String} iconCls
27395      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27396      */
27397     iconCls : undefined,
27398     /**
27399      * @cfg {String} type
27400      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27401      */
27402     type : 'button',
27403
27404     // private
27405     menuClassTarget: 'tr',
27406
27407     /**
27408      * @cfg {String} clickEvent
27409      * The type of event to map to the button's event handler (defaults to 'click')
27410      */
27411     clickEvent : 'click',
27412
27413     /**
27414      * @cfg {Boolean} handleMouseEvents
27415      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27416      */
27417     handleMouseEvents : true,
27418
27419     /**
27420      * @cfg {String} tooltipType
27421      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27422      */
27423     tooltipType : 'qtip',
27424
27425     /**
27426      * @cfg {String} cls
27427      * A CSS class to apply to the button's main element.
27428      */
27429     
27430     /**
27431      * @cfg {Roo.Template} template (Optional)
27432      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27433      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27434      * require code modifications if required elements (e.g. a button) aren't present.
27435      */
27436
27437     // private
27438     render : function(renderTo){
27439         var btn;
27440         if(this.hideParent){
27441             this.parentEl = Roo.get(renderTo);
27442         }
27443         if(!this.dhconfig){
27444             if(!this.template){
27445                 if(!Roo.Button.buttonTemplate){
27446                     // hideous table template
27447                     Roo.Button.buttonTemplate = new Roo.Template(
27448                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27449                         '<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>',
27450                         "</tr></tbody></table>");
27451                 }
27452                 this.template = Roo.Button.buttonTemplate;
27453             }
27454             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27455             var btnEl = btn.child("button:first");
27456             btnEl.on('focus', this.onFocus, this);
27457             btnEl.on('blur', this.onBlur, this);
27458             if(this.cls){
27459                 btn.addClass(this.cls);
27460             }
27461             if(this.icon){
27462                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27463             }
27464             if(this.iconCls){
27465                 btnEl.addClass(this.iconCls);
27466                 if(!this.cls){
27467                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27468                 }
27469             }
27470             if(this.tabIndex !== undefined){
27471                 btnEl.dom.tabIndex = this.tabIndex;
27472             }
27473             if(this.tooltip){
27474                 if(typeof this.tooltip == 'object'){
27475                     Roo.QuickTips.tips(Roo.apply({
27476                           target: btnEl.id
27477                     }, this.tooltip));
27478                 } else {
27479                     btnEl.dom[this.tooltipType] = this.tooltip;
27480                 }
27481             }
27482         }else{
27483             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27484         }
27485         this.el = btn;
27486         if(this.id){
27487             this.el.dom.id = this.el.id = this.id;
27488         }
27489         if(this.menu){
27490             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27491             this.menu.on("show", this.onMenuShow, this);
27492             this.menu.on("hide", this.onMenuHide, this);
27493         }
27494         btn.addClass("x-btn");
27495         if(Roo.isIE && !Roo.isIE7){
27496             this.autoWidth.defer(1, this);
27497         }else{
27498             this.autoWidth();
27499         }
27500         if(this.handleMouseEvents){
27501             btn.on("mouseover", this.onMouseOver, this);
27502             btn.on("mouseout", this.onMouseOut, this);
27503             btn.on("mousedown", this.onMouseDown, this);
27504         }
27505         btn.on(this.clickEvent, this.onClick, this);
27506         //btn.on("mouseup", this.onMouseUp, this);
27507         if(this.hidden){
27508             this.hide();
27509         }
27510         if(this.disabled){
27511             this.disable();
27512         }
27513         Roo.ButtonToggleMgr.register(this);
27514         if(this.pressed){
27515             this.el.addClass("x-btn-pressed");
27516         }
27517         if(this.repeat){
27518             var repeater = new Roo.util.ClickRepeater(btn,
27519                 typeof this.repeat == "object" ? this.repeat : {}
27520             );
27521             repeater.on("click", this.onClick,  this);
27522         }
27523         
27524         this.fireEvent('render', this);
27525         
27526     },
27527     /**
27528      * Returns the button's underlying element
27529      * @return {Roo.Element} The element
27530      */
27531     getEl : function(){
27532         return this.el;  
27533     },
27534     
27535     /**
27536      * Destroys this Button and removes any listeners.
27537      */
27538     destroy : function(){
27539         Roo.ButtonToggleMgr.unregister(this);
27540         this.el.removeAllListeners();
27541         this.purgeListeners();
27542         this.el.remove();
27543     },
27544
27545     // private
27546     autoWidth : function(){
27547         if(this.el){
27548             this.el.setWidth("auto");
27549             if(Roo.isIE7 && Roo.isStrict){
27550                 var ib = this.el.child('button');
27551                 if(ib && ib.getWidth() > 20){
27552                     ib.clip();
27553                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27554                 }
27555             }
27556             if(this.minWidth){
27557                 if(this.hidden){
27558                     this.el.beginMeasure();
27559                 }
27560                 if(this.el.getWidth() < this.minWidth){
27561                     this.el.setWidth(this.minWidth);
27562                 }
27563                 if(this.hidden){
27564                     this.el.endMeasure();
27565                 }
27566             }
27567         }
27568     },
27569
27570     /**
27571      * Assigns this button's click handler
27572      * @param {Function} handler The function to call when the button is clicked
27573      * @param {Object} scope (optional) Scope for the function passed in
27574      */
27575     setHandler : function(handler, scope){
27576         this.handler = handler;
27577         this.scope = scope;  
27578     },
27579     
27580     /**
27581      * Sets this button's text
27582      * @param {String} text The button text
27583      */
27584     setText : function(text){
27585         this.text = text;
27586         if(this.el){
27587             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27588         }
27589         this.autoWidth();
27590     },
27591     
27592     /**
27593      * Gets the text for this button
27594      * @return {String} The button text
27595      */
27596     getText : function(){
27597         return this.text;  
27598     },
27599     
27600     /**
27601      * Show this button
27602      */
27603     show: function(){
27604         this.hidden = false;
27605         if(this.el){
27606             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27607         }
27608     },
27609     
27610     /**
27611      * Hide this button
27612      */
27613     hide: function(){
27614         this.hidden = true;
27615         if(this.el){
27616             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27617         }
27618     },
27619     
27620     /**
27621      * Convenience function for boolean show/hide
27622      * @param {Boolean} visible True to show, false to hide
27623      */
27624     setVisible: function(visible){
27625         if(visible) {
27626             this.show();
27627         }else{
27628             this.hide();
27629         }
27630     },
27631     
27632     /**
27633      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27634      * @param {Boolean} state (optional) Force a particular state
27635      */
27636     toggle : function(state){
27637         state = state === undefined ? !this.pressed : state;
27638         if(state != this.pressed){
27639             if(state){
27640                 this.el.addClass("x-btn-pressed");
27641                 this.pressed = true;
27642                 this.fireEvent("toggle", this, true);
27643             }else{
27644                 this.el.removeClass("x-btn-pressed");
27645                 this.pressed = false;
27646                 this.fireEvent("toggle", this, false);
27647             }
27648             if(this.toggleHandler){
27649                 this.toggleHandler.call(this.scope || this, this, state);
27650             }
27651         }
27652     },
27653     
27654     /**
27655      * Focus the button
27656      */
27657     focus : function(){
27658         this.el.child('button:first').focus();
27659     },
27660     
27661     /**
27662      * Disable this button
27663      */
27664     disable : function(){
27665         if(this.el){
27666             this.el.addClass("x-btn-disabled");
27667         }
27668         this.disabled = true;
27669     },
27670     
27671     /**
27672      * Enable this button
27673      */
27674     enable : function(){
27675         if(this.el){
27676             this.el.removeClass("x-btn-disabled");
27677         }
27678         this.disabled = false;
27679     },
27680
27681     /**
27682      * Convenience function for boolean enable/disable
27683      * @param {Boolean} enabled True to enable, false to disable
27684      */
27685     setDisabled : function(v){
27686         this[v !== true ? "enable" : "disable"]();
27687     },
27688
27689     // private
27690     onClick : function(e){
27691         if(e){
27692             e.preventDefault();
27693         }
27694         if(e.button != 0){
27695             return;
27696         }
27697         if(!this.disabled){
27698             if(this.enableToggle){
27699                 this.toggle();
27700             }
27701             if(this.menu && !this.menu.isVisible()){
27702                 this.menu.show(this.el, this.menuAlign);
27703             }
27704             this.fireEvent("click", this, e);
27705             if(this.handler){
27706                 this.el.removeClass("x-btn-over");
27707                 this.handler.call(this.scope || this, this, e);
27708             }
27709         }
27710     },
27711     // private
27712     onMouseOver : function(e){
27713         if(!this.disabled){
27714             this.el.addClass("x-btn-over");
27715             this.fireEvent('mouseover', this, e);
27716         }
27717     },
27718     // private
27719     onMouseOut : function(e){
27720         if(!e.within(this.el,  true)){
27721             this.el.removeClass("x-btn-over");
27722             this.fireEvent('mouseout', this, e);
27723         }
27724     },
27725     // private
27726     onFocus : function(e){
27727         if(!this.disabled){
27728             this.el.addClass("x-btn-focus");
27729         }
27730     },
27731     // private
27732     onBlur : function(e){
27733         this.el.removeClass("x-btn-focus");
27734     },
27735     // private
27736     onMouseDown : function(e){
27737         if(!this.disabled && e.button == 0){
27738             this.el.addClass("x-btn-click");
27739             Roo.get(document).on('mouseup', this.onMouseUp, this);
27740         }
27741     },
27742     // private
27743     onMouseUp : function(e){
27744         if(e.button == 0){
27745             this.el.removeClass("x-btn-click");
27746             Roo.get(document).un('mouseup', this.onMouseUp, this);
27747         }
27748     },
27749     // private
27750     onMenuShow : function(e){
27751         this.el.addClass("x-btn-menu-active");
27752     },
27753     // private
27754     onMenuHide : function(e){
27755         this.el.removeClass("x-btn-menu-active");
27756     }   
27757 });
27758
27759 // Private utility class used by Button
27760 Roo.ButtonToggleMgr = function(){
27761    var groups = {};
27762    
27763    function toggleGroup(btn, state){
27764        if(state){
27765            var g = groups[btn.toggleGroup];
27766            for(var i = 0, l = g.length; i < l; i++){
27767                if(g[i] != btn){
27768                    g[i].toggle(false);
27769                }
27770            }
27771        }
27772    }
27773    
27774    return {
27775        register : function(btn){
27776            if(!btn.toggleGroup){
27777                return;
27778            }
27779            var g = groups[btn.toggleGroup];
27780            if(!g){
27781                g = groups[btn.toggleGroup] = [];
27782            }
27783            g.push(btn);
27784            btn.on("toggle", toggleGroup);
27785        },
27786        
27787        unregister : function(btn){
27788            if(!btn.toggleGroup){
27789                return;
27790            }
27791            var g = groups[btn.toggleGroup];
27792            if(g){
27793                g.remove(btn);
27794                btn.un("toggle", toggleGroup);
27795            }
27796        }
27797    };
27798 }();/*
27799  * Based on:
27800  * Ext JS Library 1.1.1
27801  * Copyright(c) 2006-2007, Ext JS, LLC.
27802  *
27803  * Originally Released Under LGPL - original licence link has changed is not relivant.
27804  *
27805  * Fork - LGPL
27806  * <script type="text/javascript">
27807  */
27808  
27809 /**
27810  * @class Roo.SplitButton
27811  * @extends Roo.Button
27812  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27813  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27814  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27815  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27816  * @cfg {String} arrowTooltip The title attribute of the arrow
27817  * @constructor
27818  * Create a new menu button
27819  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27820  * @param {Object} config The config object
27821  */
27822 Roo.SplitButton = function(renderTo, config){
27823     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27824     /**
27825      * @event arrowclick
27826      * Fires when this button's arrow is clicked
27827      * @param {SplitButton} this
27828      * @param {EventObject} e The click event
27829      */
27830     this.addEvents({"arrowclick":true});
27831 };
27832
27833 Roo.extend(Roo.SplitButton, Roo.Button, {
27834     render : function(renderTo){
27835         // this is one sweet looking template!
27836         var tpl = new Roo.Template(
27837             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27838             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27839             '<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>',
27840             "</tbody></table></td><td>",
27841             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27842             '<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>',
27843             "</tbody></table></td></tr></table>"
27844         );
27845         var btn = tpl.append(renderTo, [this.text, this.type], true);
27846         var btnEl = btn.child("button");
27847         if(this.cls){
27848             btn.addClass(this.cls);
27849         }
27850         if(this.icon){
27851             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27852         }
27853         if(this.iconCls){
27854             btnEl.addClass(this.iconCls);
27855             if(!this.cls){
27856                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27857             }
27858         }
27859         this.el = btn;
27860         if(this.handleMouseEvents){
27861             btn.on("mouseover", this.onMouseOver, this);
27862             btn.on("mouseout", this.onMouseOut, this);
27863             btn.on("mousedown", this.onMouseDown, this);
27864             btn.on("mouseup", this.onMouseUp, this);
27865         }
27866         btn.on(this.clickEvent, this.onClick, this);
27867         if(this.tooltip){
27868             if(typeof this.tooltip == 'object'){
27869                 Roo.QuickTips.tips(Roo.apply({
27870                       target: btnEl.id
27871                 }, this.tooltip));
27872             } else {
27873                 btnEl.dom[this.tooltipType] = this.tooltip;
27874             }
27875         }
27876         if(this.arrowTooltip){
27877             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27878         }
27879         if(this.hidden){
27880             this.hide();
27881         }
27882         if(this.disabled){
27883             this.disable();
27884         }
27885         if(this.pressed){
27886             this.el.addClass("x-btn-pressed");
27887         }
27888         if(Roo.isIE && !Roo.isIE7){
27889             this.autoWidth.defer(1, this);
27890         }else{
27891             this.autoWidth();
27892         }
27893         if(this.menu){
27894             this.menu.on("show", this.onMenuShow, this);
27895             this.menu.on("hide", this.onMenuHide, this);
27896         }
27897         this.fireEvent('render', this);
27898     },
27899
27900     // private
27901     autoWidth : function(){
27902         if(this.el){
27903             var tbl = this.el.child("table:first");
27904             var tbl2 = this.el.child("table:last");
27905             this.el.setWidth("auto");
27906             tbl.setWidth("auto");
27907             if(Roo.isIE7 && Roo.isStrict){
27908                 var ib = this.el.child('button:first');
27909                 if(ib && ib.getWidth() > 20){
27910                     ib.clip();
27911                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27912                 }
27913             }
27914             if(this.minWidth){
27915                 if(this.hidden){
27916                     this.el.beginMeasure();
27917                 }
27918                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27919                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27920                 }
27921                 if(this.hidden){
27922                     this.el.endMeasure();
27923                 }
27924             }
27925             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27926         } 
27927     },
27928     /**
27929      * Sets this button's click handler
27930      * @param {Function} handler The function to call when the button is clicked
27931      * @param {Object} scope (optional) Scope for the function passed above
27932      */
27933     setHandler : function(handler, scope){
27934         this.handler = handler;
27935         this.scope = scope;  
27936     },
27937     
27938     /**
27939      * Sets this button's arrow click handler
27940      * @param {Function} handler The function to call when the arrow is clicked
27941      * @param {Object} scope (optional) Scope for the function passed above
27942      */
27943     setArrowHandler : function(handler, scope){
27944         this.arrowHandler = handler;
27945         this.scope = scope;  
27946     },
27947     
27948     /**
27949      * Focus the button
27950      */
27951     focus : function(){
27952         if(this.el){
27953             this.el.child("button:first").focus();
27954         }
27955     },
27956
27957     // private
27958     onClick : function(e){
27959         e.preventDefault();
27960         if(!this.disabled){
27961             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27962                 if(this.menu && !this.menu.isVisible()){
27963                     this.menu.show(this.el, this.menuAlign);
27964                 }
27965                 this.fireEvent("arrowclick", this, e);
27966                 if(this.arrowHandler){
27967                     this.arrowHandler.call(this.scope || this, this, e);
27968                 }
27969             }else{
27970                 this.fireEvent("click", this, e);
27971                 if(this.handler){
27972                     this.handler.call(this.scope || this, this, e);
27973                 }
27974             }
27975         }
27976     },
27977     // private
27978     onMouseDown : function(e){
27979         if(!this.disabled){
27980             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27981         }
27982     },
27983     // private
27984     onMouseUp : function(e){
27985         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27986     }   
27987 });
27988
27989
27990 // backwards compat
27991 Roo.MenuButton = Roo.SplitButton;/*
27992  * Based on:
27993  * Ext JS Library 1.1.1
27994  * Copyright(c) 2006-2007, Ext JS, LLC.
27995  *
27996  * Originally Released Under LGPL - original licence link has changed is not relivant.
27997  *
27998  * Fork - LGPL
27999  * <script type="text/javascript">
28000  */
28001
28002 /**
28003  * @class Roo.Toolbar
28004  * Basic Toolbar class.
28005  * @constructor
28006  * Creates a new Toolbar
28007  * @param {Object} container The config object
28008  */ 
28009 Roo.Toolbar = function(container, buttons, config)
28010 {
28011     /// old consturctor format still supported..
28012     if(container instanceof Array){ // omit the container for later rendering
28013         buttons = container;
28014         config = buttons;
28015         container = null;
28016     }
28017     if (typeof(container) == 'object' && container.xtype) {
28018         config = container;
28019         container = config.container;
28020         buttons = config.buttons || []; // not really - use items!!
28021     }
28022     var xitems = [];
28023     if (config && config.items) {
28024         xitems = config.items;
28025         delete config.items;
28026     }
28027     Roo.apply(this, config);
28028     this.buttons = buttons;
28029     
28030     if(container){
28031         this.render(container);
28032     }
28033     this.xitems = xitems;
28034     Roo.each(xitems, function(b) {
28035         this.add(b);
28036     }, this);
28037     
28038 };
28039
28040 Roo.Toolbar.prototype = {
28041     /**
28042      * @cfg {Array} items
28043      * array of button configs or elements to add (will be converted to a MixedCollection)
28044      */
28045     
28046     /**
28047      * @cfg {String/HTMLElement/Element} container
28048      * The id or element that will contain the toolbar
28049      */
28050     // private
28051     render : function(ct){
28052         this.el = Roo.get(ct);
28053         if(this.cls){
28054             this.el.addClass(this.cls);
28055         }
28056         // using a table allows for vertical alignment
28057         // 100% width is needed by Safari...
28058         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28059         this.tr = this.el.child("tr", true);
28060         var autoId = 0;
28061         this.items = new Roo.util.MixedCollection(false, function(o){
28062             return o.id || ("item" + (++autoId));
28063         });
28064         if(this.buttons){
28065             this.add.apply(this, this.buttons);
28066             delete this.buttons;
28067         }
28068     },
28069
28070     /**
28071      * Adds element(s) to the toolbar -- this function takes a variable number of 
28072      * arguments of mixed type and adds them to the toolbar.
28073      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28074      * <ul>
28075      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28076      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28077      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28078      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28079      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28080      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28081      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28082      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28083      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28084      * </ul>
28085      * @param {Mixed} arg2
28086      * @param {Mixed} etc.
28087      */
28088     add : function(){
28089         var a = arguments, l = a.length;
28090         for(var i = 0; i < l; i++){
28091             this._add(a[i]);
28092         }
28093     },
28094     // private..
28095     _add : function(el) {
28096         
28097         if (el.xtype) {
28098             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28099         }
28100         
28101         if (el.applyTo){ // some kind of form field
28102             return this.addField(el);
28103         } 
28104         if (el.render){ // some kind of Toolbar.Item
28105             return this.addItem(el);
28106         }
28107         if (typeof el == "string"){ // string
28108             if(el == "separator" || el == "-"){
28109                 return this.addSeparator();
28110             }
28111             if (el == " "){
28112                 return this.addSpacer();
28113             }
28114             if(el == "->"){
28115                 return this.addFill();
28116             }
28117             return this.addText(el);
28118             
28119         }
28120         if(el.tagName){ // element
28121             return this.addElement(el);
28122         }
28123         if(typeof el == "object"){ // must be button config?
28124             return this.addButton(el);
28125         }
28126         // and now what?!?!
28127         return false;
28128         
28129     },
28130     
28131     /**
28132      * Add an Xtype element
28133      * @param {Object} xtype Xtype Object
28134      * @return {Object} created Object
28135      */
28136     addxtype : function(e){
28137         return this.add(e);  
28138     },
28139     
28140     /**
28141      * Returns the Element for this toolbar.
28142      * @return {Roo.Element}
28143      */
28144     getEl : function(){
28145         return this.el;  
28146     },
28147     
28148     /**
28149      * Adds a separator
28150      * @return {Roo.Toolbar.Item} The separator item
28151      */
28152     addSeparator : function(){
28153         return this.addItem(new Roo.Toolbar.Separator());
28154     },
28155
28156     /**
28157      * Adds a spacer element
28158      * @return {Roo.Toolbar.Spacer} The spacer item
28159      */
28160     addSpacer : function(){
28161         return this.addItem(new Roo.Toolbar.Spacer());
28162     },
28163
28164     /**
28165      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28166      * @return {Roo.Toolbar.Fill} The fill item
28167      */
28168     addFill : function(){
28169         return this.addItem(new Roo.Toolbar.Fill());
28170     },
28171
28172     /**
28173      * Adds any standard HTML element to the toolbar
28174      * @param {String/HTMLElement/Element} el The element or id of the element to add
28175      * @return {Roo.Toolbar.Item} The element's item
28176      */
28177     addElement : function(el){
28178         return this.addItem(new Roo.Toolbar.Item(el));
28179     },
28180     /**
28181      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28182      * @type Roo.util.MixedCollection  
28183      */
28184     items : false,
28185      
28186     /**
28187      * Adds any Toolbar.Item or subclass
28188      * @param {Roo.Toolbar.Item} item
28189      * @return {Roo.Toolbar.Item} The item
28190      */
28191     addItem : function(item){
28192         var td = this.nextBlock();
28193         item.render(td);
28194         this.items.add(item);
28195         return item;
28196     },
28197     
28198     /**
28199      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28200      * @param {Object/Array} config A button config or array of configs
28201      * @return {Roo.Toolbar.Button/Array}
28202      */
28203     addButton : function(config){
28204         if(config instanceof Array){
28205             var buttons = [];
28206             for(var i = 0, len = config.length; i < len; i++) {
28207                 buttons.push(this.addButton(config[i]));
28208             }
28209             return buttons;
28210         }
28211         var b = config;
28212         if(!(config instanceof Roo.Toolbar.Button)){
28213             b = config.split ?
28214                 new Roo.Toolbar.SplitButton(config) :
28215                 new Roo.Toolbar.Button(config);
28216         }
28217         var td = this.nextBlock();
28218         b.render(td);
28219         this.items.add(b);
28220         return b;
28221     },
28222     
28223     /**
28224      * Adds text to the toolbar
28225      * @param {String} text The text to add
28226      * @return {Roo.Toolbar.Item} The element's item
28227      */
28228     addText : function(text){
28229         return this.addItem(new Roo.Toolbar.TextItem(text));
28230     },
28231     
28232     /**
28233      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28234      * @param {Number} index The index where the item is to be inserted
28235      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28236      * @return {Roo.Toolbar.Button/Item}
28237      */
28238     insertButton : function(index, item){
28239         if(item instanceof Array){
28240             var buttons = [];
28241             for(var i = 0, len = item.length; i < len; i++) {
28242                buttons.push(this.insertButton(index + i, item[i]));
28243             }
28244             return buttons;
28245         }
28246         if (!(item instanceof Roo.Toolbar.Button)){
28247            item = new Roo.Toolbar.Button(item);
28248         }
28249         var td = document.createElement("td");
28250         this.tr.insertBefore(td, this.tr.childNodes[index]);
28251         item.render(td);
28252         this.items.insert(index, item);
28253         return item;
28254     },
28255     
28256     /**
28257      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28258      * @param {Object} config
28259      * @return {Roo.Toolbar.Item} The element's item
28260      */
28261     addDom : function(config, returnEl){
28262         var td = this.nextBlock();
28263         Roo.DomHelper.overwrite(td, config);
28264         var ti = new Roo.Toolbar.Item(td.firstChild);
28265         ti.render(td);
28266         this.items.add(ti);
28267         return ti;
28268     },
28269
28270     /**
28271      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28272      * @type Roo.util.MixedCollection  
28273      */
28274     fields : false,
28275     
28276     /**
28277      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28278      * Note: the field should not have been rendered yet. For a field that has already been
28279      * rendered, use {@link #addElement}.
28280      * @param {Roo.form.Field} field
28281      * @return {Roo.ToolbarItem}
28282      */
28283      
28284       
28285     addField : function(field) {
28286         if (!this.fields) {
28287             var autoId = 0;
28288             this.fields = new Roo.util.MixedCollection(false, function(o){
28289                 return o.id || ("item" + (++autoId));
28290             });
28291
28292         }
28293         
28294         var td = this.nextBlock();
28295         field.render(td);
28296         var ti = new Roo.Toolbar.Item(td.firstChild);
28297         ti.render(td);
28298         this.items.add(ti);
28299         this.fields.add(field);
28300         return ti;
28301     },
28302     /**
28303      * Hide the toolbar
28304      * @method hide
28305      */
28306      
28307       
28308     hide : function()
28309     {
28310         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28311         this.el.child('div').hide();
28312     },
28313     /**
28314      * Show the toolbar
28315      * @method show
28316      */
28317     show : function()
28318     {
28319         this.el.child('div').show();
28320     },
28321       
28322     // private
28323     nextBlock : function(){
28324         var td = document.createElement("td");
28325         this.tr.appendChild(td);
28326         return td;
28327     },
28328
28329     // private
28330     destroy : function(){
28331         if(this.items){ // rendered?
28332             Roo.destroy.apply(Roo, this.items.items);
28333         }
28334         if(this.fields){ // rendered?
28335             Roo.destroy.apply(Roo, this.fields.items);
28336         }
28337         Roo.Element.uncache(this.el, this.tr);
28338     }
28339 };
28340
28341 /**
28342  * @class Roo.Toolbar.Item
28343  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28344  * @constructor
28345  * Creates a new Item
28346  * @param {HTMLElement} el 
28347  */
28348 Roo.Toolbar.Item = function(el){
28349     this.el = Roo.getDom(el);
28350     this.id = Roo.id(this.el);
28351     this.hidden = false;
28352 };
28353
28354 Roo.Toolbar.Item.prototype = {
28355     
28356     /**
28357      * Get this item's HTML Element
28358      * @return {HTMLElement}
28359      */
28360     getEl : function(){
28361        return this.el;  
28362     },
28363
28364     // private
28365     render : function(td){
28366         this.td = td;
28367         td.appendChild(this.el);
28368     },
28369     
28370     /**
28371      * Removes and destroys this item.
28372      */
28373     destroy : function(){
28374         this.td.parentNode.removeChild(this.td);
28375     },
28376     
28377     /**
28378      * Shows this item.
28379      */
28380     show: function(){
28381         this.hidden = false;
28382         this.td.style.display = "";
28383     },
28384     
28385     /**
28386      * Hides this item.
28387      */
28388     hide: function(){
28389         this.hidden = true;
28390         this.td.style.display = "none";
28391     },
28392     
28393     /**
28394      * Convenience function for boolean show/hide.
28395      * @param {Boolean} visible true to show/false to hide
28396      */
28397     setVisible: function(visible){
28398         if(visible) {
28399             this.show();
28400         }else{
28401             this.hide();
28402         }
28403     },
28404     
28405     /**
28406      * Try to focus this item.
28407      */
28408     focus : function(){
28409         Roo.fly(this.el).focus();
28410     },
28411     
28412     /**
28413      * Disables this item.
28414      */
28415     disable : function(){
28416         Roo.fly(this.td).addClass("x-item-disabled");
28417         this.disabled = true;
28418         this.el.disabled = true;
28419     },
28420     
28421     /**
28422      * Enables this item.
28423      */
28424     enable : function(){
28425         Roo.fly(this.td).removeClass("x-item-disabled");
28426         this.disabled = false;
28427         this.el.disabled = false;
28428     }
28429 };
28430
28431
28432 /**
28433  * @class Roo.Toolbar.Separator
28434  * @extends Roo.Toolbar.Item
28435  * A simple toolbar separator class
28436  * @constructor
28437  * Creates a new Separator
28438  */
28439 Roo.Toolbar.Separator = function(){
28440     var s = document.createElement("span");
28441     s.className = "ytb-sep";
28442     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28443 };
28444 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28445     enable:Roo.emptyFn,
28446     disable:Roo.emptyFn,
28447     focus:Roo.emptyFn
28448 });
28449
28450 /**
28451  * @class Roo.Toolbar.Spacer
28452  * @extends Roo.Toolbar.Item
28453  * A simple element that adds extra horizontal space to a toolbar.
28454  * @constructor
28455  * Creates a new Spacer
28456  */
28457 Roo.Toolbar.Spacer = function(){
28458     var s = document.createElement("div");
28459     s.className = "ytb-spacer";
28460     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28461 };
28462 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28463     enable:Roo.emptyFn,
28464     disable:Roo.emptyFn,
28465     focus:Roo.emptyFn
28466 });
28467
28468 /**
28469  * @class Roo.Toolbar.Fill
28470  * @extends Roo.Toolbar.Spacer
28471  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28472  * @constructor
28473  * Creates a new Spacer
28474  */
28475 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28476     // private
28477     render : function(td){
28478         td.style.width = '100%';
28479         Roo.Toolbar.Fill.superclass.render.call(this, td);
28480     }
28481 });
28482
28483 /**
28484  * @class Roo.Toolbar.TextItem
28485  * @extends Roo.Toolbar.Item
28486  * A simple class that renders text directly into a toolbar.
28487  * @constructor
28488  * Creates a new TextItem
28489  * @param {String} text
28490  */
28491 Roo.Toolbar.TextItem = function(text){
28492     if (typeof(text) == 'object') {
28493         text = text.text;
28494     }
28495     var s = document.createElement("span");
28496     s.className = "ytb-text";
28497     s.innerHTML = text;
28498     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28499 };
28500 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28501     enable:Roo.emptyFn,
28502     disable:Roo.emptyFn,
28503     focus:Roo.emptyFn
28504 });
28505
28506 /**
28507  * @class Roo.Toolbar.Button
28508  * @extends Roo.Button
28509  * A button that renders into a toolbar.
28510  * @constructor
28511  * Creates a new Button
28512  * @param {Object} config A standard {@link Roo.Button} config object
28513  */
28514 Roo.Toolbar.Button = function(config){
28515     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28516 };
28517 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28518     render : function(td){
28519         this.td = td;
28520         Roo.Toolbar.Button.superclass.render.call(this, td);
28521     },
28522     
28523     /**
28524      * Removes and destroys this button
28525      */
28526     destroy : function(){
28527         Roo.Toolbar.Button.superclass.destroy.call(this);
28528         this.td.parentNode.removeChild(this.td);
28529     },
28530     
28531     /**
28532      * Shows this button
28533      */
28534     show: function(){
28535         this.hidden = false;
28536         this.td.style.display = "";
28537     },
28538     
28539     /**
28540      * Hides this button
28541      */
28542     hide: function(){
28543         this.hidden = true;
28544         this.td.style.display = "none";
28545     },
28546
28547     /**
28548      * Disables this item
28549      */
28550     disable : function(){
28551         Roo.fly(this.td).addClass("x-item-disabled");
28552         this.disabled = true;
28553     },
28554
28555     /**
28556      * Enables this item
28557      */
28558     enable : function(){
28559         Roo.fly(this.td).removeClass("x-item-disabled");
28560         this.disabled = false;
28561     }
28562 });
28563 // backwards compat
28564 Roo.ToolbarButton = Roo.Toolbar.Button;
28565
28566 /**
28567  * @class Roo.Toolbar.SplitButton
28568  * @extends Roo.SplitButton
28569  * A menu button that renders into a toolbar.
28570  * @constructor
28571  * Creates a new SplitButton
28572  * @param {Object} config A standard {@link Roo.SplitButton} config object
28573  */
28574 Roo.Toolbar.SplitButton = function(config){
28575     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28576 };
28577 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28578     render : function(td){
28579         this.td = td;
28580         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28581     },
28582     
28583     /**
28584      * Removes and destroys this button
28585      */
28586     destroy : function(){
28587         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28588         this.td.parentNode.removeChild(this.td);
28589     },
28590     
28591     /**
28592      * Shows this button
28593      */
28594     show: function(){
28595         this.hidden = false;
28596         this.td.style.display = "";
28597     },
28598     
28599     /**
28600      * Hides this button
28601      */
28602     hide: function(){
28603         this.hidden = true;
28604         this.td.style.display = "none";
28605     }
28606 });
28607
28608 // backwards compat
28609 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28610  * Based on:
28611  * Ext JS Library 1.1.1
28612  * Copyright(c) 2006-2007, Ext JS, LLC.
28613  *
28614  * Originally Released Under LGPL - original licence link has changed is not relivant.
28615  *
28616  * Fork - LGPL
28617  * <script type="text/javascript">
28618  */
28619  
28620 /**
28621  * @class Roo.PagingToolbar
28622  * @extends Roo.Toolbar
28623  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28624  * @constructor
28625  * Create a new PagingToolbar
28626  * @param {Object} config The config object
28627  */
28628 Roo.PagingToolbar = function(el, ds, config)
28629 {
28630     // old args format still supported... - xtype is prefered..
28631     if (typeof(el) == 'object' && el.xtype) {
28632         // created from xtype...
28633         config = el;
28634         ds = el.dataSource;
28635         el = config.container;
28636     }
28637     var items = [];
28638     if (config.items) {
28639         items = config.items;
28640         config.items = [];
28641     }
28642     
28643     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28644     this.ds = ds;
28645     this.cursor = 0;
28646     this.renderButtons(this.el);
28647     this.bind(ds);
28648     
28649     // supprot items array.
28650    
28651     Roo.each(items, function(e) {
28652         this.add(Roo.factory(e));
28653     },this);
28654     
28655 };
28656
28657 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28658     /**
28659      * @cfg {Roo.data.Store} dataSource
28660      * The underlying data store providing the paged data
28661      */
28662     /**
28663      * @cfg {String/HTMLElement/Element} container
28664      * container The id or element that will contain the toolbar
28665      */
28666     /**
28667      * @cfg {Boolean} displayInfo
28668      * True to display the displayMsg (defaults to false)
28669      */
28670     /**
28671      * @cfg {Number} pageSize
28672      * The number of records to display per page (defaults to 20)
28673      */
28674     pageSize: 20,
28675     /**
28676      * @cfg {String} displayMsg
28677      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28678      */
28679     displayMsg : 'Displaying {0} - {1} of {2}',
28680     /**
28681      * @cfg {String} emptyMsg
28682      * The message to display when no records are found (defaults to "No data to display")
28683      */
28684     emptyMsg : 'No data to display',
28685     /**
28686      * Customizable piece of the default paging text (defaults to "Page")
28687      * @type String
28688      */
28689     beforePageText : "Page",
28690     /**
28691      * Customizable piece of the default paging text (defaults to "of %0")
28692      * @type String
28693      */
28694     afterPageText : "of {0}",
28695     /**
28696      * Customizable piece of the default paging text (defaults to "First Page")
28697      * @type String
28698      */
28699     firstText : "First Page",
28700     /**
28701      * Customizable piece of the default paging text (defaults to "Previous Page")
28702      * @type String
28703      */
28704     prevText : "Previous Page",
28705     /**
28706      * Customizable piece of the default paging text (defaults to "Next Page")
28707      * @type String
28708      */
28709     nextText : "Next Page",
28710     /**
28711      * Customizable piece of the default paging text (defaults to "Last Page")
28712      * @type String
28713      */
28714     lastText : "Last Page",
28715     /**
28716      * Customizable piece of the default paging text (defaults to "Refresh")
28717      * @type String
28718      */
28719     refreshText : "Refresh",
28720
28721     // private
28722     renderButtons : function(el){
28723         Roo.PagingToolbar.superclass.render.call(this, el);
28724         this.first = this.addButton({
28725             tooltip: this.firstText,
28726             cls: "x-btn-icon x-grid-page-first",
28727             disabled: true,
28728             handler: this.onClick.createDelegate(this, ["first"])
28729         });
28730         this.prev = this.addButton({
28731             tooltip: this.prevText,
28732             cls: "x-btn-icon x-grid-page-prev",
28733             disabled: true,
28734             handler: this.onClick.createDelegate(this, ["prev"])
28735         });
28736         //this.addSeparator();
28737         this.add(this.beforePageText);
28738         this.field = Roo.get(this.addDom({
28739            tag: "input",
28740            type: "text",
28741            size: "3",
28742            value: "1",
28743            cls: "x-grid-page-number"
28744         }).el);
28745         this.field.on("keydown", this.onPagingKeydown, this);
28746         this.field.on("focus", function(){this.dom.select();});
28747         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28748         this.field.setHeight(18);
28749         //this.addSeparator();
28750         this.next = this.addButton({
28751             tooltip: this.nextText,
28752             cls: "x-btn-icon x-grid-page-next",
28753             disabled: true,
28754             handler: this.onClick.createDelegate(this, ["next"])
28755         });
28756         this.last = this.addButton({
28757             tooltip: this.lastText,
28758             cls: "x-btn-icon x-grid-page-last",
28759             disabled: true,
28760             handler: this.onClick.createDelegate(this, ["last"])
28761         });
28762         //this.addSeparator();
28763         this.loading = this.addButton({
28764             tooltip: this.refreshText,
28765             cls: "x-btn-icon x-grid-loading",
28766             handler: this.onClick.createDelegate(this, ["refresh"])
28767         });
28768
28769         if(this.displayInfo){
28770             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28771         }
28772     },
28773
28774     // private
28775     updateInfo : function(){
28776         if(this.displayEl){
28777             var count = this.ds.getCount();
28778             var msg = count == 0 ?
28779                 this.emptyMsg :
28780                 String.format(
28781                     this.displayMsg,
28782                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28783                 );
28784             this.displayEl.update(msg);
28785         }
28786     },
28787
28788     // private
28789     onLoad : function(ds, r, o){
28790        this.cursor = o.params ? o.params.start : 0;
28791        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28792
28793        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28794        this.field.dom.value = ap;
28795        this.first.setDisabled(ap == 1);
28796        this.prev.setDisabled(ap == 1);
28797        this.next.setDisabled(ap == ps);
28798        this.last.setDisabled(ap == ps);
28799        this.loading.enable();
28800        this.updateInfo();
28801     },
28802
28803     // private
28804     getPageData : function(){
28805         var total = this.ds.getTotalCount();
28806         return {
28807             total : total,
28808             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28809             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28810         };
28811     },
28812
28813     // private
28814     onLoadError : function(){
28815         this.loading.enable();
28816     },
28817
28818     // private
28819     onPagingKeydown : function(e){
28820         var k = e.getKey();
28821         var d = this.getPageData();
28822         if(k == e.RETURN){
28823             var v = this.field.dom.value, pageNum;
28824             if(!v || isNaN(pageNum = parseInt(v, 10))){
28825                 this.field.dom.value = d.activePage;
28826                 return;
28827             }
28828             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28829             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28830             e.stopEvent();
28831         }
28832         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))
28833         {
28834           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28835           this.field.dom.value = pageNum;
28836           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28837           e.stopEvent();
28838         }
28839         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28840         {
28841           var v = this.field.dom.value, pageNum; 
28842           var increment = (e.shiftKey) ? 10 : 1;
28843           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28844             increment *= -1;
28845           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28846             this.field.dom.value = d.activePage;
28847             return;
28848           }
28849           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28850           {
28851             this.field.dom.value = parseInt(v, 10) + increment;
28852             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28853             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28854           }
28855           e.stopEvent();
28856         }
28857     },
28858
28859     // private
28860     beforeLoad : function(){
28861         if(this.loading){
28862             this.loading.disable();
28863         }
28864     },
28865
28866     // private
28867     onClick : function(which){
28868         var ds = this.ds;
28869         switch(which){
28870             case "first":
28871                 ds.load({params:{start: 0, limit: this.pageSize}});
28872             break;
28873             case "prev":
28874                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28875             break;
28876             case "next":
28877                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28878             break;
28879             case "last":
28880                 var total = ds.getTotalCount();
28881                 var extra = total % this.pageSize;
28882                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28883                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28884             break;
28885             case "refresh":
28886                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28887             break;
28888         }
28889     },
28890
28891     /**
28892      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28893      * @param {Roo.data.Store} store The data store to unbind
28894      */
28895     unbind : function(ds){
28896         ds.un("beforeload", this.beforeLoad, this);
28897         ds.un("load", this.onLoad, this);
28898         ds.un("loadexception", this.onLoadError, this);
28899         ds.un("remove", this.updateInfo, this);
28900         ds.un("add", this.updateInfo, this);
28901         this.ds = undefined;
28902     },
28903
28904     /**
28905      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28906      * @param {Roo.data.Store} store The data store to bind
28907      */
28908     bind : function(ds){
28909         ds.on("beforeload", this.beforeLoad, this);
28910         ds.on("load", this.onLoad, this);
28911         ds.on("loadexception", this.onLoadError, this);
28912         ds.on("remove", this.updateInfo, this);
28913         ds.on("add", this.updateInfo, this);
28914         this.ds = ds;
28915     }
28916 });/*
28917  * Based on:
28918  * Ext JS Library 1.1.1
28919  * Copyright(c) 2006-2007, Ext JS, LLC.
28920  *
28921  * Originally Released Under LGPL - original licence link has changed is not relivant.
28922  *
28923  * Fork - LGPL
28924  * <script type="text/javascript">
28925  */
28926
28927 /**
28928  * @class Roo.Resizable
28929  * @extends Roo.util.Observable
28930  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28931  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28932  * 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
28933  * the element will be wrapped for you automatically.</p>
28934  * <p>Here is the list of valid resize handles:</p>
28935  * <pre>
28936 Value   Description
28937 ------  -------------------
28938  'n'     north
28939  's'     south
28940  'e'     east
28941  'w'     west
28942  'nw'    northwest
28943  'sw'    southwest
28944  'se'    southeast
28945  'ne'    northeast
28946  'hd'    horizontal drag
28947  'all'   all
28948 </pre>
28949  * <p>Here's an example showing the creation of a typical Resizable:</p>
28950  * <pre><code>
28951 var resizer = new Roo.Resizable("element-id", {
28952     handles: 'all',
28953     minWidth: 200,
28954     minHeight: 100,
28955     maxWidth: 500,
28956     maxHeight: 400,
28957     pinned: true
28958 });
28959 resizer.on("resize", myHandler);
28960 </code></pre>
28961  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28962  * resizer.east.setDisplayed(false);</p>
28963  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28964  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28965  * resize operation's new size (defaults to [0, 0])
28966  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28967  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28968  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28969  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28970  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28971  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28972  * @cfg {Number} width The width of the element in pixels (defaults to null)
28973  * @cfg {Number} height The height of the element in pixels (defaults to null)
28974  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28975  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28976  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28977  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28978  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28979  * in favor of the handles config option (defaults to false)
28980  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28981  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28982  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28983  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28984  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28985  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28986  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28987  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28988  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28989  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28990  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28991  * @constructor
28992  * Create a new resizable component
28993  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28994  * @param {Object} config configuration options
28995   */
28996 Roo.Resizable = function(el, config)
28997 {
28998     this.el = Roo.get(el);
28999
29000     if(config && config.wrap){
29001         config.resizeChild = this.el;
29002         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29003         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29004         this.el.setStyle("overflow", "hidden");
29005         this.el.setPositioning(config.resizeChild.getPositioning());
29006         config.resizeChild.clearPositioning();
29007         if(!config.width || !config.height){
29008             var csize = config.resizeChild.getSize();
29009             this.el.setSize(csize.width, csize.height);
29010         }
29011         if(config.pinned && !config.adjustments){
29012             config.adjustments = "auto";
29013         }
29014     }
29015
29016     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29017     this.proxy.unselectable();
29018     this.proxy.enableDisplayMode('block');
29019
29020     Roo.apply(this, config);
29021
29022     if(this.pinned){
29023         this.disableTrackOver = true;
29024         this.el.addClass("x-resizable-pinned");
29025     }
29026     // if the element isn't positioned, make it relative
29027     var position = this.el.getStyle("position");
29028     if(position != "absolute" && position != "fixed"){
29029         this.el.setStyle("position", "relative");
29030     }
29031     if(!this.handles){ // no handles passed, must be legacy style
29032         this.handles = 's,e,se';
29033         if(this.multiDirectional){
29034             this.handles += ',n,w';
29035         }
29036     }
29037     if(this.handles == "all"){
29038         this.handles = "n s e w ne nw se sw";
29039     }
29040     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29041     var ps = Roo.Resizable.positions;
29042     for(var i = 0, len = hs.length; i < len; i++){
29043         if(hs[i] && ps[hs[i]]){
29044             var pos = ps[hs[i]];
29045             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29046         }
29047     }
29048     // legacy
29049     this.corner = this.southeast;
29050     
29051     // updateBox = the box can move..
29052     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29053         this.updateBox = true;
29054     }
29055
29056     this.activeHandle = null;
29057
29058     if(this.resizeChild){
29059         if(typeof this.resizeChild == "boolean"){
29060             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29061         }else{
29062             this.resizeChild = Roo.get(this.resizeChild, true);
29063         }
29064     }
29065     
29066     if(this.adjustments == "auto"){
29067         var rc = this.resizeChild;
29068         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29069         if(rc && (hw || hn)){
29070             rc.position("relative");
29071             rc.setLeft(hw ? hw.el.getWidth() : 0);
29072             rc.setTop(hn ? hn.el.getHeight() : 0);
29073         }
29074         this.adjustments = [
29075             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29076             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29077         ];
29078     }
29079
29080     if(this.draggable){
29081         this.dd = this.dynamic ?
29082             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29083         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29084     }
29085
29086     // public events
29087     this.addEvents({
29088         /**
29089          * @event beforeresize
29090          * Fired before resize is allowed. Set enabled to false to cancel resize.
29091          * @param {Roo.Resizable} this
29092          * @param {Roo.EventObject} e The mousedown event
29093          */
29094         "beforeresize" : true,
29095         /**
29096          * @event resizing
29097          * Fired a resizing.
29098          * @param {Roo.Resizable} this
29099          * @param {Number} x The new x position
29100          * @param {Number} y The new y position
29101          * @param {Number} w The new w width
29102          * @param {Number} h The new h hight
29103          * @param {Roo.EventObject} e The mouseup event
29104          */
29105         "resizing" : true,
29106         /**
29107          * @event resize
29108          * Fired after a resize.
29109          * @param {Roo.Resizable} this
29110          * @param {Number} width The new width
29111          * @param {Number} height The new height
29112          * @param {Roo.EventObject} e The mouseup event
29113          */
29114         "resize" : true
29115     });
29116
29117     if(this.width !== null && this.height !== null){
29118         this.resizeTo(this.width, this.height);
29119     }else{
29120         this.updateChildSize();
29121     }
29122     if(Roo.isIE){
29123         this.el.dom.style.zoom = 1;
29124     }
29125     Roo.Resizable.superclass.constructor.call(this);
29126 };
29127
29128 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29129         resizeChild : false,
29130         adjustments : [0, 0],
29131         minWidth : 5,
29132         minHeight : 5,
29133         maxWidth : 10000,
29134         maxHeight : 10000,
29135         enabled : true,
29136         animate : false,
29137         duration : .35,
29138         dynamic : false,
29139         handles : false,
29140         multiDirectional : false,
29141         disableTrackOver : false,
29142         easing : 'easeOutStrong',
29143         widthIncrement : 0,
29144         heightIncrement : 0,
29145         pinned : false,
29146         width : null,
29147         height : null,
29148         preserveRatio : false,
29149         transparent: false,
29150         minX: 0,
29151         minY: 0,
29152         draggable: false,
29153
29154         /**
29155          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29156          */
29157         constrainTo: undefined,
29158         /**
29159          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29160          */
29161         resizeRegion: undefined,
29162
29163
29164     /**
29165      * Perform a manual resize
29166      * @param {Number} width
29167      * @param {Number} height
29168      */
29169     resizeTo : function(width, height){
29170         this.el.setSize(width, height);
29171         this.updateChildSize();
29172         this.fireEvent("resize", this, width, height, null);
29173     },
29174
29175     // private
29176     startSizing : function(e, handle){
29177         this.fireEvent("beforeresize", this, e);
29178         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29179
29180             if(!this.overlay){
29181                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29182                 this.overlay.unselectable();
29183                 this.overlay.enableDisplayMode("block");
29184                 this.overlay.on("mousemove", this.onMouseMove, this);
29185                 this.overlay.on("mouseup", this.onMouseUp, this);
29186             }
29187             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29188
29189             this.resizing = true;
29190             this.startBox = this.el.getBox();
29191             this.startPoint = e.getXY();
29192             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29193                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29194
29195             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29196             this.overlay.show();
29197
29198             if(this.constrainTo) {
29199                 var ct = Roo.get(this.constrainTo);
29200                 this.resizeRegion = ct.getRegion().adjust(
29201                     ct.getFrameWidth('t'),
29202                     ct.getFrameWidth('l'),
29203                     -ct.getFrameWidth('b'),
29204                     -ct.getFrameWidth('r')
29205                 );
29206             }
29207
29208             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29209             this.proxy.show();
29210             this.proxy.setBox(this.startBox);
29211             if(!this.dynamic){
29212                 this.proxy.setStyle('visibility', 'visible');
29213             }
29214         }
29215     },
29216
29217     // private
29218     onMouseDown : function(handle, e){
29219         if(this.enabled){
29220             e.stopEvent();
29221             this.activeHandle = handle;
29222             this.startSizing(e, handle);
29223         }
29224     },
29225
29226     // private
29227     onMouseUp : function(e){
29228         var size = this.resizeElement();
29229         this.resizing = false;
29230         this.handleOut();
29231         this.overlay.hide();
29232         this.proxy.hide();
29233         this.fireEvent("resize", this, size.width, size.height, e);
29234     },
29235
29236     // private
29237     updateChildSize : function(){
29238         
29239         if(this.resizeChild){
29240             var el = this.el;
29241             var child = this.resizeChild;
29242             var adj = this.adjustments;
29243             if(el.dom.offsetWidth){
29244                 var b = el.getSize(true);
29245                 child.setSize(b.width+adj[0], b.height+adj[1]);
29246             }
29247             // Second call here for IE
29248             // The first call enables instant resizing and
29249             // the second call corrects scroll bars if they
29250             // exist
29251             if(Roo.isIE){
29252                 setTimeout(function(){
29253                     if(el.dom.offsetWidth){
29254                         var b = el.getSize(true);
29255                         child.setSize(b.width+adj[0], b.height+adj[1]);
29256                     }
29257                 }, 10);
29258             }
29259         }
29260     },
29261
29262     // private
29263     snap : function(value, inc, min){
29264         if(!inc || !value) return value;
29265         var newValue = value;
29266         var m = value % inc;
29267         if(m > 0){
29268             if(m > (inc/2)){
29269                 newValue = value + (inc-m);
29270             }else{
29271                 newValue = value - m;
29272             }
29273         }
29274         return Math.max(min, newValue);
29275     },
29276
29277     // private
29278     resizeElement : function(){
29279         var box = this.proxy.getBox();
29280         if(this.updateBox){
29281             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29282         }else{
29283             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29284         }
29285         this.updateChildSize();
29286         if(!this.dynamic){
29287             this.proxy.hide();
29288         }
29289         return box;
29290     },
29291
29292     // private
29293     constrain : function(v, diff, m, mx){
29294         if(v - diff < m){
29295             diff = v - m;
29296         }else if(v - diff > mx){
29297             diff = mx - v;
29298         }
29299         return diff;
29300     },
29301
29302     // private
29303     onMouseMove : function(e){
29304         
29305         if(this.enabled){
29306             try{// try catch so if something goes wrong the user doesn't get hung
29307
29308             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29309                 return;
29310             }
29311
29312             //var curXY = this.startPoint;
29313             var curSize = this.curSize || this.startBox;
29314             var x = this.startBox.x, y = this.startBox.y;
29315             var ox = x, oy = y;
29316             var w = curSize.width, h = curSize.height;
29317             var ow = w, oh = h;
29318             var mw = this.minWidth, mh = this.minHeight;
29319             var mxw = this.maxWidth, mxh = this.maxHeight;
29320             var wi = this.widthIncrement;
29321             var hi = this.heightIncrement;
29322
29323             var eventXY = e.getXY();
29324             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29325             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29326
29327             var pos = this.activeHandle.position;
29328
29329             switch(pos){
29330                 case "east":
29331                     w += diffX;
29332                     w = Math.min(Math.max(mw, w), mxw);
29333                     break;
29334              
29335                 case "south":
29336                     h += diffY;
29337                     h = Math.min(Math.max(mh, h), mxh);
29338                     break;
29339                 case "southeast":
29340                     w += diffX;
29341                     h += diffY;
29342                     w = Math.min(Math.max(mw, w), mxw);
29343                     h = Math.min(Math.max(mh, h), mxh);
29344                     break;
29345                 case "north":
29346                     diffY = this.constrain(h, diffY, mh, mxh);
29347                     y += diffY;
29348                     h -= diffY;
29349                     break;
29350                 case "hdrag":
29351                     
29352                     if (wi) {
29353                         var adiffX = Math.abs(diffX);
29354                         var sub = (adiffX % wi); // how much 
29355                         if (sub > (wi/2)) { // far enough to snap
29356                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29357                         } else {
29358                             // remove difference.. 
29359                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29360                         }
29361                     }
29362                     x += diffX;
29363                     x = Math.max(this.minX, x);
29364                     break;
29365                 case "west":
29366                     diffX = this.constrain(w, diffX, mw, mxw);
29367                     x += diffX;
29368                     w -= diffX;
29369                     break;
29370                 case "northeast":
29371                     w += diffX;
29372                     w = Math.min(Math.max(mw, w), mxw);
29373                     diffY = this.constrain(h, diffY, mh, mxh);
29374                     y += diffY;
29375                     h -= diffY;
29376                     break;
29377                 case "northwest":
29378                     diffX = this.constrain(w, diffX, mw, mxw);
29379                     diffY = this.constrain(h, diffY, mh, mxh);
29380                     y += diffY;
29381                     h -= diffY;
29382                     x += diffX;
29383                     w -= diffX;
29384                     break;
29385                case "southwest":
29386                     diffX = this.constrain(w, diffX, mw, mxw);
29387                     h += diffY;
29388                     h = Math.min(Math.max(mh, h), mxh);
29389                     x += diffX;
29390                     w -= diffX;
29391                     break;
29392             }
29393
29394             var sw = this.snap(w, wi, mw);
29395             var sh = this.snap(h, hi, mh);
29396             if(sw != w || sh != h){
29397                 switch(pos){
29398                     case "northeast":
29399                         y -= sh - h;
29400                     break;
29401                     case "north":
29402                         y -= sh - h;
29403                         break;
29404                     case "southwest":
29405                         x -= sw - w;
29406                     break;
29407                     case "west":
29408                         x -= sw - w;
29409                         break;
29410                     case "northwest":
29411                         x -= sw - w;
29412                         y -= sh - h;
29413                     break;
29414                 }
29415                 w = sw;
29416                 h = sh;
29417             }
29418
29419             if(this.preserveRatio){
29420                 switch(pos){
29421                     case "southeast":
29422                     case "east":
29423                         h = oh * (w/ow);
29424                         h = Math.min(Math.max(mh, h), mxh);
29425                         w = ow * (h/oh);
29426                        break;
29427                     case "south":
29428                         w = ow * (h/oh);
29429                         w = Math.min(Math.max(mw, w), mxw);
29430                         h = oh * (w/ow);
29431                         break;
29432                     case "northeast":
29433                         w = ow * (h/oh);
29434                         w = Math.min(Math.max(mw, w), mxw);
29435                         h = oh * (w/ow);
29436                     break;
29437                     case "north":
29438                         var tw = w;
29439                         w = ow * (h/oh);
29440                         w = Math.min(Math.max(mw, w), mxw);
29441                         h = oh * (w/ow);
29442                         x += (tw - w) / 2;
29443                         break;
29444                     case "southwest":
29445                         h = oh * (w/ow);
29446                         h = Math.min(Math.max(mh, h), mxh);
29447                         var tw = w;
29448                         w = ow * (h/oh);
29449                         x += tw - w;
29450                         break;
29451                     case "west":
29452                         var th = h;
29453                         h = oh * (w/ow);
29454                         h = Math.min(Math.max(mh, h), mxh);
29455                         y += (th - h) / 2;
29456                         var tw = w;
29457                         w = ow * (h/oh);
29458                         x += tw - w;
29459                        break;
29460                     case "northwest":
29461                         var tw = w;
29462                         var th = h;
29463                         h = oh * (w/ow);
29464                         h = Math.min(Math.max(mh, h), mxh);
29465                         w = ow * (h/oh);
29466                         y += th - h;
29467                         x += tw - w;
29468                        break;
29469
29470                 }
29471             }
29472             if (pos == 'hdrag') {
29473                 w = ow;
29474             }
29475             this.proxy.setBounds(x, y, w, h);
29476             if(this.dynamic){
29477                 this.resizeElement();
29478             }
29479             }catch(e){}
29480         }
29481         this.fireEvent("resizing", this, x, y, w, h, e);
29482     },
29483
29484     // private
29485     handleOver : function(){
29486         if(this.enabled){
29487             this.el.addClass("x-resizable-over");
29488         }
29489     },
29490
29491     // private
29492     handleOut : function(){
29493         if(!this.resizing){
29494             this.el.removeClass("x-resizable-over");
29495         }
29496     },
29497
29498     /**
29499      * Returns the element this component is bound to.
29500      * @return {Roo.Element}
29501      */
29502     getEl : function(){
29503         return this.el;
29504     },
29505
29506     /**
29507      * Returns the resizeChild element (or null).
29508      * @return {Roo.Element}
29509      */
29510     getResizeChild : function(){
29511         return this.resizeChild;
29512     },
29513     groupHandler : function()
29514     {
29515         
29516     },
29517     /**
29518      * Destroys this resizable. If the element was wrapped and
29519      * removeEl is not true then the element remains.
29520      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29521      */
29522     destroy : function(removeEl){
29523         this.proxy.remove();
29524         if(this.overlay){
29525             this.overlay.removeAllListeners();
29526             this.overlay.remove();
29527         }
29528         var ps = Roo.Resizable.positions;
29529         for(var k in ps){
29530             if(typeof ps[k] != "function" && this[ps[k]]){
29531                 var h = this[ps[k]];
29532                 h.el.removeAllListeners();
29533                 h.el.remove();
29534             }
29535         }
29536         if(removeEl){
29537             this.el.update("");
29538             this.el.remove();
29539         }
29540     }
29541 });
29542
29543 // private
29544 // hash to map config positions to true positions
29545 Roo.Resizable.positions = {
29546     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29547     hd: "hdrag"
29548 };
29549
29550 // private
29551 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29552     if(!this.tpl){
29553         // only initialize the template if resizable is used
29554         var tpl = Roo.DomHelper.createTemplate(
29555             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29556         );
29557         tpl.compile();
29558         Roo.Resizable.Handle.prototype.tpl = tpl;
29559     }
29560     this.position = pos;
29561     this.rz = rz;
29562     // show north drag fro topdra
29563     var handlepos = pos == 'hdrag' ? 'north' : pos;
29564     
29565     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29566     if (pos == 'hdrag') {
29567         this.el.setStyle('cursor', 'pointer');
29568     }
29569     this.el.unselectable();
29570     if(transparent){
29571         this.el.setOpacity(0);
29572     }
29573     this.el.on("mousedown", this.onMouseDown, this);
29574     if(!disableTrackOver){
29575         this.el.on("mouseover", this.onMouseOver, this);
29576         this.el.on("mouseout", this.onMouseOut, this);
29577     }
29578 };
29579
29580 // private
29581 Roo.Resizable.Handle.prototype = {
29582     afterResize : function(rz){
29583         Roo.log('after?');
29584         // do nothing
29585     },
29586     // private
29587     onMouseDown : function(e){
29588         this.rz.onMouseDown(this, e);
29589     },
29590     // private
29591     onMouseOver : function(e){
29592         this.rz.handleOver(this, e);
29593     },
29594     // private
29595     onMouseOut : function(e){
29596         this.rz.handleOut(this, e);
29597     }
29598 };/*
29599  * Based on:
29600  * Ext JS Library 1.1.1
29601  * Copyright(c) 2006-2007, Ext JS, LLC.
29602  *
29603  * Originally Released Under LGPL - original licence link has changed is not relivant.
29604  *
29605  * Fork - LGPL
29606  * <script type="text/javascript">
29607  */
29608
29609 /**
29610  * @class Roo.Editor
29611  * @extends Roo.Component
29612  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29613  * @constructor
29614  * Create a new Editor
29615  * @param {Roo.form.Field} field The Field object (or descendant)
29616  * @param {Object} config The config object
29617  */
29618 Roo.Editor = function(field, config){
29619     Roo.Editor.superclass.constructor.call(this, config);
29620     this.field = field;
29621     this.addEvents({
29622         /**
29623              * @event beforestartedit
29624              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29625              * false from the handler of this event.
29626              * @param {Editor} this
29627              * @param {Roo.Element} boundEl The underlying element bound to this editor
29628              * @param {Mixed} value The field value being set
29629              */
29630         "beforestartedit" : true,
29631         /**
29632              * @event startedit
29633              * Fires when this editor is displayed
29634              * @param {Roo.Element} boundEl The underlying element bound to this editor
29635              * @param {Mixed} value The starting field value
29636              */
29637         "startedit" : true,
29638         /**
29639              * @event beforecomplete
29640              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29641              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29642              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29643              * event will not fire since no edit actually occurred.
29644              * @param {Editor} this
29645              * @param {Mixed} value The current field value
29646              * @param {Mixed} startValue The original field value
29647              */
29648         "beforecomplete" : true,
29649         /**
29650              * @event complete
29651              * Fires after editing is complete and any changed value has been written to the underlying field.
29652              * @param {Editor} this
29653              * @param {Mixed} value The current field value
29654              * @param {Mixed} startValue The original field value
29655              */
29656         "complete" : true,
29657         /**
29658          * @event specialkey
29659          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29660          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29661          * @param {Roo.form.Field} this
29662          * @param {Roo.EventObject} e The event object
29663          */
29664         "specialkey" : true
29665     });
29666 };
29667
29668 Roo.extend(Roo.Editor, Roo.Component, {
29669     /**
29670      * @cfg {Boolean/String} autosize
29671      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29672      * or "height" to adopt the height only (defaults to false)
29673      */
29674     /**
29675      * @cfg {Boolean} revertInvalid
29676      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29677      * validation fails (defaults to true)
29678      */
29679     /**
29680      * @cfg {Boolean} ignoreNoChange
29681      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29682      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29683      * will never be ignored.
29684      */
29685     /**
29686      * @cfg {Boolean} hideEl
29687      * False to keep the bound element visible while the editor is displayed (defaults to true)
29688      */
29689     /**
29690      * @cfg {Mixed} value
29691      * The data value of the underlying field (defaults to "")
29692      */
29693     value : "",
29694     /**
29695      * @cfg {String} alignment
29696      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29697      */
29698     alignment: "c-c?",
29699     /**
29700      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29701      * for bottom-right shadow (defaults to "frame")
29702      */
29703     shadow : "frame",
29704     /**
29705      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29706      */
29707     constrain : false,
29708     /**
29709      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29710      */
29711     completeOnEnter : false,
29712     /**
29713      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29714      */
29715     cancelOnEsc : false,
29716     /**
29717      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29718      */
29719     updateEl : false,
29720
29721     // private
29722     onRender : function(ct, position){
29723         this.el = new Roo.Layer({
29724             shadow: this.shadow,
29725             cls: "x-editor",
29726             parentEl : ct,
29727             shim : this.shim,
29728             shadowOffset:4,
29729             id: this.id,
29730             constrain: this.constrain
29731         });
29732         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29733         if(this.field.msgTarget != 'title'){
29734             this.field.msgTarget = 'qtip';
29735         }
29736         this.field.render(this.el);
29737         if(Roo.isGecko){
29738             this.field.el.dom.setAttribute('autocomplete', 'off');
29739         }
29740         this.field.on("specialkey", this.onSpecialKey, this);
29741         if(this.swallowKeys){
29742             this.field.el.swallowEvent(['keydown','keypress']);
29743         }
29744         this.field.show();
29745         this.field.on("blur", this.onBlur, this);
29746         if(this.field.grow){
29747             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29748         }
29749     },
29750
29751     onSpecialKey : function(field, e)
29752     {
29753         //Roo.log('editor onSpecialKey');
29754         if(this.completeOnEnter && e.getKey() == e.ENTER){
29755             e.stopEvent();
29756             this.completeEdit();
29757             return;
29758         }
29759         // do not fire special key otherwise it might hide close the editor...
29760         if(e.getKey() == e.ENTER){    
29761             return;
29762         }
29763         if(this.cancelOnEsc && e.getKey() == e.ESC){
29764             this.cancelEdit();
29765             return;
29766         } 
29767         this.fireEvent('specialkey', field, e);
29768     
29769     },
29770
29771     /**
29772      * Starts the editing process and shows the editor.
29773      * @param {String/HTMLElement/Element} el The element to edit
29774      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29775       * to the innerHTML of el.
29776      */
29777     startEdit : function(el, value){
29778         if(this.editing){
29779             this.completeEdit();
29780         }
29781         this.boundEl = Roo.get(el);
29782         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29783         if(!this.rendered){
29784             this.render(this.parentEl || document.body);
29785         }
29786         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29787             return;
29788         }
29789         this.startValue = v;
29790         this.field.setValue(v);
29791         if(this.autoSize){
29792             var sz = this.boundEl.getSize();
29793             switch(this.autoSize){
29794                 case "width":
29795                 this.setSize(sz.width,  "");
29796                 break;
29797                 case "height":
29798                 this.setSize("",  sz.height);
29799                 break;
29800                 default:
29801                 this.setSize(sz.width,  sz.height);
29802             }
29803         }
29804         this.el.alignTo(this.boundEl, this.alignment);
29805         this.editing = true;
29806         if(Roo.QuickTips){
29807             Roo.QuickTips.disable();
29808         }
29809         this.show();
29810     },
29811
29812     /**
29813      * Sets the height and width of this editor.
29814      * @param {Number} width The new width
29815      * @param {Number} height The new height
29816      */
29817     setSize : function(w, h){
29818         this.field.setSize(w, h);
29819         if(this.el){
29820             this.el.sync();
29821         }
29822     },
29823
29824     /**
29825      * Realigns the editor to the bound field based on the current alignment config value.
29826      */
29827     realign : function(){
29828         this.el.alignTo(this.boundEl, this.alignment);
29829     },
29830
29831     /**
29832      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29833      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29834      */
29835     completeEdit : function(remainVisible){
29836         if(!this.editing){
29837             return;
29838         }
29839         var v = this.getValue();
29840         if(this.revertInvalid !== false && !this.field.isValid()){
29841             v = this.startValue;
29842             this.cancelEdit(true);
29843         }
29844         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29845             this.editing = false;
29846             this.hide();
29847             return;
29848         }
29849         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29850             this.editing = false;
29851             if(this.updateEl && this.boundEl){
29852                 this.boundEl.update(v);
29853             }
29854             if(remainVisible !== true){
29855                 this.hide();
29856             }
29857             this.fireEvent("complete", this, v, this.startValue);
29858         }
29859     },
29860
29861     // private
29862     onShow : function(){
29863         this.el.show();
29864         if(this.hideEl !== false){
29865             this.boundEl.hide();
29866         }
29867         this.field.show();
29868         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29869             this.fixIEFocus = true;
29870             this.deferredFocus.defer(50, this);
29871         }else{
29872             this.field.focus();
29873         }
29874         this.fireEvent("startedit", this.boundEl, this.startValue);
29875     },
29876
29877     deferredFocus : function(){
29878         if(this.editing){
29879             this.field.focus();
29880         }
29881     },
29882
29883     /**
29884      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29885      * reverted to the original starting value.
29886      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29887      * cancel (defaults to false)
29888      */
29889     cancelEdit : function(remainVisible){
29890         if(this.editing){
29891             this.setValue(this.startValue);
29892             if(remainVisible !== true){
29893                 this.hide();
29894             }
29895         }
29896     },
29897
29898     // private
29899     onBlur : function(){
29900         if(this.allowBlur !== true && this.editing){
29901             this.completeEdit();
29902         }
29903     },
29904
29905     // private
29906     onHide : function(){
29907         if(this.editing){
29908             this.completeEdit();
29909             return;
29910         }
29911         this.field.blur();
29912         if(this.field.collapse){
29913             this.field.collapse();
29914         }
29915         this.el.hide();
29916         if(this.hideEl !== false){
29917             this.boundEl.show();
29918         }
29919         if(Roo.QuickTips){
29920             Roo.QuickTips.enable();
29921         }
29922     },
29923
29924     /**
29925      * Sets the data value of the editor
29926      * @param {Mixed} value Any valid value supported by the underlying field
29927      */
29928     setValue : function(v){
29929         this.field.setValue(v);
29930     },
29931
29932     /**
29933      * Gets the data value of the editor
29934      * @return {Mixed} The data value
29935      */
29936     getValue : function(){
29937         return this.field.getValue();
29938     }
29939 });/*
29940  * Based on:
29941  * Ext JS Library 1.1.1
29942  * Copyright(c) 2006-2007, Ext JS, LLC.
29943  *
29944  * Originally Released Under LGPL - original licence link has changed is not relivant.
29945  *
29946  * Fork - LGPL
29947  * <script type="text/javascript">
29948  */
29949  
29950 /**
29951  * @class Roo.BasicDialog
29952  * @extends Roo.util.Observable
29953  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29954  * <pre><code>
29955 var dlg = new Roo.BasicDialog("my-dlg", {
29956     height: 200,
29957     width: 300,
29958     minHeight: 100,
29959     minWidth: 150,
29960     modal: true,
29961     proxyDrag: true,
29962     shadow: true
29963 });
29964 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29965 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29966 dlg.addButton('Cancel', dlg.hide, dlg);
29967 dlg.show();
29968 </code></pre>
29969   <b>A Dialog should always be a direct child of the body element.</b>
29970  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29971  * @cfg {String} title Default text to display in the title bar (defaults to null)
29972  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29973  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29974  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29975  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29976  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29977  * (defaults to null with no animation)
29978  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29979  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29980  * property for valid values (defaults to 'all')
29981  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29982  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29983  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29984  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29985  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29986  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29987  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29988  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29989  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29990  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29991  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29992  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29993  * draggable = true (defaults to false)
29994  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29995  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29996  * shadow (defaults to false)
29997  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29998  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29999  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30000  * @cfg {Array} buttons Array of buttons
30001  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30002  * @constructor
30003  * Create a new BasicDialog.
30004  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30005  * @param {Object} config Configuration options
30006  */
30007 Roo.BasicDialog = function(el, config){
30008     this.el = Roo.get(el);
30009     var dh = Roo.DomHelper;
30010     if(!this.el && config && config.autoCreate){
30011         if(typeof config.autoCreate == "object"){
30012             if(!config.autoCreate.id){
30013                 config.autoCreate.id = el;
30014             }
30015             this.el = dh.append(document.body,
30016                         config.autoCreate, true);
30017         }else{
30018             this.el = dh.append(document.body,
30019                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30020         }
30021     }
30022     el = this.el;
30023     el.setDisplayed(true);
30024     el.hide = this.hideAction;
30025     this.id = el.id;
30026     el.addClass("x-dlg");
30027
30028     Roo.apply(this, config);
30029
30030     this.proxy = el.createProxy("x-dlg-proxy");
30031     this.proxy.hide = this.hideAction;
30032     this.proxy.setOpacity(.5);
30033     this.proxy.hide();
30034
30035     if(config.width){
30036         el.setWidth(config.width);
30037     }
30038     if(config.height){
30039         el.setHeight(config.height);
30040     }
30041     this.size = el.getSize();
30042     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30043         this.xy = [config.x,config.y];
30044     }else{
30045         this.xy = el.getCenterXY(true);
30046     }
30047     /** The header element @type Roo.Element */
30048     this.header = el.child("> .x-dlg-hd");
30049     /** The body element @type Roo.Element */
30050     this.body = el.child("> .x-dlg-bd");
30051     /** The footer element @type Roo.Element */
30052     this.footer = el.child("> .x-dlg-ft");
30053
30054     if(!this.header){
30055         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30056     }
30057     if(!this.body){
30058         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30059     }
30060
30061     this.header.unselectable();
30062     if(this.title){
30063         this.header.update(this.title);
30064     }
30065     // this element allows the dialog to be focused for keyboard event
30066     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30067     this.focusEl.swallowEvent("click", true);
30068
30069     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30070
30071     // wrap the body and footer for special rendering
30072     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30073     if(this.footer){
30074         this.bwrap.dom.appendChild(this.footer.dom);
30075     }
30076
30077     this.bg = this.el.createChild({
30078         tag: "div", cls:"x-dlg-bg",
30079         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30080     });
30081     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30082
30083
30084     if(this.autoScroll !== false && !this.autoTabs){
30085         this.body.setStyle("overflow", "auto");
30086     }
30087
30088     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30089
30090     if(this.closable !== false){
30091         this.el.addClass("x-dlg-closable");
30092         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30093         this.close.on("click", this.closeClick, this);
30094         this.close.addClassOnOver("x-dlg-close-over");
30095     }
30096     if(this.collapsible !== false){
30097         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30098         this.collapseBtn.on("click", this.collapseClick, this);
30099         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30100         this.header.on("dblclick", this.collapseClick, this);
30101     }
30102     if(this.resizable !== false){
30103         this.el.addClass("x-dlg-resizable");
30104         this.resizer = new Roo.Resizable(el, {
30105             minWidth: this.minWidth || 80,
30106             minHeight:this.minHeight || 80,
30107             handles: this.resizeHandles || "all",
30108             pinned: true
30109         });
30110         this.resizer.on("beforeresize", this.beforeResize, this);
30111         this.resizer.on("resize", this.onResize, this);
30112     }
30113     if(this.draggable !== false){
30114         el.addClass("x-dlg-draggable");
30115         if (!this.proxyDrag) {
30116             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30117         }
30118         else {
30119             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30120         }
30121         dd.setHandleElId(this.header.id);
30122         dd.endDrag = this.endMove.createDelegate(this);
30123         dd.startDrag = this.startMove.createDelegate(this);
30124         dd.onDrag = this.onDrag.createDelegate(this);
30125         dd.scroll = false;
30126         this.dd = dd;
30127     }
30128     if(this.modal){
30129         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30130         this.mask.enableDisplayMode("block");
30131         this.mask.hide();
30132         this.el.addClass("x-dlg-modal");
30133     }
30134     if(this.shadow){
30135         this.shadow = new Roo.Shadow({
30136             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30137             offset : this.shadowOffset
30138         });
30139     }else{
30140         this.shadowOffset = 0;
30141     }
30142     if(Roo.useShims && this.shim !== false){
30143         this.shim = this.el.createShim();
30144         this.shim.hide = this.hideAction;
30145         this.shim.hide();
30146     }else{
30147         this.shim = false;
30148     }
30149     if(this.autoTabs){
30150         this.initTabs();
30151     }
30152     if (this.buttons) { 
30153         var bts= this.buttons;
30154         this.buttons = [];
30155         Roo.each(bts, function(b) {
30156             this.addButton(b);
30157         }, this);
30158     }
30159     
30160     
30161     this.addEvents({
30162         /**
30163          * @event keydown
30164          * Fires when a key is pressed
30165          * @param {Roo.BasicDialog} this
30166          * @param {Roo.EventObject} e
30167          */
30168         "keydown" : true,
30169         /**
30170          * @event move
30171          * Fires when this dialog is moved by the user.
30172          * @param {Roo.BasicDialog} this
30173          * @param {Number} x The new page X
30174          * @param {Number} y The new page Y
30175          */
30176         "move" : true,
30177         /**
30178          * @event resize
30179          * Fires when this dialog is resized by the user.
30180          * @param {Roo.BasicDialog} this
30181          * @param {Number} width The new width
30182          * @param {Number} height The new height
30183          */
30184         "resize" : true,
30185         /**
30186          * @event beforehide
30187          * Fires before this dialog is hidden.
30188          * @param {Roo.BasicDialog} this
30189          */
30190         "beforehide" : true,
30191         /**
30192          * @event hide
30193          * Fires when this dialog is hidden.
30194          * @param {Roo.BasicDialog} this
30195          */
30196         "hide" : true,
30197         /**
30198          * @event beforeshow
30199          * Fires before this dialog is shown.
30200          * @param {Roo.BasicDialog} this
30201          */
30202         "beforeshow" : true,
30203         /**
30204          * @event show
30205          * Fires when this dialog is shown.
30206          * @param {Roo.BasicDialog} this
30207          */
30208         "show" : true
30209     });
30210     el.on("keydown", this.onKeyDown, this);
30211     el.on("mousedown", this.toFront, this);
30212     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30213     this.el.hide();
30214     Roo.DialogManager.register(this);
30215     Roo.BasicDialog.superclass.constructor.call(this);
30216 };
30217
30218 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30219     shadowOffset: Roo.isIE ? 6 : 5,
30220     minHeight: 80,
30221     minWidth: 200,
30222     minButtonWidth: 75,
30223     defaultButton: null,
30224     buttonAlign: "right",
30225     tabTag: 'div',
30226     firstShow: true,
30227
30228     /**
30229      * Sets the dialog title text
30230      * @param {String} text The title text to display
30231      * @return {Roo.BasicDialog} this
30232      */
30233     setTitle : function(text){
30234         this.header.update(text);
30235         return this;
30236     },
30237
30238     // private
30239     closeClick : function(){
30240         this.hide();
30241     },
30242
30243     // private
30244     collapseClick : function(){
30245         this[this.collapsed ? "expand" : "collapse"]();
30246     },
30247
30248     /**
30249      * Collapses the dialog to its minimized state (only the title bar is visible).
30250      * Equivalent to the user clicking the collapse dialog button.
30251      */
30252     collapse : function(){
30253         if(!this.collapsed){
30254             this.collapsed = true;
30255             this.el.addClass("x-dlg-collapsed");
30256             this.restoreHeight = this.el.getHeight();
30257             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30258         }
30259     },
30260
30261     /**
30262      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30263      * clicking the expand dialog button.
30264      */
30265     expand : function(){
30266         if(this.collapsed){
30267             this.collapsed = false;
30268             this.el.removeClass("x-dlg-collapsed");
30269             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30270         }
30271     },
30272
30273     /**
30274      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30275      * @return {Roo.TabPanel} The tabs component
30276      */
30277     initTabs : function(){
30278         var tabs = this.getTabs();
30279         while(tabs.getTab(0)){
30280             tabs.removeTab(0);
30281         }
30282         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30283             var dom = el.dom;
30284             tabs.addTab(Roo.id(dom), dom.title);
30285             dom.title = "";
30286         });
30287         tabs.activate(0);
30288         return tabs;
30289     },
30290
30291     // private
30292     beforeResize : function(){
30293         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30294     },
30295
30296     // private
30297     onResize : function(){
30298         this.refreshSize();
30299         this.syncBodyHeight();
30300         this.adjustAssets();
30301         this.focus();
30302         this.fireEvent("resize", this, this.size.width, this.size.height);
30303     },
30304
30305     // private
30306     onKeyDown : function(e){
30307         if(this.isVisible()){
30308             this.fireEvent("keydown", this, e);
30309         }
30310     },
30311
30312     /**
30313      * Resizes the dialog.
30314      * @param {Number} width
30315      * @param {Number} height
30316      * @return {Roo.BasicDialog} this
30317      */
30318     resizeTo : function(width, height){
30319         this.el.setSize(width, height);
30320         this.size = {width: width, height: height};
30321         this.syncBodyHeight();
30322         if(this.fixedcenter){
30323             this.center();
30324         }
30325         if(this.isVisible()){
30326             this.constrainXY();
30327             this.adjustAssets();
30328         }
30329         this.fireEvent("resize", this, width, height);
30330         return this;
30331     },
30332
30333
30334     /**
30335      * Resizes the dialog to fit the specified content size.
30336      * @param {Number} width
30337      * @param {Number} height
30338      * @return {Roo.BasicDialog} this
30339      */
30340     setContentSize : function(w, h){
30341         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30342         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30343         //if(!this.el.isBorderBox()){
30344             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30345             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30346         //}
30347         if(this.tabs){
30348             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30349             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30350         }
30351         this.resizeTo(w, h);
30352         return this;
30353     },
30354
30355     /**
30356      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30357      * executed in response to a particular key being pressed while the dialog is active.
30358      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30359      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30360      * @param {Function} fn The function to call
30361      * @param {Object} scope (optional) The scope of the function
30362      * @return {Roo.BasicDialog} this
30363      */
30364     addKeyListener : function(key, fn, scope){
30365         var keyCode, shift, ctrl, alt;
30366         if(typeof key == "object" && !(key instanceof Array)){
30367             keyCode = key["key"];
30368             shift = key["shift"];
30369             ctrl = key["ctrl"];
30370             alt = key["alt"];
30371         }else{
30372             keyCode = key;
30373         }
30374         var handler = function(dlg, e){
30375             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30376                 var k = e.getKey();
30377                 if(keyCode instanceof Array){
30378                     for(var i = 0, len = keyCode.length; i < len; i++){
30379                         if(keyCode[i] == k){
30380                           fn.call(scope || window, dlg, k, e);
30381                           return;
30382                         }
30383                     }
30384                 }else{
30385                     if(k == keyCode){
30386                         fn.call(scope || window, dlg, k, e);
30387                     }
30388                 }
30389             }
30390         };
30391         this.on("keydown", handler);
30392         return this;
30393     },
30394
30395     /**
30396      * Returns the TabPanel component (creates it if it doesn't exist).
30397      * Note: If you wish to simply check for the existence of tabs without creating them,
30398      * check for a null 'tabs' property.
30399      * @return {Roo.TabPanel} The tabs component
30400      */
30401     getTabs : function(){
30402         if(!this.tabs){
30403             this.el.addClass("x-dlg-auto-tabs");
30404             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30405             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30406         }
30407         return this.tabs;
30408     },
30409
30410     /**
30411      * Adds a button to the footer section of the dialog.
30412      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30413      * object or a valid Roo.DomHelper element config
30414      * @param {Function} handler The function called when the button is clicked
30415      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30416      * @return {Roo.Button} The new button
30417      */
30418     addButton : function(config, handler, scope){
30419         var dh = Roo.DomHelper;
30420         if(!this.footer){
30421             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30422         }
30423         if(!this.btnContainer){
30424             var tb = this.footer.createChild({
30425
30426                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30427                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30428             }, null, true);
30429             this.btnContainer = tb.firstChild.firstChild.firstChild;
30430         }
30431         var bconfig = {
30432             handler: handler,
30433             scope: scope,
30434             minWidth: this.minButtonWidth,
30435             hideParent:true
30436         };
30437         if(typeof config == "string"){
30438             bconfig.text = config;
30439         }else{
30440             if(config.tag){
30441                 bconfig.dhconfig = config;
30442             }else{
30443                 Roo.apply(bconfig, config);
30444             }
30445         }
30446         var fc = false;
30447         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30448             bconfig.position = Math.max(0, bconfig.position);
30449             fc = this.btnContainer.childNodes[bconfig.position];
30450         }
30451          
30452         var btn = new Roo.Button(
30453             fc ? 
30454                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30455                 : this.btnContainer.appendChild(document.createElement("td")),
30456             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30457             bconfig
30458         );
30459         this.syncBodyHeight();
30460         if(!this.buttons){
30461             /**
30462              * Array of all the buttons that have been added to this dialog via addButton
30463              * @type Array
30464              */
30465             this.buttons = [];
30466         }
30467         this.buttons.push(btn);
30468         return btn;
30469     },
30470
30471     /**
30472      * Sets the default button to be focused when the dialog is displayed.
30473      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30474      * @return {Roo.BasicDialog} this
30475      */
30476     setDefaultButton : function(btn){
30477         this.defaultButton = btn;
30478         return this;
30479     },
30480
30481     // private
30482     getHeaderFooterHeight : function(safe){
30483         var height = 0;
30484         if(this.header){
30485            height += this.header.getHeight();
30486         }
30487         if(this.footer){
30488            var fm = this.footer.getMargins();
30489             height += (this.footer.getHeight()+fm.top+fm.bottom);
30490         }
30491         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30492         height += this.centerBg.getPadding("tb");
30493         return height;
30494     },
30495
30496     // private
30497     syncBodyHeight : function()
30498     {
30499         var bd = this.body, // the text
30500             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30501             bw = this.bwrap;
30502         var height = this.size.height - this.getHeaderFooterHeight(false);
30503         bd.setHeight(height-bd.getMargins("tb"));
30504         var hh = this.header.getHeight();
30505         var h = this.size.height-hh;
30506         cb.setHeight(h);
30507         
30508         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30509         bw.setHeight(h-cb.getPadding("tb"));
30510         
30511         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30512         bd.setWidth(bw.getWidth(true));
30513         if(this.tabs){
30514             this.tabs.syncHeight();
30515             if(Roo.isIE){
30516                 this.tabs.el.repaint();
30517             }
30518         }
30519     },
30520
30521     /**
30522      * Restores the previous state of the dialog if Roo.state is configured.
30523      * @return {Roo.BasicDialog} this
30524      */
30525     restoreState : function(){
30526         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30527         if(box && box.width){
30528             this.xy = [box.x, box.y];
30529             this.resizeTo(box.width, box.height);
30530         }
30531         return this;
30532     },
30533
30534     // private
30535     beforeShow : function(){
30536         this.expand();
30537         if(this.fixedcenter){
30538             this.xy = this.el.getCenterXY(true);
30539         }
30540         if(this.modal){
30541             Roo.get(document.body).addClass("x-body-masked");
30542             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30543             this.mask.show();
30544         }
30545         this.constrainXY();
30546     },
30547
30548     // private
30549     animShow : function(){
30550         var b = Roo.get(this.animateTarget).getBox();
30551         this.proxy.setSize(b.width, b.height);
30552         this.proxy.setLocation(b.x, b.y);
30553         this.proxy.show();
30554         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30555                     true, .35, this.showEl.createDelegate(this));
30556     },
30557
30558     /**
30559      * Shows the dialog.
30560      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30561      * @return {Roo.BasicDialog} this
30562      */
30563     show : function(animateTarget){
30564         if (this.fireEvent("beforeshow", this) === false){
30565             return;
30566         }
30567         if(this.syncHeightBeforeShow){
30568             this.syncBodyHeight();
30569         }else if(this.firstShow){
30570             this.firstShow = false;
30571             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30572         }
30573         this.animateTarget = animateTarget || this.animateTarget;
30574         if(!this.el.isVisible()){
30575             this.beforeShow();
30576             if(this.animateTarget && Roo.get(this.animateTarget)){
30577                 this.animShow();
30578             }else{
30579                 this.showEl();
30580             }
30581         }
30582         return this;
30583     },
30584
30585     // private
30586     showEl : function(){
30587         this.proxy.hide();
30588         this.el.setXY(this.xy);
30589         this.el.show();
30590         this.adjustAssets(true);
30591         this.toFront();
30592         this.focus();
30593         // IE peekaboo bug - fix found by Dave Fenwick
30594         if(Roo.isIE){
30595             this.el.repaint();
30596         }
30597         this.fireEvent("show", this);
30598     },
30599
30600     /**
30601      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30602      * dialog itself will receive focus.
30603      */
30604     focus : function(){
30605         if(this.defaultButton){
30606             this.defaultButton.focus();
30607         }else{
30608             this.focusEl.focus();
30609         }
30610     },
30611
30612     // private
30613     constrainXY : function(){
30614         if(this.constraintoviewport !== false){
30615             if(!this.viewSize){
30616                 if(this.container){
30617                     var s = this.container.getSize();
30618                     this.viewSize = [s.width, s.height];
30619                 }else{
30620                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30621                 }
30622             }
30623             var s = Roo.get(this.container||document).getScroll();
30624
30625             var x = this.xy[0], y = this.xy[1];
30626             var w = this.size.width, h = this.size.height;
30627             var vw = this.viewSize[0], vh = this.viewSize[1];
30628             // only move it if it needs it
30629             var moved = false;
30630             // first validate right/bottom
30631             if(x + w > vw+s.left){
30632                 x = vw - w;
30633                 moved = true;
30634             }
30635             if(y + h > vh+s.top){
30636                 y = vh - h;
30637                 moved = true;
30638             }
30639             // then make sure top/left isn't negative
30640             if(x < s.left){
30641                 x = s.left;
30642                 moved = true;
30643             }
30644             if(y < s.top){
30645                 y = s.top;
30646                 moved = true;
30647             }
30648             if(moved){
30649                 // cache xy
30650                 this.xy = [x, y];
30651                 if(this.isVisible()){
30652                     this.el.setLocation(x, y);
30653                     this.adjustAssets();
30654                 }
30655             }
30656         }
30657     },
30658
30659     // private
30660     onDrag : function(){
30661         if(!this.proxyDrag){
30662             this.xy = this.el.getXY();
30663             this.adjustAssets();
30664         }
30665     },
30666
30667     // private
30668     adjustAssets : function(doShow){
30669         var x = this.xy[0], y = this.xy[1];
30670         var w = this.size.width, h = this.size.height;
30671         if(doShow === true){
30672             if(this.shadow){
30673                 this.shadow.show(this.el);
30674             }
30675             if(this.shim){
30676                 this.shim.show();
30677             }
30678         }
30679         if(this.shadow && this.shadow.isVisible()){
30680             this.shadow.show(this.el);
30681         }
30682         if(this.shim && this.shim.isVisible()){
30683             this.shim.setBounds(x, y, w, h);
30684         }
30685     },
30686
30687     // private
30688     adjustViewport : function(w, h){
30689         if(!w || !h){
30690             w = Roo.lib.Dom.getViewWidth();
30691             h = Roo.lib.Dom.getViewHeight();
30692         }
30693         // cache the size
30694         this.viewSize = [w, h];
30695         if(this.modal && this.mask.isVisible()){
30696             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30697             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30698         }
30699         if(this.isVisible()){
30700             this.constrainXY();
30701         }
30702     },
30703
30704     /**
30705      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30706      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30707      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30708      */
30709     destroy : function(removeEl){
30710         if(this.isVisible()){
30711             this.animateTarget = null;
30712             this.hide();
30713         }
30714         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30715         if(this.tabs){
30716             this.tabs.destroy(removeEl);
30717         }
30718         Roo.destroy(
30719              this.shim,
30720              this.proxy,
30721              this.resizer,
30722              this.close,
30723              this.mask
30724         );
30725         if(this.dd){
30726             this.dd.unreg();
30727         }
30728         if(this.buttons){
30729            for(var i = 0, len = this.buttons.length; i < len; i++){
30730                this.buttons[i].destroy();
30731            }
30732         }
30733         this.el.removeAllListeners();
30734         if(removeEl === true){
30735             this.el.update("");
30736             this.el.remove();
30737         }
30738         Roo.DialogManager.unregister(this);
30739     },
30740
30741     // private
30742     startMove : function(){
30743         if(this.proxyDrag){
30744             this.proxy.show();
30745         }
30746         if(this.constraintoviewport !== false){
30747             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30748         }
30749     },
30750
30751     // private
30752     endMove : function(){
30753         if(!this.proxyDrag){
30754             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30755         }else{
30756             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30757             this.proxy.hide();
30758         }
30759         this.refreshSize();
30760         this.adjustAssets();
30761         this.focus();
30762         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30763     },
30764
30765     /**
30766      * Brings this dialog to the front of any other visible dialogs
30767      * @return {Roo.BasicDialog} this
30768      */
30769     toFront : function(){
30770         Roo.DialogManager.bringToFront(this);
30771         return this;
30772     },
30773
30774     /**
30775      * Sends this dialog to the back (under) of any other visible dialogs
30776      * @return {Roo.BasicDialog} this
30777      */
30778     toBack : function(){
30779         Roo.DialogManager.sendToBack(this);
30780         return this;
30781     },
30782
30783     /**
30784      * Centers this dialog in the viewport
30785      * @return {Roo.BasicDialog} this
30786      */
30787     center : function(){
30788         var xy = this.el.getCenterXY(true);
30789         this.moveTo(xy[0], xy[1]);
30790         return this;
30791     },
30792
30793     /**
30794      * Moves the dialog's top-left corner to the specified point
30795      * @param {Number} x
30796      * @param {Number} y
30797      * @return {Roo.BasicDialog} this
30798      */
30799     moveTo : function(x, y){
30800         this.xy = [x,y];
30801         if(this.isVisible()){
30802             this.el.setXY(this.xy);
30803             this.adjustAssets();
30804         }
30805         return this;
30806     },
30807
30808     /**
30809      * Aligns the dialog to the specified element
30810      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30811      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30812      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30813      * @return {Roo.BasicDialog} this
30814      */
30815     alignTo : function(element, position, offsets){
30816         this.xy = this.el.getAlignToXY(element, position, offsets);
30817         if(this.isVisible()){
30818             this.el.setXY(this.xy);
30819             this.adjustAssets();
30820         }
30821         return this;
30822     },
30823
30824     /**
30825      * Anchors an element to another element and realigns it when the window is resized.
30826      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30827      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30828      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30829      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30830      * is a number, it is used as the buffer delay (defaults to 50ms).
30831      * @return {Roo.BasicDialog} this
30832      */
30833     anchorTo : function(el, alignment, offsets, monitorScroll){
30834         var action = function(){
30835             this.alignTo(el, alignment, offsets);
30836         };
30837         Roo.EventManager.onWindowResize(action, this);
30838         var tm = typeof monitorScroll;
30839         if(tm != 'undefined'){
30840             Roo.EventManager.on(window, 'scroll', action, this,
30841                 {buffer: tm == 'number' ? monitorScroll : 50});
30842         }
30843         action.call(this);
30844         return this;
30845     },
30846
30847     /**
30848      * Returns true if the dialog is visible
30849      * @return {Boolean}
30850      */
30851     isVisible : function(){
30852         return this.el.isVisible();
30853     },
30854
30855     // private
30856     animHide : function(callback){
30857         var b = Roo.get(this.animateTarget).getBox();
30858         this.proxy.show();
30859         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30860         this.el.hide();
30861         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30862                     this.hideEl.createDelegate(this, [callback]));
30863     },
30864
30865     /**
30866      * Hides the dialog.
30867      * @param {Function} callback (optional) Function to call when the dialog is hidden
30868      * @return {Roo.BasicDialog} this
30869      */
30870     hide : function(callback){
30871         if (this.fireEvent("beforehide", this) === false){
30872             return;
30873         }
30874         if(this.shadow){
30875             this.shadow.hide();
30876         }
30877         if(this.shim) {
30878           this.shim.hide();
30879         }
30880         // sometimes animateTarget seems to get set.. causing problems...
30881         // this just double checks..
30882         if(this.animateTarget && Roo.get(this.animateTarget)) {
30883            this.animHide(callback);
30884         }else{
30885             this.el.hide();
30886             this.hideEl(callback);
30887         }
30888         return this;
30889     },
30890
30891     // private
30892     hideEl : function(callback){
30893         this.proxy.hide();
30894         if(this.modal){
30895             this.mask.hide();
30896             Roo.get(document.body).removeClass("x-body-masked");
30897         }
30898         this.fireEvent("hide", this);
30899         if(typeof callback == "function"){
30900             callback();
30901         }
30902     },
30903
30904     // private
30905     hideAction : function(){
30906         this.setLeft("-10000px");
30907         this.setTop("-10000px");
30908         this.setStyle("visibility", "hidden");
30909     },
30910
30911     // private
30912     refreshSize : function(){
30913         this.size = this.el.getSize();
30914         this.xy = this.el.getXY();
30915         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30916     },
30917
30918     // private
30919     // z-index is managed by the DialogManager and may be overwritten at any time
30920     setZIndex : function(index){
30921         if(this.modal){
30922             this.mask.setStyle("z-index", index);
30923         }
30924         if(this.shim){
30925             this.shim.setStyle("z-index", ++index);
30926         }
30927         if(this.shadow){
30928             this.shadow.setZIndex(++index);
30929         }
30930         this.el.setStyle("z-index", ++index);
30931         if(this.proxy){
30932             this.proxy.setStyle("z-index", ++index);
30933         }
30934         if(this.resizer){
30935             this.resizer.proxy.setStyle("z-index", ++index);
30936         }
30937
30938         this.lastZIndex = index;
30939     },
30940
30941     /**
30942      * Returns the element for this dialog
30943      * @return {Roo.Element} The underlying dialog Element
30944      */
30945     getEl : function(){
30946         return this.el;
30947     }
30948 });
30949
30950 /**
30951  * @class Roo.DialogManager
30952  * Provides global access to BasicDialogs that have been created and
30953  * support for z-indexing (layering) multiple open dialogs.
30954  */
30955 Roo.DialogManager = function(){
30956     var list = {};
30957     var accessList = [];
30958     var front = null;
30959
30960     // private
30961     var sortDialogs = function(d1, d2){
30962         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30963     };
30964
30965     // private
30966     var orderDialogs = function(){
30967         accessList.sort(sortDialogs);
30968         var seed = Roo.DialogManager.zseed;
30969         for(var i = 0, len = accessList.length; i < len; i++){
30970             var dlg = accessList[i];
30971             if(dlg){
30972                 dlg.setZIndex(seed + (i*10));
30973             }
30974         }
30975     };
30976
30977     return {
30978         /**
30979          * The starting z-index for BasicDialogs (defaults to 9000)
30980          * @type Number The z-index value
30981          */
30982         zseed : 9000,
30983
30984         // private
30985         register : function(dlg){
30986             list[dlg.id] = dlg;
30987             accessList.push(dlg);
30988         },
30989
30990         // private
30991         unregister : function(dlg){
30992             delete list[dlg.id];
30993             var i=0;
30994             var len=0;
30995             if(!accessList.indexOf){
30996                 for(  i = 0, len = accessList.length; i < len; i++){
30997                     if(accessList[i] == dlg){
30998                         accessList.splice(i, 1);
30999                         return;
31000                     }
31001                 }
31002             }else{
31003                  i = accessList.indexOf(dlg);
31004                 if(i != -1){
31005                     accessList.splice(i, 1);
31006                 }
31007             }
31008         },
31009
31010         /**
31011          * Gets a registered dialog by id
31012          * @param {String/Object} id The id of the dialog or a dialog
31013          * @return {Roo.BasicDialog} this
31014          */
31015         get : function(id){
31016             return typeof id == "object" ? id : list[id];
31017         },
31018
31019         /**
31020          * Brings the specified dialog to the front
31021          * @param {String/Object} dlg The id of the dialog or a dialog
31022          * @return {Roo.BasicDialog} this
31023          */
31024         bringToFront : function(dlg){
31025             dlg = this.get(dlg);
31026             if(dlg != front){
31027                 front = dlg;
31028                 dlg._lastAccess = new Date().getTime();
31029                 orderDialogs();
31030             }
31031             return dlg;
31032         },
31033
31034         /**
31035          * Sends the specified dialog to the back
31036          * @param {String/Object} dlg The id of the dialog or a dialog
31037          * @return {Roo.BasicDialog} this
31038          */
31039         sendToBack : function(dlg){
31040             dlg = this.get(dlg);
31041             dlg._lastAccess = -(new Date().getTime());
31042             orderDialogs();
31043             return dlg;
31044         },
31045
31046         /**
31047          * Hides all dialogs
31048          */
31049         hideAll : function(){
31050             for(var id in list){
31051                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31052                     list[id].hide();
31053                 }
31054             }
31055         }
31056     };
31057 }();
31058
31059 /**
31060  * @class Roo.LayoutDialog
31061  * @extends Roo.BasicDialog
31062  * Dialog which provides adjustments for working with a layout in a Dialog.
31063  * Add your necessary layout config options to the dialog's config.<br>
31064  * Example usage (including a nested layout):
31065  * <pre><code>
31066 if(!dialog){
31067     dialog = new Roo.LayoutDialog("download-dlg", {
31068         modal: true,
31069         width:600,
31070         height:450,
31071         shadow:true,
31072         minWidth:500,
31073         minHeight:350,
31074         autoTabs:true,
31075         proxyDrag:true,
31076         // layout config merges with the dialog config
31077         center:{
31078             tabPosition: "top",
31079             alwaysShowTabs: true
31080         }
31081     });
31082     dialog.addKeyListener(27, dialog.hide, dialog);
31083     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31084     dialog.addButton("Build It!", this.getDownload, this);
31085
31086     // we can even add nested layouts
31087     var innerLayout = new Roo.BorderLayout("dl-inner", {
31088         east: {
31089             initialSize: 200,
31090             autoScroll:true,
31091             split:true
31092         },
31093         center: {
31094             autoScroll:true
31095         }
31096     });
31097     innerLayout.beginUpdate();
31098     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31099     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31100     innerLayout.endUpdate(true);
31101
31102     var layout = dialog.getLayout();
31103     layout.beginUpdate();
31104     layout.add("center", new Roo.ContentPanel("standard-panel",
31105                         {title: "Download the Source", fitToFrame:true}));
31106     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31107                {title: "Build your own roo.js"}));
31108     layout.getRegion("center").showPanel(sp);
31109     layout.endUpdate();
31110 }
31111 </code></pre>
31112     * @constructor
31113     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31114     * @param {Object} config configuration options
31115   */
31116 Roo.LayoutDialog = function(el, cfg){
31117     
31118     var config=  cfg;
31119     if (typeof(cfg) == 'undefined') {
31120         config = Roo.apply({}, el);
31121         // not sure why we use documentElement here.. - it should always be body.
31122         // IE7 borks horribly if we use documentElement.
31123         // webkit also does not like documentElement - it creates a body element...
31124         el = Roo.get( document.body || document.documentElement ).createChild();
31125         //config.autoCreate = true;
31126     }
31127     
31128     
31129     config.autoTabs = false;
31130     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31131     this.body.setStyle({overflow:"hidden", position:"relative"});
31132     this.layout = new Roo.BorderLayout(this.body.dom, config);
31133     this.layout.monitorWindowResize = false;
31134     this.el.addClass("x-dlg-auto-layout");
31135     // fix case when center region overwrites center function
31136     this.center = Roo.BasicDialog.prototype.center;
31137     this.on("show", this.layout.layout, this.layout, true);
31138     if (config.items) {
31139         var xitems = config.items;
31140         delete config.items;
31141         Roo.each(xitems, this.addxtype, this);
31142     }
31143     
31144     
31145 };
31146 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31147     /**
31148      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31149      * @deprecated
31150      */
31151     endUpdate : function(){
31152         this.layout.endUpdate();
31153     },
31154
31155     /**
31156      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31157      *  @deprecated
31158      */
31159     beginUpdate : function(){
31160         this.layout.beginUpdate();
31161     },
31162
31163     /**
31164      * Get the BorderLayout for this dialog
31165      * @return {Roo.BorderLayout}
31166      */
31167     getLayout : function(){
31168         return this.layout;
31169     },
31170
31171     showEl : function(){
31172         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31173         if(Roo.isIE7){
31174             this.layout.layout();
31175         }
31176     },
31177
31178     // private
31179     // Use the syncHeightBeforeShow config option to control this automatically
31180     syncBodyHeight : function(){
31181         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31182         if(this.layout){this.layout.layout();}
31183     },
31184     
31185       /**
31186      * Add an xtype element (actually adds to the layout.)
31187      * @return {Object} xdata xtype object data.
31188      */
31189     
31190     addxtype : function(c) {
31191         return this.layout.addxtype(c);
31192     }
31193 });/*
31194  * Based on:
31195  * Ext JS Library 1.1.1
31196  * Copyright(c) 2006-2007, Ext JS, LLC.
31197  *
31198  * Originally Released Under LGPL - original licence link has changed is not relivant.
31199  *
31200  * Fork - LGPL
31201  * <script type="text/javascript">
31202  */
31203  
31204 /**
31205  * @class Roo.MessageBox
31206  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31207  * Example usage:
31208  *<pre><code>
31209 // Basic alert:
31210 Roo.Msg.alert('Status', 'Changes saved successfully.');
31211
31212 // Prompt for user data:
31213 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31214     if (btn == 'ok'){
31215         // process text value...
31216     }
31217 });
31218
31219 // Show a dialog using config options:
31220 Roo.Msg.show({
31221    title:'Save Changes?',
31222    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31223    buttons: Roo.Msg.YESNOCANCEL,
31224    fn: processResult,
31225    animEl: 'elId'
31226 });
31227 </code></pre>
31228  * @singleton
31229  */
31230 Roo.MessageBox = function(){
31231     var dlg, opt, mask, waitTimer;
31232     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31233     var buttons, activeTextEl, bwidth;
31234
31235     // private
31236     var handleButton = function(button){
31237         dlg.hide();
31238         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31239     };
31240
31241     // private
31242     var handleHide = function(){
31243         if(opt && opt.cls){
31244             dlg.el.removeClass(opt.cls);
31245         }
31246         if(waitTimer){
31247             Roo.TaskMgr.stop(waitTimer);
31248             waitTimer = null;
31249         }
31250     };
31251
31252     // private
31253     var updateButtons = function(b){
31254         var width = 0;
31255         if(!b){
31256             buttons["ok"].hide();
31257             buttons["cancel"].hide();
31258             buttons["yes"].hide();
31259             buttons["no"].hide();
31260             dlg.footer.dom.style.display = 'none';
31261             return width;
31262         }
31263         dlg.footer.dom.style.display = '';
31264         for(var k in buttons){
31265             if(typeof buttons[k] != "function"){
31266                 if(b[k]){
31267                     buttons[k].show();
31268                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31269                     width += buttons[k].el.getWidth()+15;
31270                 }else{
31271                     buttons[k].hide();
31272                 }
31273             }
31274         }
31275         return width;
31276     };
31277
31278     // private
31279     var handleEsc = function(d, k, e){
31280         if(opt && opt.closable !== false){
31281             dlg.hide();
31282         }
31283         if(e){
31284             e.stopEvent();
31285         }
31286     };
31287
31288     return {
31289         /**
31290          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31291          * @return {Roo.BasicDialog} The BasicDialog element
31292          */
31293         getDialog : function(){
31294            if(!dlg){
31295                 dlg = new Roo.BasicDialog("x-msg-box", {
31296                     autoCreate : true,
31297                     shadow: true,
31298                     draggable: true,
31299                     resizable:false,
31300                     constraintoviewport:false,
31301                     fixedcenter:true,
31302                     collapsible : false,
31303                     shim:true,
31304                     modal: true,
31305                     width:400, height:100,
31306                     buttonAlign:"center",
31307                     closeClick : function(){
31308                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31309                             handleButton("no");
31310                         }else{
31311                             handleButton("cancel");
31312                         }
31313                     }
31314                 });
31315                 dlg.on("hide", handleHide);
31316                 mask = dlg.mask;
31317                 dlg.addKeyListener(27, handleEsc);
31318                 buttons = {};
31319                 var bt = this.buttonText;
31320                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31321                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31322                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31323                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31324                 bodyEl = dlg.body.createChild({
31325
31326                     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>'
31327                 });
31328                 msgEl = bodyEl.dom.firstChild;
31329                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31330                 textboxEl.enableDisplayMode();
31331                 textboxEl.addKeyListener([10,13], function(){
31332                     if(dlg.isVisible() && opt && opt.buttons){
31333                         if(opt.buttons.ok){
31334                             handleButton("ok");
31335                         }else if(opt.buttons.yes){
31336                             handleButton("yes");
31337                         }
31338                     }
31339                 });
31340                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31341                 textareaEl.enableDisplayMode();
31342                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31343                 progressEl.enableDisplayMode();
31344                 var pf = progressEl.dom.firstChild;
31345                 if (pf) {
31346                     pp = Roo.get(pf.firstChild);
31347                     pp.setHeight(pf.offsetHeight);
31348                 }
31349                 
31350             }
31351             return dlg;
31352         },
31353
31354         /**
31355          * Updates the message box body text
31356          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31357          * the XHTML-compliant non-breaking space character '&amp;#160;')
31358          * @return {Roo.MessageBox} This message box
31359          */
31360         updateText : function(text){
31361             if(!dlg.isVisible() && !opt.width){
31362                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31363             }
31364             msgEl.innerHTML = text || '&#160;';
31365       
31366             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31367             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31368             var w = Math.max(
31369                     Math.min(opt.width || cw , this.maxWidth), 
31370                     Math.max(opt.minWidth || this.minWidth, bwidth)
31371             );
31372             if(opt.prompt){
31373                 activeTextEl.setWidth(w);
31374             }
31375             if(dlg.isVisible()){
31376                 dlg.fixedcenter = false;
31377             }
31378             // to big, make it scroll. = But as usual stupid IE does not support
31379             // !important..
31380             
31381             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31382                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31383                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31384             } else {
31385                 bodyEl.dom.style.height = '';
31386                 bodyEl.dom.style.overflowY = '';
31387             }
31388             if (cw > w) {
31389                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31390             } else {
31391                 bodyEl.dom.style.overflowX = '';
31392             }
31393             
31394             dlg.setContentSize(w, bodyEl.getHeight());
31395             if(dlg.isVisible()){
31396                 dlg.fixedcenter = true;
31397             }
31398             return this;
31399         },
31400
31401         /**
31402          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31403          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31404          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31405          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31406          * @return {Roo.MessageBox} This message box
31407          */
31408         updateProgress : function(value, text){
31409             if(text){
31410                 this.updateText(text);
31411             }
31412             if (pp) { // weird bug on my firefox - for some reason this is not defined
31413                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31414             }
31415             return this;
31416         },        
31417
31418         /**
31419          * Returns true if the message box is currently displayed
31420          * @return {Boolean} True if the message box is visible, else false
31421          */
31422         isVisible : function(){
31423             return dlg && dlg.isVisible();  
31424         },
31425
31426         /**
31427          * Hides the message box if it is displayed
31428          */
31429         hide : function(){
31430             if(this.isVisible()){
31431                 dlg.hide();
31432             }  
31433         },
31434
31435         /**
31436          * Displays a new message box, or reinitializes an existing message box, based on the config options
31437          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31438          * The following config object properties are supported:
31439          * <pre>
31440 Property    Type             Description
31441 ----------  ---------------  ------------------------------------------------------------------------------------
31442 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31443                                    closes (defaults to undefined)
31444 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31445                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31446 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31447                                    progress and wait dialogs will ignore this property and always hide the
31448                                    close button as they can only be closed programmatically.
31449 cls               String           A custom CSS class to apply to the message box element
31450 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31451                                    displayed (defaults to 75)
31452 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31453                                    function will be btn (the name of the button that was clicked, if applicable,
31454                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31455                                    Progress and wait dialogs will ignore this option since they do not respond to
31456                                    user actions and can only be closed programmatically, so any required function
31457                                    should be called by the same code after it closes the dialog.
31458 icon              String           A CSS class that provides a background image to be used as an icon for
31459                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31460 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31461 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31462 modal             Boolean          False to allow user interaction with the page while the message box is
31463                                    displayed (defaults to true)
31464 msg               String           A string that will replace the existing message box body text (defaults
31465                                    to the XHTML-compliant non-breaking space character '&#160;')
31466 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31467 progress          Boolean          True to display a progress bar (defaults to false)
31468 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31469 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31470 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31471 title             String           The title text
31472 value             String           The string value to set into the active textbox element if displayed
31473 wait              Boolean          True to display a progress bar (defaults to false)
31474 width             Number           The width of the dialog in pixels
31475 </pre>
31476          *
31477          * Example usage:
31478          * <pre><code>
31479 Roo.Msg.show({
31480    title: 'Address',
31481    msg: 'Please enter your address:',
31482    width: 300,
31483    buttons: Roo.MessageBox.OKCANCEL,
31484    multiline: true,
31485    fn: saveAddress,
31486    animEl: 'addAddressBtn'
31487 });
31488 </code></pre>
31489          * @param {Object} config Configuration options
31490          * @return {Roo.MessageBox} This message box
31491          */
31492         show : function(options)
31493         {
31494             
31495             // this causes nightmares if you show one dialog after another
31496             // especially on callbacks..
31497              
31498             if(this.isVisible()){
31499                 
31500                 this.hide();
31501                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31502                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31503                 Roo.log("New Dialog Message:" +  options.msg )
31504                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31505                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31506                 
31507             }
31508             var d = this.getDialog();
31509             opt = options;
31510             d.setTitle(opt.title || "&#160;");
31511             d.close.setDisplayed(opt.closable !== false);
31512             activeTextEl = textboxEl;
31513             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31514             if(opt.prompt){
31515                 if(opt.multiline){
31516                     textboxEl.hide();
31517                     textareaEl.show();
31518                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31519                         opt.multiline : this.defaultTextHeight);
31520                     activeTextEl = textareaEl;
31521                 }else{
31522                     textboxEl.show();
31523                     textareaEl.hide();
31524                 }
31525             }else{
31526                 textboxEl.hide();
31527                 textareaEl.hide();
31528             }
31529             progressEl.setDisplayed(opt.progress === true);
31530             this.updateProgress(0);
31531             activeTextEl.dom.value = opt.value || "";
31532             if(opt.prompt){
31533                 dlg.setDefaultButton(activeTextEl);
31534             }else{
31535                 var bs = opt.buttons;
31536                 var db = null;
31537                 if(bs && bs.ok){
31538                     db = buttons["ok"];
31539                 }else if(bs && bs.yes){
31540                     db = buttons["yes"];
31541                 }
31542                 dlg.setDefaultButton(db);
31543             }
31544             bwidth = updateButtons(opt.buttons);
31545             this.updateText(opt.msg);
31546             if(opt.cls){
31547                 d.el.addClass(opt.cls);
31548             }
31549             d.proxyDrag = opt.proxyDrag === true;
31550             d.modal = opt.modal !== false;
31551             d.mask = opt.modal !== false ? mask : false;
31552             if(!d.isVisible()){
31553                 // force it to the end of the z-index stack so it gets a cursor in FF
31554                 document.body.appendChild(dlg.el.dom);
31555                 d.animateTarget = null;
31556                 d.show(options.animEl);
31557             }
31558             return this;
31559         },
31560
31561         /**
31562          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31563          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31564          * and closing the message box when the process is complete.
31565          * @param {String} title The title bar text
31566          * @param {String} msg The message box body text
31567          * @return {Roo.MessageBox} This message box
31568          */
31569         progress : function(title, msg){
31570             this.show({
31571                 title : title,
31572                 msg : msg,
31573                 buttons: false,
31574                 progress:true,
31575                 closable:false,
31576                 minWidth: this.minProgressWidth,
31577                 modal : true
31578             });
31579             return this;
31580         },
31581
31582         /**
31583          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31584          * If a callback function is passed it will be called after the user clicks the button, and the
31585          * id of the button that was clicked will be passed as the only parameter to the callback
31586          * (could also be the top-right close button).
31587          * @param {String} title The title bar text
31588          * @param {String} msg The message box body text
31589          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31590          * @param {Object} scope (optional) The scope of the callback function
31591          * @return {Roo.MessageBox} This message box
31592          */
31593         alert : function(title, msg, fn, scope){
31594             this.show({
31595                 title : title,
31596                 msg : msg,
31597                 buttons: this.OK,
31598                 fn: fn,
31599                 scope : scope,
31600                 modal : true
31601             });
31602             return this;
31603         },
31604
31605         /**
31606          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31607          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31608          * You are responsible for closing the message box when the process is complete.
31609          * @param {String} msg The message box body text
31610          * @param {String} title (optional) The title bar text
31611          * @return {Roo.MessageBox} This message box
31612          */
31613         wait : function(msg, title){
31614             this.show({
31615                 title : title,
31616                 msg : msg,
31617                 buttons: false,
31618                 closable:false,
31619                 progress:true,
31620                 modal:true,
31621                 width:300,
31622                 wait:true
31623             });
31624             waitTimer = Roo.TaskMgr.start({
31625                 run: function(i){
31626                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31627                 },
31628                 interval: 1000
31629             });
31630             return this;
31631         },
31632
31633         /**
31634          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31635          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31636          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31637          * @param {String} title The title bar text
31638          * @param {String} msg The message box body text
31639          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31640          * @param {Object} scope (optional) The scope of the callback function
31641          * @return {Roo.MessageBox} This message box
31642          */
31643         confirm : function(title, msg, fn, scope){
31644             this.show({
31645                 title : title,
31646                 msg : msg,
31647                 buttons: this.YESNO,
31648                 fn: fn,
31649                 scope : scope,
31650                 modal : true
31651             });
31652             return this;
31653         },
31654
31655         /**
31656          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31657          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31658          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31659          * (could also be the top-right close button) and the text that was entered will be passed as the two
31660          * parameters to the callback.
31661          * @param {String} title The title bar text
31662          * @param {String} msg The message box body text
31663          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31664          * @param {Object} scope (optional) The scope of the callback function
31665          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31666          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31667          * @return {Roo.MessageBox} This message box
31668          */
31669         prompt : function(title, msg, fn, scope, multiline){
31670             this.show({
31671                 title : title,
31672                 msg : msg,
31673                 buttons: this.OKCANCEL,
31674                 fn: fn,
31675                 minWidth:250,
31676                 scope : scope,
31677                 prompt:true,
31678                 multiline: multiline,
31679                 modal : true
31680             });
31681             return this;
31682         },
31683
31684         /**
31685          * Button config that displays a single OK button
31686          * @type Object
31687          */
31688         OK : {ok:true},
31689         /**
31690          * Button config that displays Yes and No buttons
31691          * @type Object
31692          */
31693         YESNO : {yes:true, no:true},
31694         /**
31695          * Button config that displays OK and Cancel buttons
31696          * @type Object
31697          */
31698         OKCANCEL : {ok:true, cancel:true},
31699         /**
31700          * Button config that displays Yes, No and Cancel buttons
31701          * @type Object
31702          */
31703         YESNOCANCEL : {yes:true, no:true, cancel:true},
31704
31705         /**
31706          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31707          * @type Number
31708          */
31709         defaultTextHeight : 75,
31710         /**
31711          * The maximum width in pixels of the message box (defaults to 600)
31712          * @type Number
31713          */
31714         maxWidth : 600,
31715         /**
31716          * The minimum width in pixels of the message box (defaults to 100)
31717          * @type Number
31718          */
31719         minWidth : 100,
31720         /**
31721          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31722          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31723          * @type Number
31724          */
31725         minProgressWidth : 250,
31726         /**
31727          * An object containing the default button text strings that can be overriden for localized language support.
31728          * Supported properties are: ok, cancel, yes and no.
31729          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31730          * @type Object
31731          */
31732         buttonText : {
31733             ok : "OK",
31734             cancel : "Cancel",
31735             yes : "Yes",
31736             no : "No"
31737         }
31738     };
31739 }();
31740
31741 /**
31742  * Shorthand for {@link Roo.MessageBox}
31743  */
31744 Roo.Msg = Roo.MessageBox;/*
31745  * Based on:
31746  * Ext JS Library 1.1.1
31747  * Copyright(c) 2006-2007, Ext JS, LLC.
31748  *
31749  * Originally Released Under LGPL - original licence link has changed is not relivant.
31750  *
31751  * Fork - LGPL
31752  * <script type="text/javascript">
31753  */
31754 /**
31755  * @class Roo.QuickTips
31756  * Provides attractive and customizable tooltips for any element.
31757  * @singleton
31758  */
31759 Roo.QuickTips = function(){
31760     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31761     var ce, bd, xy, dd;
31762     var visible = false, disabled = true, inited = false;
31763     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31764     
31765     var onOver = function(e){
31766         if(disabled){
31767             return;
31768         }
31769         var t = e.getTarget();
31770         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31771             return;
31772         }
31773         if(ce && t == ce.el){
31774             clearTimeout(hideProc);
31775             return;
31776         }
31777         if(t && tagEls[t.id]){
31778             tagEls[t.id].el = t;
31779             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31780             return;
31781         }
31782         var ttp, et = Roo.fly(t);
31783         var ns = cfg.namespace;
31784         if(tm.interceptTitles && t.title){
31785             ttp = t.title;
31786             t.qtip = ttp;
31787             t.removeAttribute("title");
31788             e.preventDefault();
31789         }else{
31790             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31791         }
31792         if(ttp){
31793             showProc = show.defer(tm.showDelay, tm, [{
31794                 el: t, 
31795                 text: ttp, 
31796                 width: et.getAttributeNS(ns, cfg.width),
31797                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31798                 title: et.getAttributeNS(ns, cfg.title),
31799                     cls: et.getAttributeNS(ns, cfg.cls)
31800             }]);
31801         }
31802     };
31803     
31804     var onOut = function(e){
31805         clearTimeout(showProc);
31806         var t = e.getTarget();
31807         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31808             hideProc = setTimeout(hide, tm.hideDelay);
31809         }
31810     };
31811     
31812     var onMove = function(e){
31813         if(disabled){
31814             return;
31815         }
31816         xy = e.getXY();
31817         xy[1] += 18;
31818         if(tm.trackMouse && ce){
31819             el.setXY(xy);
31820         }
31821     };
31822     
31823     var onDown = function(e){
31824         clearTimeout(showProc);
31825         clearTimeout(hideProc);
31826         if(!e.within(el)){
31827             if(tm.hideOnClick){
31828                 hide();
31829                 tm.disable();
31830                 tm.enable.defer(100, tm);
31831             }
31832         }
31833     };
31834     
31835     var getPad = function(){
31836         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31837     };
31838
31839     var show = function(o){
31840         if(disabled){
31841             return;
31842         }
31843         clearTimeout(dismissProc);
31844         ce = o;
31845         if(removeCls){ // in case manually hidden
31846             el.removeClass(removeCls);
31847             removeCls = null;
31848         }
31849         if(ce.cls){
31850             el.addClass(ce.cls);
31851             removeCls = ce.cls;
31852         }
31853         if(ce.title){
31854             tipTitle.update(ce.title);
31855             tipTitle.show();
31856         }else{
31857             tipTitle.update('');
31858             tipTitle.hide();
31859         }
31860         el.dom.style.width  = tm.maxWidth+'px';
31861         //tipBody.dom.style.width = '';
31862         tipBodyText.update(o.text);
31863         var p = getPad(), w = ce.width;
31864         if(!w){
31865             var td = tipBodyText.dom;
31866             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31867             if(aw > tm.maxWidth){
31868                 w = tm.maxWidth;
31869             }else if(aw < tm.minWidth){
31870                 w = tm.minWidth;
31871             }else{
31872                 w = aw;
31873             }
31874         }
31875         //tipBody.setWidth(w);
31876         el.setWidth(parseInt(w, 10) + p);
31877         if(ce.autoHide === false){
31878             close.setDisplayed(true);
31879             if(dd){
31880                 dd.unlock();
31881             }
31882         }else{
31883             close.setDisplayed(false);
31884             if(dd){
31885                 dd.lock();
31886             }
31887         }
31888         if(xy){
31889             el.avoidY = xy[1]-18;
31890             el.setXY(xy);
31891         }
31892         if(tm.animate){
31893             el.setOpacity(.1);
31894             el.setStyle("visibility", "visible");
31895             el.fadeIn({callback: afterShow});
31896         }else{
31897             afterShow();
31898         }
31899     };
31900     
31901     var afterShow = function(){
31902         if(ce){
31903             el.show();
31904             esc.enable();
31905             if(tm.autoDismiss && ce.autoHide !== false){
31906                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31907             }
31908         }
31909     };
31910     
31911     var hide = function(noanim){
31912         clearTimeout(dismissProc);
31913         clearTimeout(hideProc);
31914         ce = null;
31915         if(el.isVisible()){
31916             esc.disable();
31917             if(noanim !== true && tm.animate){
31918                 el.fadeOut({callback: afterHide});
31919             }else{
31920                 afterHide();
31921             } 
31922         }
31923     };
31924     
31925     var afterHide = function(){
31926         el.hide();
31927         if(removeCls){
31928             el.removeClass(removeCls);
31929             removeCls = null;
31930         }
31931     };
31932     
31933     return {
31934         /**
31935         * @cfg {Number} minWidth
31936         * The minimum width of the quick tip (defaults to 40)
31937         */
31938        minWidth : 40,
31939         /**
31940         * @cfg {Number} maxWidth
31941         * The maximum width of the quick tip (defaults to 300)
31942         */
31943        maxWidth : 300,
31944         /**
31945         * @cfg {Boolean} interceptTitles
31946         * True to automatically use the element's DOM title value if available (defaults to false)
31947         */
31948        interceptTitles : false,
31949         /**
31950         * @cfg {Boolean} trackMouse
31951         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31952         */
31953        trackMouse : false,
31954         /**
31955         * @cfg {Boolean} hideOnClick
31956         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31957         */
31958        hideOnClick : true,
31959         /**
31960         * @cfg {Number} showDelay
31961         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31962         */
31963        showDelay : 500,
31964         /**
31965         * @cfg {Number} hideDelay
31966         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31967         */
31968        hideDelay : 200,
31969         /**
31970         * @cfg {Boolean} autoHide
31971         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31972         * Used in conjunction with hideDelay.
31973         */
31974        autoHide : true,
31975         /**
31976         * @cfg {Boolean}
31977         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31978         * (defaults to true).  Used in conjunction with autoDismissDelay.
31979         */
31980        autoDismiss : true,
31981         /**
31982         * @cfg {Number}
31983         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31984         */
31985        autoDismissDelay : 5000,
31986        /**
31987         * @cfg {Boolean} animate
31988         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31989         */
31990        animate : false,
31991
31992        /**
31993         * @cfg {String} title
31994         * Title text to display (defaults to '').  This can be any valid HTML markup.
31995         */
31996         title: '',
31997        /**
31998         * @cfg {String} text
31999         * Body text to display (defaults to '').  This can be any valid HTML markup.
32000         */
32001         text : '',
32002        /**
32003         * @cfg {String} cls
32004         * A CSS class to apply to the base quick tip element (defaults to '').
32005         */
32006         cls : '',
32007        /**
32008         * @cfg {Number} width
32009         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32010         * minWidth or maxWidth.
32011         */
32012         width : null,
32013
32014     /**
32015      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32016      * or display QuickTips in a page.
32017      */
32018        init : function(){
32019           tm = Roo.QuickTips;
32020           cfg = tm.tagConfig;
32021           if(!inited){
32022               if(!Roo.isReady){ // allow calling of init() before onReady
32023                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32024                   return;
32025               }
32026               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32027               el.fxDefaults = {stopFx: true};
32028               // maximum custom styling
32029               //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>');
32030               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>');              
32031               tipTitle = el.child('h3');
32032               tipTitle.enableDisplayMode("block");
32033               tipBody = el.child('div.x-tip-bd');
32034               tipBodyText = el.child('div.x-tip-bd-inner');
32035               //bdLeft = el.child('div.x-tip-bd-left');
32036               //bdRight = el.child('div.x-tip-bd-right');
32037               close = el.child('div.x-tip-close');
32038               close.enableDisplayMode("block");
32039               close.on("click", hide);
32040               var d = Roo.get(document);
32041               d.on("mousedown", onDown);
32042               d.on("mouseover", onOver);
32043               d.on("mouseout", onOut);
32044               d.on("mousemove", onMove);
32045               esc = d.addKeyListener(27, hide);
32046               esc.disable();
32047               if(Roo.dd.DD){
32048                   dd = el.initDD("default", null, {
32049                       onDrag : function(){
32050                           el.sync();  
32051                       }
32052                   });
32053                   dd.setHandleElId(tipTitle.id);
32054                   dd.lock();
32055               }
32056               inited = true;
32057           }
32058           this.enable(); 
32059        },
32060
32061     /**
32062      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32063      * are supported:
32064      * <pre>
32065 Property    Type                   Description
32066 ----------  ---------------------  ------------------------------------------------------------------------
32067 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32068      * </ul>
32069      * @param {Object} config The config object
32070      */
32071        register : function(config){
32072            var cs = config instanceof Array ? config : arguments;
32073            for(var i = 0, len = cs.length; i < len; i++) {
32074                var c = cs[i];
32075                var target = c.target;
32076                if(target){
32077                    if(target instanceof Array){
32078                        for(var j = 0, jlen = target.length; j < jlen; j++){
32079                            tagEls[target[j]] = c;
32080                        }
32081                    }else{
32082                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32083                    }
32084                }
32085            }
32086        },
32087
32088     /**
32089      * Removes this quick tip from its element and destroys it.
32090      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32091      */
32092        unregister : function(el){
32093            delete tagEls[Roo.id(el)];
32094        },
32095
32096     /**
32097      * Enable this quick tip.
32098      */
32099        enable : function(){
32100            if(inited && disabled){
32101                locks.pop();
32102                if(locks.length < 1){
32103                    disabled = false;
32104                }
32105            }
32106        },
32107
32108     /**
32109      * Disable this quick tip.
32110      */
32111        disable : function(){
32112           disabled = true;
32113           clearTimeout(showProc);
32114           clearTimeout(hideProc);
32115           clearTimeout(dismissProc);
32116           if(ce){
32117               hide(true);
32118           }
32119           locks.push(1);
32120        },
32121
32122     /**
32123      * Returns true if the quick tip is enabled, else false.
32124      */
32125        isEnabled : function(){
32126             return !disabled;
32127        },
32128
32129         // private
32130        tagConfig : {
32131            namespace : "ext",
32132            attribute : "qtip",
32133            width : "width",
32134            target : "target",
32135            title : "qtitle",
32136            hide : "hide",
32137            cls : "qclass"
32138        }
32139    };
32140 }();
32141
32142 // backwards compat
32143 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32144  * Based on:
32145  * Ext JS Library 1.1.1
32146  * Copyright(c) 2006-2007, Ext JS, LLC.
32147  *
32148  * Originally Released Under LGPL - original licence link has changed is not relivant.
32149  *
32150  * Fork - LGPL
32151  * <script type="text/javascript">
32152  */
32153  
32154
32155 /**
32156  * @class Roo.tree.TreePanel
32157  * @extends Roo.data.Tree
32158
32159  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32160  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32161  * @cfg {Boolean} enableDD true to enable drag and drop
32162  * @cfg {Boolean} enableDrag true to enable just drag
32163  * @cfg {Boolean} enableDrop true to enable just drop
32164  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32165  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32166  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32167  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32168  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32169  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32170  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32171  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32172  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32173  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32174  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32175  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32176  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32177  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32178  * @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>
32179  * @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>
32180  * 
32181  * @constructor
32182  * @param {String/HTMLElement/Element} el The container element
32183  * @param {Object} config
32184  */
32185 Roo.tree.TreePanel = function(el, config){
32186     var root = false;
32187     var loader = false;
32188     if (config.root) {
32189         root = config.root;
32190         delete config.root;
32191     }
32192     if (config.loader) {
32193         loader = config.loader;
32194         delete config.loader;
32195     }
32196     
32197     Roo.apply(this, config);
32198     Roo.tree.TreePanel.superclass.constructor.call(this);
32199     this.el = Roo.get(el);
32200     this.el.addClass('x-tree');
32201     //console.log(root);
32202     if (root) {
32203         this.setRootNode( Roo.factory(root, Roo.tree));
32204     }
32205     if (loader) {
32206         this.loader = Roo.factory(loader, Roo.tree);
32207     }
32208    /**
32209     * Read-only. The id of the container element becomes this TreePanel's id.
32210     */
32211     this.id = this.el.id;
32212     this.addEvents({
32213         /**
32214         * @event beforeload
32215         * Fires before a node is loaded, return false to cancel
32216         * @param {Node} node The node being loaded
32217         */
32218         "beforeload" : true,
32219         /**
32220         * @event load
32221         * Fires when a node is loaded
32222         * @param {Node} node The node that was loaded
32223         */
32224         "load" : true,
32225         /**
32226         * @event textchange
32227         * Fires when the text for a node is changed
32228         * @param {Node} node The node
32229         * @param {String} text The new text
32230         * @param {String} oldText The old text
32231         */
32232         "textchange" : true,
32233         /**
32234         * @event beforeexpand
32235         * Fires before a node is expanded, return false to cancel.
32236         * @param {Node} node The node
32237         * @param {Boolean} deep
32238         * @param {Boolean} anim
32239         */
32240         "beforeexpand" : true,
32241         /**
32242         * @event beforecollapse
32243         * Fires before a node is collapsed, return false to cancel.
32244         * @param {Node} node The node
32245         * @param {Boolean} deep
32246         * @param {Boolean} anim
32247         */
32248         "beforecollapse" : true,
32249         /**
32250         * @event expand
32251         * Fires when a node is expanded
32252         * @param {Node} node The node
32253         */
32254         "expand" : true,
32255         /**
32256         * @event disabledchange
32257         * Fires when the disabled status of a node changes
32258         * @param {Node} node The node
32259         * @param {Boolean} disabled
32260         */
32261         "disabledchange" : true,
32262         /**
32263         * @event collapse
32264         * Fires when a node is collapsed
32265         * @param {Node} node The node
32266         */
32267         "collapse" : true,
32268         /**
32269         * @event beforeclick
32270         * Fires before click processing on a node. Return false to cancel the default action.
32271         * @param {Node} node The node
32272         * @param {Roo.EventObject} e The event object
32273         */
32274         "beforeclick":true,
32275         /**
32276         * @event checkchange
32277         * Fires when a node with a checkbox's checked property changes
32278         * @param {Node} this This node
32279         * @param {Boolean} checked
32280         */
32281         "checkchange":true,
32282         /**
32283         * @event click
32284         * Fires when a node is clicked
32285         * @param {Node} node The node
32286         * @param {Roo.EventObject} e The event object
32287         */
32288         "click":true,
32289         /**
32290         * @event dblclick
32291         * Fires when a node is double clicked
32292         * @param {Node} node The node
32293         * @param {Roo.EventObject} e The event object
32294         */
32295         "dblclick":true,
32296         /**
32297         * @event contextmenu
32298         * Fires when a node is right clicked
32299         * @param {Node} node The node
32300         * @param {Roo.EventObject} e The event object
32301         */
32302         "contextmenu":true,
32303         /**
32304         * @event beforechildrenrendered
32305         * Fires right before the child nodes for a node are rendered
32306         * @param {Node} node The node
32307         */
32308         "beforechildrenrendered":true,
32309         /**
32310         * @event startdrag
32311         * Fires when a node starts being dragged
32312         * @param {Roo.tree.TreePanel} this
32313         * @param {Roo.tree.TreeNode} node
32314         * @param {event} e The raw browser event
32315         */ 
32316        "startdrag" : true,
32317        /**
32318         * @event enddrag
32319         * Fires when a drag operation is complete
32320         * @param {Roo.tree.TreePanel} this
32321         * @param {Roo.tree.TreeNode} node
32322         * @param {event} e The raw browser event
32323         */
32324        "enddrag" : true,
32325        /**
32326         * @event dragdrop
32327         * Fires when a dragged node is dropped on a valid DD target
32328         * @param {Roo.tree.TreePanel} this
32329         * @param {Roo.tree.TreeNode} node
32330         * @param {DD} dd The dd it was dropped on
32331         * @param {event} e The raw browser event
32332         */
32333        "dragdrop" : true,
32334        /**
32335         * @event beforenodedrop
32336         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32337         * passed to handlers has the following properties:<br />
32338         * <ul style="padding:5px;padding-left:16px;">
32339         * <li>tree - The TreePanel</li>
32340         * <li>target - The node being targeted for the drop</li>
32341         * <li>data - The drag data from the drag source</li>
32342         * <li>point - The point of the drop - append, above or below</li>
32343         * <li>source - The drag source</li>
32344         * <li>rawEvent - Raw mouse event</li>
32345         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32346         * to be inserted by setting them on this object.</li>
32347         * <li>cancel - Set this to true to cancel the drop.</li>
32348         * </ul>
32349         * @param {Object} dropEvent
32350         */
32351        "beforenodedrop" : true,
32352        /**
32353         * @event nodedrop
32354         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32355         * passed to handlers has the following properties:<br />
32356         * <ul style="padding:5px;padding-left:16px;">
32357         * <li>tree - The TreePanel</li>
32358         * <li>target - The node being targeted for the drop</li>
32359         * <li>data - The drag data from the drag source</li>
32360         * <li>point - The point of the drop - append, above or below</li>
32361         * <li>source - The drag source</li>
32362         * <li>rawEvent - Raw mouse event</li>
32363         * <li>dropNode - Dropped node(s).</li>
32364         * </ul>
32365         * @param {Object} dropEvent
32366         */
32367        "nodedrop" : true,
32368         /**
32369         * @event nodedragover
32370         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32371         * passed to handlers has the following properties:<br />
32372         * <ul style="padding:5px;padding-left:16px;">
32373         * <li>tree - The TreePanel</li>
32374         * <li>target - The node being targeted for the drop</li>
32375         * <li>data - The drag data from the drag source</li>
32376         * <li>point - The point of the drop - append, above or below</li>
32377         * <li>source - The drag source</li>
32378         * <li>rawEvent - Raw mouse event</li>
32379         * <li>dropNode - Drop node(s) provided by the source.</li>
32380         * <li>cancel - Set this to true to signal drop not allowed.</li>
32381         * </ul>
32382         * @param {Object} dragOverEvent
32383         */
32384        "nodedragover" : true
32385         
32386     });
32387     if(this.singleExpand){
32388        this.on("beforeexpand", this.restrictExpand, this);
32389     }
32390     if (this.editor) {
32391         this.editor.tree = this;
32392         this.editor = Roo.factory(this.editor, Roo.tree);
32393     }
32394     
32395     if (this.selModel) {
32396         this.selModel = Roo.factory(this.selModel, Roo.tree);
32397     }
32398    
32399 };
32400 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32401     rootVisible : true,
32402     animate: Roo.enableFx,
32403     lines : true,
32404     enableDD : false,
32405     hlDrop : Roo.enableFx,
32406   
32407     renderer: false,
32408     
32409     rendererTip: false,
32410     // private
32411     restrictExpand : function(node){
32412         var p = node.parentNode;
32413         if(p){
32414             if(p.expandedChild && p.expandedChild.parentNode == p){
32415                 p.expandedChild.collapse();
32416             }
32417             p.expandedChild = node;
32418         }
32419     },
32420
32421     // private override
32422     setRootNode : function(node){
32423         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32424         if(!this.rootVisible){
32425             node.ui = new Roo.tree.RootTreeNodeUI(node);
32426         }
32427         return node;
32428     },
32429
32430     /**
32431      * Returns the container element for this TreePanel
32432      */
32433     getEl : function(){
32434         return this.el;
32435     },
32436
32437     /**
32438      * Returns the default TreeLoader for this TreePanel
32439      */
32440     getLoader : function(){
32441         return this.loader;
32442     },
32443
32444     /**
32445      * Expand all nodes
32446      */
32447     expandAll : function(){
32448         this.root.expand(true);
32449     },
32450
32451     /**
32452      * Collapse all nodes
32453      */
32454     collapseAll : function(){
32455         this.root.collapse(true);
32456     },
32457
32458     /**
32459      * Returns the selection model used by this TreePanel
32460      */
32461     getSelectionModel : function(){
32462         if(!this.selModel){
32463             this.selModel = new Roo.tree.DefaultSelectionModel();
32464         }
32465         return this.selModel;
32466     },
32467
32468     /**
32469      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32470      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32471      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32472      * @return {Array}
32473      */
32474     getChecked : function(a, startNode){
32475         startNode = startNode || this.root;
32476         var r = [];
32477         var f = function(){
32478             if(this.attributes.checked){
32479                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32480             }
32481         }
32482         startNode.cascade(f);
32483         return r;
32484     },
32485
32486     /**
32487      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32488      * @param {String} path
32489      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32490      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32491      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32492      */
32493     expandPath : function(path, attr, callback){
32494         attr = attr || "id";
32495         var keys = path.split(this.pathSeparator);
32496         var curNode = this.root;
32497         if(curNode.attributes[attr] != keys[1]){ // invalid root
32498             if(callback){
32499                 callback(false, null);
32500             }
32501             return;
32502         }
32503         var index = 1;
32504         var f = function(){
32505             if(++index == keys.length){
32506                 if(callback){
32507                     callback(true, curNode);
32508                 }
32509                 return;
32510             }
32511             var c = curNode.findChild(attr, keys[index]);
32512             if(!c){
32513                 if(callback){
32514                     callback(false, curNode);
32515                 }
32516                 return;
32517             }
32518             curNode = c;
32519             c.expand(false, false, f);
32520         };
32521         curNode.expand(false, false, f);
32522     },
32523
32524     /**
32525      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32526      * @param {String} path
32527      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32528      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32529      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32530      */
32531     selectPath : function(path, attr, callback){
32532         attr = attr || "id";
32533         var keys = path.split(this.pathSeparator);
32534         var v = keys.pop();
32535         if(keys.length > 0){
32536             var f = function(success, node){
32537                 if(success && node){
32538                     var n = node.findChild(attr, v);
32539                     if(n){
32540                         n.select();
32541                         if(callback){
32542                             callback(true, n);
32543                         }
32544                     }else if(callback){
32545                         callback(false, n);
32546                     }
32547                 }else{
32548                     if(callback){
32549                         callback(false, n);
32550                     }
32551                 }
32552             };
32553             this.expandPath(keys.join(this.pathSeparator), attr, f);
32554         }else{
32555             this.root.select();
32556             if(callback){
32557                 callback(true, this.root);
32558             }
32559         }
32560     },
32561
32562     getTreeEl : function(){
32563         return this.el;
32564     },
32565
32566     /**
32567      * Trigger rendering of this TreePanel
32568      */
32569     render : function(){
32570         if (this.innerCt) {
32571             return this; // stop it rendering more than once!!
32572         }
32573         
32574         this.innerCt = this.el.createChild({tag:"ul",
32575                cls:"x-tree-root-ct " +
32576                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32577
32578         if(this.containerScroll){
32579             Roo.dd.ScrollManager.register(this.el);
32580         }
32581         if((this.enableDD || this.enableDrop) && !this.dropZone){
32582            /**
32583             * The dropZone used by this tree if drop is enabled
32584             * @type Roo.tree.TreeDropZone
32585             */
32586              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32587                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32588            });
32589         }
32590         if((this.enableDD || this.enableDrag) && !this.dragZone){
32591            /**
32592             * The dragZone used by this tree if drag is enabled
32593             * @type Roo.tree.TreeDragZone
32594             */
32595             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32596                ddGroup: this.ddGroup || "TreeDD",
32597                scroll: this.ddScroll
32598            });
32599         }
32600         this.getSelectionModel().init(this);
32601         if (!this.root) {
32602             Roo.log("ROOT not set in tree");
32603             return this;
32604         }
32605         this.root.render();
32606         if(!this.rootVisible){
32607             this.root.renderChildren();
32608         }
32609         return this;
32610     }
32611 });/*
32612  * Based on:
32613  * Ext JS Library 1.1.1
32614  * Copyright(c) 2006-2007, Ext JS, LLC.
32615  *
32616  * Originally Released Under LGPL - original licence link has changed is not relivant.
32617  *
32618  * Fork - LGPL
32619  * <script type="text/javascript">
32620  */
32621  
32622
32623 /**
32624  * @class Roo.tree.DefaultSelectionModel
32625  * @extends Roo.util.Observable
32626  * The default single selection for a TreePanel.
32627  * @param {Object} cfg Configuration
32628  */
32629 Roo.tree.DefaultSelectionModel = function(cfg){
32630    this.selNode = null;
32631    
32632    
32633    
32634    this.addEvents({
32635        /**
32636         * @event selectionchange
32637         * Fires when the selected node changes
32638         * @param {DefaultSelectionModel} this
32639         * @param {TreeNode} node the new selection
32640         */
32641        "selectionchange" : true,
32642
32643        /**
32644         * @event beforeselect
32645         * Fires before the selected node changes, return false to cancel the change
32646         * @param {DefaultSelectionModel} this
32647         * @param {TreeNode} node the new selection
32648         * @param {TreeNode} node the old selection
32649         */
32650        "beforeselect" : true
32651    });
32652    
32653     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32654 };
32655
32656 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32657     init : function(tree){
32658         this.tree = tree;
32659         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32660         tree.on("click", this.onNodeClick, this);
32661     },
32662     
32663     onNodeClick : function(node, e){
32664         if (e.ctrlKey && this.selNode == node)  {
32665             this.unselect(node);
32666             return;
32667         }
32668         this.select(node);
32669     },
32670     
32671     /**
32672      * Select a node.
32673      * @param {TreeNode} node The node to select
32674      * @return {TreeNode} The selected node
32675      */
32676     select : function(node){
32677         var last = this.selNode;
32678         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32679             if(last){
32680                 last.ui.onSelectedChange(false);
32681             }
32682             this.selNode = node;
32683             node.ui.onSelectedChange(true);
32684             this.fireEvent("selectionchange", this, node, last);
32685         }
32686         return node;
32687     },
32688     
32689     /**
32690      * Deselect a node.
32691      * @param {TreeNode} node The node to unselect
32692      */
32693     unselect : function(node){
32694         if(this.selNode == node){
32695             this.clearSelections();
32696         }    
32697     },
32698     
32699     /**
32700      * Clear all selections
32701      */
32702     clearSelections : function(){
32703         var n = this.selNode;
32704         if(n){
32705             n.ui.onSelectedChange(false);
32706             this.selNode = null;
32707             this.fireEvent("selectionchange", this, null);
32708         }
32709         return n;
32710     },
32711     
32712     /**
32713      * Get the selected node
32714      * @return {TreeNode} The selected node
32715      */
32716     getSelectedNode : function(){
32717         return this.selNode;    
32718     },
32719     
32720     /**
32721      * Returns true if the node is selected
32722      * @param {TreeNode} node The node to check
32723      * @return {Boolean}
32724      */
32725     isSelected : function(node){
32726         return this.selNode == node;  
32727     },
32728
32729     /**
32730      * Selects the node above the selected node in the tree, intelligently walking the nodes
32731      * @return TreeNode The new selection
32732      */
32733     selectPrevious : function(){
32734         var s = this.selNode || this.lastSelNode;
32735         if(!s){
32736             return null;
32737         }
32738         var ps = s.previousSibling;
32739         if(ps){
32740             if(!ps.isExpanded() || ps.childNodes.length < 1){
32741                 return this.select(ps);
32742             } else{
32743                 var lc = ps.lastChild;
32744                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32745                     lc = lc.lastChild;
32746                 }
32747                 return this.select(lc);
32748             }
32749         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32750             return this.select(s.parentNode);
32751         }
32752         return null;
32753     },
32754
32755     /**
32756      * Selects the node above the selected node in the tree, intelligently walking the nodes
32757      * @return TreeNode The new selection
32758      */
32759     selectNext : function(){
32760         var s = this.selNode || this.lastSelNode;
32761         if(!s){
32762             return null;
32763         }
32764         if(s.firstChild && s.isExpanded()){
32765              return this.select(s.firstChild);
32766          }else if(s.nextSibling){
32767              return this.select(s.nextSibling);
32768          }else if(s.parentNode){
32769             var newS = null;
32770             s.parentNode.bubble(function(){
32771                 if(this.nextSibling){
32772                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32773                     return false;
32774                 }
32775             });
32776             return newS;
32777          }
32778         return null;
32779     },
32780
32781     onKeyDown : function(e){
32782         var s = this.selNode || this.lastSelNode;
32783         // undesirable, but required
32784         var sm = this;
32785         if(!s){
32786             return;
32787         }
32788         var k = e.getKey();
32789         switch(k){
32790              case e.DOWN:
32791                  e.stopEvent();
32792                  this.selectNext();
32793              break;
32794              case e.UP:
32795                  e.stopEvent();
32796                  this.selectPrevious();
32797              break;
32798              case e.RIGHT:
32799                  e.preventDefault();
32800                  if(s.hasChildNodes()){
32801                      if(!s.isExpanded()){
32802                          s.expand();
32803                      }else if(s.firstChild){
32804                          this.select(s.firstChild, e);
32805                      }
32806                  }
32807              break;
32808              case e.LEFT:
32809                  e.preventDefault();
32810                  if(s.hasChildNodes() && s.isExpanded()){
32811                      s.collapse();
32812                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32813                      this.select(s.parentNode, e);
32814                  }
32815              break;
32816         };
32817     }
32818 });
32819
32820 /**
32821  * @class Roo.tree.MultiSelectionModel
32822  * @extends Roo.util.Observable
32823  * Multi selection for a TreePanel.
32824  * @param {Object} cfg Configuration
32825  */
32826 Roo.tree.MultiSelectionModel = function(){
32827    this.selNodes = [];
32828    this.selMap = {};
32829    this.addEvents({
32830        /**
32831         * @event selectionchange
32832         * Fires when the selected nodes change
32833         * @param {MultiSelectionModel} this
32834         * @param {Array} nodes Array of the selected nodes
32835         */
32836        "selectionchange" : true
32837    });
32838    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32839    
32840 };
32841
32842 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32843     init : function(tree){
32844         this.tree = tree;
32845         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32846         tree.on("click", this.onNodeClick, this);
32847     },
32848     
32849     onNodeClick : function(node, e){
32850         this.select(node, e, e.ctrlKey);
32851     },
32852     
32853     /**
32854      * Select a node.
32855      * @param {TreeNode} node The node to select
32856      * @param {EventObject} e (optional) An event associated with the selection
32857      * @param {Boolean} keepExisting True to retain existing selections
32858      * @return {TreeNode} The selected node
32859      */
32860     select : function(node, e, keepExisting){
32861         if(keepExisting !== true){
32862             this.clearSelections(true);
32863         }
32864         if(this.isSelected(node)){
32865             this.lastSelNode = node;
32866             return node;
32867         }
32868         this.selNodes.push(node);
32869         this.selMap[node.id] = node;
32870         this.lastSelNode = node;
32871         node.ui.onSelectedChange(true);
32872         this.fireEvent("selectionchange", this, this.selNodes);
32873         return node;
32874     },
32875     
32876     /**
32877      * Deselect a node.
32878      * @param {TreeNode} node The node to unselect
32879      */
32880     unselect : function(node){
32881         if(this.selMap[node.id]){
32882             node.ui.onSelectedChange(false);
32883             var sn = this.selNodes;
32884             var index = -1;
32885             if(sn.indexOf){
32886                 index = sn.indexOf(node);
32887             }else{
32888                 for(var i = 0, len = sn.length; i < len; i++){
32889                     if(sn[i] == node){
32890                         index = i;
32891                         break;
32892                     }
32893                 }
32894             }
32895             if(index != -1){
32896                 this.selNodes.splice(index, 1);
32897             }
32898             delete this.selMap[node.id];
32899             this.fireEvent("selectionchange", this, this.selNodes);
32900         }
32901     },
32902     
32903     /**
32904      * Clear all selections
32905      */
32906     clearSelections : function(suppressEvent){
32907         var sn = this.selNodes;
32908         if(sn.length > 0){
32909             for(var i = 0, len = sn.length; i < len; i++){
32910                 sn[i].ui.onSelectedChange(false);
32911             }
32912             this.selNodes = [];
32913             this.selMap = {};
32914             if(suppressEvent !== true){
32915                 this.fireEvent("selectionchange", this, this.selNodes);
32916             }
32917         }
32918     },
32919     
32920     /**
32921      * Returns true if the node is selected
32922      * @param {TreeNode} node The node to check
32923      * @return {Boolean}
32924      */
32925     isSelected : function(node){
32926         return this.selMap[node.id] ? true : false;  
32927     },
32928     
32929     /**
32930      * Returns an array of the selected nodes
32931      * @return {Array}
32932      */
32933     getSelectedNodes : function(){
32934         return this.selNodes;    
32935     },
32936
32937     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32938
32939     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32940
32941     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32942 });/*
32943  * Based on:
32944  * Ext JS Library 1.1.1
32945  * Copyright(c) 2006-2007, Ext JS, LLC.
32946  *
32947  * Originally Released Under LGPL - original licence link has changed is not relivant.
32948  *
32949  * Fork - LGPL
32950  * <script type="text/javascript">
32951  */
32952  
32953 /**
32954  * @class Roo.tree.TreeNode
32955  * @extends Roo.data.Node
32956  * @cfg {String} text The text for this node
32957  * @cfg {Boolean} expanded true to start the node expanded
32958  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32959  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32960  * @cfg {Boolean} disabled true to start the node disabled
32961  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32962  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32963  * @cfg {String} cls A css class to be added to the node
32964  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32965  * @cfg {String} href URL of the link used for the node (defaults to #)
32966  * @cfg {String} hrefTarget target frame for the link
32967  * @cfg {String} qtip An Ext QuickTip for the node
32968  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32969  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32970  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32971  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32972  * (defaults to undefined with no checkbox rendered)
32973  * @constructor
32974  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32975  */
32976 Roo.tree.TreeNode = function(attributes){
32977     attributes = attributes || {};
32978     if(typeof attributes == "string"){
32979         attributes = {text: attributes};
32980     }
32981     this.childrenRendered = false;
32982     this.rendered = false;
32983     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32984     this.expanded = attributes.expanded === true;
32985     this.isTarget = attributes.isTarget !== false;
32986     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32987     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32988
32989     /**
32990      * Read-only. The text for this node. To change it use setText().
32991      * @type String
32992      */
32993     this.text = attributes.text;
32994     /**
32995      * True if this node is disabled.
32996      * @type Boolean
32997      */
32998     this.disabled = attributes.disabled === true;
32999
33000     this.addEvents({
33001         /**
33002         * @event textchange
33003         * Fires when the text for this node is changed
33004         * @param {Node} this This node
33005         * @param {String} text The new text
33006         * @param {String} oldText The old text
33007         */
33008         "textchange" : true,
33009         /**
33010         * @event beforeexpand
33011         * Fires before this node is expanded, return false to cancel.
33012         * @param {Node} this This node
33013         * @param {Boolean} deep
33014         * @param {Boolean} anim
33015         */
33016         "beforeexpand" : true,
33017         /**
33018         * @event beforecollapse
33019         * Fires before this node is collapsed, return false to cancel.
33020         * @param {Node} this This node
33021         * @param {Boolean} deep
33022         * @param {Boolean} anim
33023         */
33024         "beforecollapse" : true,
33025         /**
33026         * @event expand
33027         * Fires when this node is expanded
33028         * @param {Node} this This node
33029         */
33030         "expand" : true,
33031         /**
33032         * @event disabledchange
33033         * Fires when the disabled status of this node changes
33034         * @param {Node} this This node
33035         * @param {Boolean} disabled
33036         */
33037         "disabledchange" : true,
33038         /**
33039         * @event collapse
33040         * Fires when this node is collapsed
33041         * @param {Node} this This node
33042         */
33043         "collapse" : true,
33044         /**
33045         * @event beforeclick
33046         * Fires before click processing. Return false to cancel the default action.
33047         * @param {Node} this This node
33048         * @param {Roo.EventObject} e The event object
33049         */
33050         "beforeclick":true,
33051         /**
33052         * @event checkchange
33053         * Fires when a node with a checkbox's checked property changes
33054         * @param {Node} this This node
33055         * @param {Boolean} checked
33056         */
33057         "checkchange":true,
33058         /**
33059         * @event click
33060         * Fires when this node is clicked
33061         * @param {Node} this This node
33062         * @param {Roo.EventObject} e The event object
33063         */
33064         "click":true,
33065         /**
33066         * @event dblclick
33067         * Fires when this node is double clicked
33068         * @param {Node} this This node
33069         * @param {Roo.EventObject} e The event object
33070         */
33071         "dblclick":true,
33072         /**
33073         * @event contextmenu
33074         * Fires when this node is right clicked
33075         * @param {Node} this This node
33076         * @param {Roo.EventObject} e The event object
33077         */
33078         "contextmenu":true,
33079         /**
33080         * @event beforechildrenrendered
33081         * Fires right before the child nodes for this node are rendered
33082         * @param {Node} this This node
33083         */
33084         "beforechildrenrendered":true
33085     });
33086
33087     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33088
33089     /**
33090      * Read-only. The UI for this node
33091      * @type TreeNodeUI
33092      */
33093     this.ui = new uiClass(this);
33094     
33095     // finally support items[]
33096     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33097         return;
33098     }
33099     
33100     
33101     Roo.each(this.attributes.items, function(c) {
33102         this.appendChild(Roo.factory(c,Roo.Tree));
33103     }, this);
33104     delete this.attributes.items;
33105     
33106     
33107     
33108 };
33109 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33110     preventHScroll: true,
33111     /**
33112      * Returns true if this node is expanded
33113      * @return {Boolean}
33114      */
33115     isExpanded : function(){
33116         return this.expanded;
33117     },
33118
33119     /**
33120      * Returns the UI object for this node
33121      * @return {TreeNodeUI}
33122      */
33123     getUI : function(){
33124         return this.ui;
33125     },
33126
33127     // private override
33128     setFirstChild : function(node){
33129         var of = this.firstChild;
33130         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33131         if(this.childrenRendered && of && node != of){
33132             of.renderIndent(true, true);
33133         }
33134         if(this.rendered){
33135             this.renderIndent(true, true);
33136         }
33137     },
33138
33139     // private override
33140     setLastChild : function(node){
33141         var ol = this.lastChild;
33142         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33143         if(this.childrenRendered && ol && node != ol){
33144             ol.renderIndent(true, true);
33145         }
33146         if(this.rendered){
33147             this.renderIndent(true, true);
33148         }
33149     },
33150
33151     // these methods are overridden to provide lazy rendering support
33152     // private override
33153     appendChild : function()
33154     {
33155         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33156         if(node && this.childrenRendered){
33157             node.render();
33158         }
33159         this.ui.updateExpandIcon();
33160         return node;
33161     },
33162
33163     // private override
33164     removeChild : function(node){
33165         this.ownerTree.getSelectionModel().unselect(node);
33166         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33167         // if it's been rendered remove dom node
33168         if(this.childrenRendered){
33169             node.ui.remove();
33170         }
33171         if(this.childNodes.length < 1){
33172             this.collapse(false, false);
33173         }else{
33174             this.ui.updateExpandIcon();
33175         }
33176         if(!this.firstChild) {
33177             this.childrenRendered = false;
33178         }
33179         return node;
33180     },
33181
33182     // private override
33183     insertBefore : function(node, refNode){
33184         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33185         if(newNode && refNode && this.childrenRendered){
33186             node.render();
33187         }
33188         this.ui.updateExpandIcon();
33189         return newNode;
33190     },
33191
33192     /**
33193      * Sets the text for this node
33194      * @param {String} text
33195      */
33196     setText : function(text){
33197         var oldText = this.text;
33198         this.text = text;
33199         this.attributes.text = text;
33200         if(this.rendered){ // event without subscribing
33201             this.ui.onTextChange(this, text, oldText);
33202         }
33203         this.fireEvent("textchange", this, text, oldText);
33204     },
33205
33206     /**
33207      * Triggers selection of this node
33208      */
33209     select : function(){
33210         this.getOwnerTree().getSelectionModel().select(this);
33211     },
33212
33213     /**
33214      * Triggers deselection of this node
33215      */
33216     unselect : function(){
33217         this.getOwnerTree().getSelectionModel().unselect(this);
33218     },
33219
33220     /**
33221      * Returns true if this node is selected
33222      * @return {Boolean}
33223      */
33224     isSelected : function(){
33225         return this.getOwnerTree().getSelectionModel().isSelected(this);
33226     },
33227
33228     /**
33229      * Expand this node.
33230      * @param {Boolean} deep (optional) True to expand all children as well
33231      * @param {Boolean} anim (optional) false to cancel the default animation
33232      * @param {Function} callback (optional) A callback to be called when
33233      * expanding this node completes (does not wait for deep expand to complete).
33234      * Called with 1 parameter, this node.
33235      */
33236     expand : function(deep, anim, callback){
33237         if(!this.expanded){
33238             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33239                 return;
33240             }
33241             if(!this.childrenRendered){
33242                 this.renderChildren();
33243             }
33244             this.expanded = true;
33245             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33246                 this.ui.animExpand(function(){
33247                     this.fireEvent("expand", this);
33248                     if(typeof callback == "function"){
33249                         callback(this);
33250                     }
33251                     if(deep === true){
33252                         this.expandChildNodes(true);
33253                     }
33254                 }.createDelegate(this));
33255                 return;
33256             }else{
33257                 this.ui.expand();
33258                 this.fireEvent("expand", this);
33259                 if(typeof callback == "function"){
33260                     callback(this);
33261                 }
33262             }
33263         }else{
33264            if(typeof callback == "function"){
33265                callback(this);
33266            }
33267         }
33268         if(deep === true){
33269             this.expandChildNodes(true);
33270         }
33271     },
33272
33273     isHiddenRoot : function(){
33274         return this.isRoot && !this.getOwnerTree().rootVisible;
33275     },
33276
33277     /**
33278      * Collapse this node.
33279      * @param {Boolean} deep (optional) True to collapse all children as well
33280      * @param {Boolean} anim (optional) false to cancel the default animation
33281      */
33282     collapse : function(deep, anim){
33283         if(this.expanded && !this.isHiddenRoot()){
33284             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33285                 return;
33286             }
33287             this.expanded = false;
33288             if((this.getOwnerTree().animate && anim !== false) || anim){
33289                 this.ui.animCollapse(function(){
33290                     this.fireEvent("collapse", this);
33291                     if(deep === true){
33292                         this.collapseChildNodes(true);
33293                     }
33294                 }.createDelegate(this));
33295                 return;
33296             }else{
33297                 this.ui.collapse();
33298                 this.fireEvent("collapse", this);
33299             }
33300         }
33301         if(deep === true){
33302             var cs = this.childNodes;
33303             for(var i = 0, len = cs.length; i < len; i++) {
33304                 cs[i].collapse(true, false);
33305             }
33306         }
33307     },
33308
33309     // private
33310     delayedExpand : function(delay){
33311         if(!this.expandProcId){
33312             this.expandProcId = this.expand.defer(delay, this);
33313         }
33314     },
33315
33316     // private
33317     cancelExpand : function(){
33318         if(this.expandProcId){
33319             clearTimeout(this.expandProcId);
33320         }
33321         this.expandProcId = false;
33322     },
33323
33324     /**
33325      * Toggles expanded/collapsed state of the node
33326      */
33327     toggle : function(){
33328         if(this.expanded){
33329             this.collapse();
33330         }else{
33331             this.expand();
33332         }
33333     },
33334
33335     /**
33336      * Ensures all parent nodes are expanded
33337      */
33338     ensureVisible : function(callback){
33339         var tree = this.getOwnerTree();
33340         tree.expandPath(this.parentNode.getPath(), false, function(){
33341             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33342             Roo.callback(callback);
33343         }.createDelegate(this));
33344     },
33345
33346     /**
33347      * Expand all child nodes
33348      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33349      */
33350     expandChildNodes : function(deep){
33351         var cs = this.childNodes;
33352         for(var i = 0, len = cs.length; i < len; i++) {
33353                 cs[i].expand(deep);
33354         }
33355     },
33356
33357     /**
33358      * Collapse all child nodes
33359      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33360      */
33361     collapseChildNodes : function(deep){
33362         var cs = this.childNodes;
33363         for(var i = 0, len = cs.length; i < len; i++) {
33364                 cs[i].collapse(deep);
33365         }
33366     },
33367
33368     /**
33369      * Disables this node
33370      */
33371     disable : function(){
33372         this.disabled = true;
33373         this.unselect();
33374         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33375             this.ui.onDisableChange(this, true);
33376         }
33377         this.fireEvent("disabledchange", this, true);
33378     },
33379
33380     /**
33381      * Enables this node
33382      */
33383     enable : function(){
33384         this.disabled = false;
33385         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33386             this.ui.onDisableChange(this, false);
33387         }
33388         this.fireEvent("disabledchange", this, false);
33389     },
33390
33391     // private
33392     renderChildren : function(suppressEvent){
33393         if(suppressEvent !== false){
33394             this.fireEvent("beforechildrenrendered", this);
33395         }
33396         var cs = this.childNodes;
33397         for(var i = 0, len = cs.length; i < len; i++){
33398             cs[i].render(true);
33399         }
33400         this.childrenRendered = true;
33401     },
33402
33403     // private
33404     sort : function(fn, scope){
33405         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33406         if(this.childrenRendered){
33407             var cs = this.childNodes;
33408             for(var i = 0, len = cs.length; i < len; i++){
33409                 cs[i].render(true);
33410             }
33411         }
33412     },
33413
33414     // private
33415     render : function(bulkRender){
33416         this.ui.render(bulkRender);
33417         if(!this.rendered){
33418             this.rendered = true;
33419             if(this.expanded){
33420                 this.expanded = false;
33421                 this.expand(false, false);
33422             }
33423         }
33424     },
33425
33426     // private
33427     renderIndent : function(deep, refresh){
33428         if(refresh){
33429             this.ui.childIndent = null;
33430         }
33431         this.ui.renderIndent();
33432         if(deep === true && this.childrenRendered){
33433             var cs = this.childNodes;
33434             for(var i = 0, len = cs.length; i < len; i++){
33435                 cs[i].renderIndent(true, refresh);
33436             }
33437         }
33438     }
33439 });/*
33440  * Based on:
33441  * Ext JS Library 1.1.1
33442  * Copyright(c) 2006-2007, Ext JS, LLC.
33443  *
33444  * Originally Released Under LGPL - original licence link has changed is not relivant.
33445  *
33446  * Fork - LGPL
33447  * <script type="text/javascript">
33448  */
33449  
33450 /**
33451  * @class Roo.tree.AsyncTreeNode
33452  * @extends Roo.tree.TreeNode
33453  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33454  * @constructor
33455  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33456  */
33457  Roo.tree.AsyncTreeNode = function(config){
33458     this.loaded = false;
33459     this.loading = false;
33460     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33461     /**
33462     * @event beforeload
33463     * Fires before this node is loaded, return false to cancel
33464     * @param {Node} this This node
33465     */
33466     this.addEvents({'beforeload':true, 'load': true});
33467     /**
33468     * @event load
33469     * Fires when this node is loaded
33470     * @param {Node} this This node
33471     */
33472     /**
33473      * The loader used by this node (defaults to using the tree's defined loader)
33474      * @type TreeLoader
33475      * @property loader
33476      */
33477 };
33478 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33479     expand : function(deep, anim, callback){
33480         if(this.loading){ // if an async load is already running, waiting til it's done
33481             var timer;
33482             var f = function(){
33483                 if(!this.loading){ // done loading
33484                     clearInterval(timer);
33485                     this.expand(deep, anim, callback);
33486                 }
33487             }.createDelegate(this);
33488             timer = setInterval(f, 200);
33489             return;
33490         }
33491         if(!this.loaded){
33492             if(this.fireEvent("beforeload", this) === false){
33493                 return;
33494             }
33495             this.loading = true;
33496             this.ui.beforeLoad(this);
33497             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33498             if(loader){
33499                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33500                 return;
33501             }
33502         }
33503         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33504     },
33505     
33506     /**
33507      * Returns true if this node is currently loading
33508      * @return {Boolean}
33509      */
33510     isLoading : function(){
33511         return this.loading;  
33512     },
33513     
33514     loadComplete : function(deep, anim, callback){
33515         this.loading = false;
33516         this.loaded = true;
33517         this.ui.afterLoad(this);
33518         this.fireEvent("load", this);
33519         this.expand(deep, anim, callback);
33520     },
33521     
33522     /**
33523      * Returns true if this node has been loaded
33524      * @return {Boolean}
33525      */
33526     isLoaded : function(){
33527         return this.loaded;
33528     },
33529     
33530     hasChildNodes : function(){
33531         if(!this.isLeaf() && !this.loaded){
33532             return true;
33533         }else{
33534             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33535         }
33536     },
33537
33538     /**
33539      * Trigger a reload for this node
33540      * @param {Function} callback
33541      */
33542     reload : function(callback){
33543         this.collapse(false, false);
33544         while(this.firstChild){
33545             this.removeChild(this.firstChild);
33546         }
33547         this.childrenRendered = false;
33548         this.loaded = false;
33549         if(this.isHiddenRoot()){
33550             this.expanded = false;
33551         }
33552         this.expand(false, false, callback);
33553     }
33554 });/*
33555  * Based on:
33556  * Ext JS Library 1.1.1
33557  * Copyright(c) 2006-2007, Ext JS, LLC.
33558  *
33559  * Originally Released Under LGPL - original licence link has changed is not relivant.
33560  *
33561  * Fork - LGPL
33562  * <script type="text/javascript">
33563  */
33564  
33565 /**
33566  * @class Roo.tree.TreeNodeUI
33567  * @constructor
33568  * @param {Object} node The node to render
33569  * The TreeNode UI implementation is separate from the
33570  * tree implementation. Unless you are customizing the tree UI,
33571  * you should never have to use this directly.
33572  */
33573 Roo.tree.TreeNodeUI = function(node){
33574     this.node = node;
33575     this.rendered = false;
33576     this.animating = false;
33577     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33578 };
33579
33580 Roo.tree.TreeNodeUI.prototype = {
33581     removeChild : function(node){
33582         if(this.rendered){
33583             this.ctNode.removeChild(node.ui.getEl());
33584         }
33585     },
33586
33587     beforeLoad : function(){
33588          this.addClass("x-tree-node-loading");
33589     },
33590
33591     afterLoad : function(){
33592          this.removeClass("x-tree-node-loading");
33593     },
33594
33595     onTextChange : function(node, text, oldText){
33596         if(this.rendered){
33597             this.textNode.innerHTML = text;
33598         }
33599     },
33600
33601     onDisableChange : function(node, state){
33602         this.disabled = state;
33603         if(state){
33604             this.addClass("x-tree-node-disabled");
33605         }else{
33606             this.removeClass("x-tree-node-disabled");
33607         }
33608     },
33609
33610     onSelectedChange : function(state){
33611         if(state){
33612             this.focus();
33613             this.addClass("x-tree-selected");
33614         }else{
33615             //this.blur();
33616             this.removeClass("x-tree-selected");
33617         }
33618     },
33619
33620     onMove : function(tree, node, oldParent, newParent, index, refNode){
33621         this.childIndent = null;
33622         if(this.rendered){
33623             var targetNode = newParent.ui.getContainer();
33624             if(!targetNode){//target not rendered
33625                 this.holder = document.createElement("div");
33626                 this.holder.appendChild(this.wrap);
33627                 return;
33628             }
33629             var insertBefore = refNode ? refNode.ui.getEl() : null;
33630             if(insertBefore){
33631                 targetNode.insertBefore(this.wrap, insertBefore);
33632             }else{
33633                 targetNode.appendChild(this.wrap);
33634             }
33635             this.node.renderIndent(true);
33636         }
33637     },
33638
33639     addClass : function(cls){
33640         if(this.elNode){
33641             Roo.fly(this.elNode).addClass(cls);
33642         }
33643     },
33644
33645     removeClass : function(cls){
33646         if(this.elNode){
33647             Roo.fly(this.elNode).removeClass(cls);
33648         }
33649     },
33650
33651     remove : function(){
33652         if(this.rendered){
33653             this.holder = document.createElement("div");
33654             this.holder.appendChild(this.wrap);
33655         }
33656     },
33657
33658     fireEvent : function(){
33659         return this.node.fireEvent.apply(this.node, arguments);
33660     },
33661
33662     initEvents : function(){
33663         this.node.on("move", this.onMove, this);
33664         var E = Roo.EventManager;
33665         var a = this.anchor;
33666
33667         var el = Roo.fly(a, '_treeui');
33668
33669         if(Roo.isOpera){ // opera render bug ignores the CSS
33670             el.setStyle("text-decoration", "none");
33671         }
33672
33673         el.on("click", this.onClick, this);
33674         el.on("dblclick", this.onDblClick, this);
33675
33676         if(this.checkbox){
33677             Roo.EventManager.on(this.checkbox,
33678                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33679         }
33680
33681         el.on("contextmenu", this.onContextMenu, this);
33682
33683         var icon = Roo.fly(this.iconNode);
33684         icon.on("click", this.onClick, this);
33685         icon.on("dblclick", this.onDblClick, this);
33686         icon.on("contextmenu", this.onContextMenu, this);
33687         E.on(this.ecNode, "click", this.ecClick, this, true);
33688
33689         if(this.node.disabled){
33690             this.addClass("x-tree-node-disabled");
33691         }
33692         if(this.node.hidden){
33693             this.addClass("x-tree-node-disabled");
33694         }
33695         var ot = this.node.getOwnerTree();
33696         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33697         if(dd && (!this.node.isRoot || ot.rootVisible)){
33698             Roo.dd.Registry.register(this.elNode, {
33699                 node: this.node,
33700                 handles: this.getDDHandles(),
33701                 isHandle: false
33702             });
33703         }
33704     },
33705
33706     getDDHandles : function(){
33707         return [this.iconNode, this.textNode];
33708     },
33709
33710     hide : function(){
33711         if(this.rendered){
33712             this.wrap.style.display = "none";
33713         }
33714     },
33715
33716     show : function(){
33717         if(this.rendered){
33718             this.wrap.style.display = "";
33719         }
33720     },
33721
33722     onContextMenu : function(e){
33723         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33724             e.preventDefault();
33725             this.focus();
33726             this.fireEvent("contextmenu", this.node, e);
33727         }
33728     },
33729
33730     onClick : function(e){
33731         if(this.dropping){
33732             e.stopEvent();
33733             return;
33734         }
33735         if(this.fireEvent("beforeclick", this.node, e) !== false){
33736             if(!this.disabled && this.node.attributes.href){
33737                 this.fireEvent("click", this.node, e);
33738                 return;
33739             }
33740             e.preventDefault();
33741             if(this.disabled){
33742                 return;
33743             }
33744
33745             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33746                 this.node.toggle();
33747             }
33748
33749             this.fireEvent("click", this.node, e);
33750         }else{
33751             e.stopEvent();
33752         }
33753     },
33754
33755     onDblClick : function(e){
33756         e.preventDefault();
33757         if(this.disabled){
33758             return;
33759         }
33760         if(this.checkbox){
33761             this.toggleCheck();
33762         }
33763         if(!this.animating && this.node.hasChildNodes()){
33764             this.node.toggle();
33765         }
33766         this.fireEvent("dblclick", this.node, e);
33767     },
33768
33769     onCheckChange : function(){
33770         var checked = this.checkbox.checked;
33771         this.node.attributes.checked = checked;
33772         this.fireEvent('checkchange', this.node, checked);
33773     },
33774
33775     ecClick : function(e){
33776         if(!this.animating && this.node.hasChildNodes()){
33777             this.node.toggle();
33778         }
33779     },
33780
33781     startDrop : function(){
33782         this.dropping = true;
33783     },
33784
33785     // delayed drop so the click event doesn't get fired on a drop
33786     endDrop : function(){
33787        setTimeout(function(){
33788            this.dropping = false;
33789        }.createDelegate(this), 50);
33790     },
33791
33792     expand : function(){
33793         this.updateExpandIcon();
33794         this.ctNode.style.display = "";
33795     },
33796
33797     focus : function(){
33798         if(!this.node.preventHScroll){
33799             try{this.anchor.focus();
33800             }catch(e){}
33801         }else if(!Roo.isIE){
33802             try{
33803                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33804                 var l = noscroll.scrollLeft;
33805                 this.anchor.focus();
33806                 noscroll.scrollLeft = l;
33807             }catch(e){}
33808         }
33809     },
33810
33811     toggleCheck : function(value){
33812         var cb = this.checkbox;
33813         if(cb){
33814             cb.checked = (value === undefined ? !cb.checked : value);
33815         }
33816     },
33817
33818     blur : function(){
33819         try{
33820             this.anchor.blur();
33821         }catch(e){}
33822     },
33823
33824     animExpand : function(callback){
33825         var ct = Roo.get(this.ctNode);
33826         ct.stopFx();
33827         if(!this.node.hasChildNodes()){
33828             this.updateExpandIcon();
33829             this.ctNode.style.display = "";
33830             Roo.callback(callback);
33831             return;
33832         }
33833         this.animating = true;
33834         this.updateExpandIcon();
33835
33836         ct.slideIn('t', {
33837            callback : function(){
33838                this.animating = false;
33839                Roo.callback(callback);
33840             },
33841             scope: this,
33842             duration: this.node.ownerTree.duration || .25
33843         });
33844     },
33845
33846     highlight : function(){
33847         var tree = this.node.getOwnerTree();
33848         Roo.fly(this.wrap).highlight(
33849             tree.hlColor || "C3DAF9",
33850             {endColor: tree.hlBaseColor}
33851         );
33852     },
33853
33854     collapse : function(){
33855         this.updateExpandIcon();
33856         this.ctNode.style.display = "none";
33857     },
33858
33859     animCollapse : function(callback){
33860         var ct = Roo.get(this.ctNode);
33861         ct.enableDisplayMode('block');
33862         ct.stopFx();
33863
33864         this.animating = true;
33865         this.updateExpandIcon();
33866
33867         ct.slideOut('t', {
33868             callback : function(){
33869                this.animating = false;
33870                Roo.callback(callback);
33871             },
33872             scope: this,
33873             duration: this.node.ownerTree.duration || .25
33874         });
33875     },
33876
33877     getContainer : function(){
33878         return this.ctNode;
33879     },
33880
33881     getEl : function(){
33882         return this.wrap;
33883     },
33884
33885     appendDDGhost : function(ghostNode){
33886         ghostNode.appendChild(this.elNode.cloneNode(true));
33887     },
33888
33889     getDDRepairXY : function(){
33890         return Roo.lib.Dom.getXY(this.iconNode);
33891     },
33892
33893     onRender : function(){
33894         this.render();
33895     },
33896
33897     render : function(bulkRender){
33898         var n = this.node, a = n.attributes;
33899         var targetNode = n.parentNode ?
33900               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33901
33902         if(!this.rendered){
33903             this.rendered = true;
33904
33905             this.renderElements(n, a, targetNode, bulkRender);
33906
33907             if(a.qtip){
33908                if(this.textNode.setAttributeNS){
33909                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33910                    if(a.qtipTitle){
33911                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33912                    }
33913                }else{
33914                    this.textNode.setAttribute("ext:qtip", a.qtip);
33915                    if(a.qtipTitle){
33916                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33917                    }
33918                }
33919             }else if(a.qtipCfg){
33920                 a.qtipCfg.target = Roo.id(this.textNode);
33921                 Roo.QuickTips.register(a.qtipCfg);
33922             }
33923             this.initEvents();
33924             if(!this.node.expanded){
33925                 this.updateExpandIcon();
33926             }
33927         }else{
33928             if(bulkRender === true) {
33929                 targetNode.appendChild(this.wrap);
33930             }
33931         }
33932     },
33933
33934     renderElements : function(n, a, targetNode, bulkRender)
33935     {
33936         // add some indent caching, this helps performance when rendering a large tree
33937         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33938         var t = n.getOwnerTree();
33939         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33940         if (typeof(n.attributes.html) != 'undefined') {
33941             txt = n.attributes.html;
33942         }
33943         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33944         var cb = typeof a.checked == 'boolean';
33945         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33946         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33947             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33948             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33949             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33950             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33951             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33952              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33953                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33954             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33955             "</li>"];
33956
33957         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33958             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33959                                 n.nextSibling.ui.getEl(), buf.join(""));
33960         }else{
33961             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33962         }
33963
33964         this.elNode = this.wrap.childNodes[0];
33965         this.ctNode = this.wrap.childNodes[1];
33966         var cs = this.elNode.childNodes;
33967         this.indentNode = cs[0];
33968         this.ecNode = cs[1];
33969         this.iconNode = cs[2];
33970         var index = 3;
33971         if(cb){
33972             this.checkbox = cs[3];
33973             index++;
33974         }
33975         this.anchor = cs[index];
33976         this.textNode = cs[index].firstChild;
33977     },
33978
33979     getAnchor : function(){
33980         return this.anchor;
33981     },
33982
33983     getTextEl : function(){
33984         return this.textNode;
33985     },
33986
33987     getIconEl : function(){
33988         return this.iconNode;
33989     },
33990
33991     isChecked : function(){
33992         return this.checkbox ? this.checkbox.checked : false;
33993     },
33994
33995     updateExpandIcon : function(){
33996         if(this.rendered){
33997             var n = this.node, c1, c2;
33998             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33999             var hasChild = n.hasChildNodes();
34000             if(hasChild){
34001                 if(n.expanded){
34002                     cls += "-minus";
34003                     c1 = "x-tree-node-collapsed";
34004                     c2 = "x-tree-node-expanded";
34005                 }else{
34006                     cls += "-plus";
34007                     c1 = "x-tree-node-expanded";
34008                     c2 = "x-tree-node-collapsed";
34009                 }
34010                 if(this.wasLeaf){
34011                     this.removeClass("x-tree-node-leaf");
34012                     this.wasLeaf = false;
34013                 }
34014                 if(this.c1 != c1 || this.c2 != c2){
34015                     Roo.fly(this.elNode).replaceClass(c1, c2);
34016                     this.c1 = c1; this.c2 = c2;
34017                 }
34018             }else{
34019                 // this changes non-leafs into leafs if they have no children.
34020                 // it's not very rational behaviour..
34021                 
34022                 if(!this.wasLeaf && this.node.leaf){
34023                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34024                     delete this.c1;
34025                     delete this.c2;
34026                     this.wasLeaf = true;
34027                 }
34028             }
34029             var ecc = "x-tree-ec-icon "+cls;
34030             if(this.ecc != ecc){
34031                 this.ecNode.className = ecc;
34032                 this.ecc = ecc;
34033             }
34034         }
34035     },
34036
34037     getChildIndent : function(){
34038         if(!this.childIndent){
34039             var buf = [];
34040             var p = this.node;
34041             while(p){
34042                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34043                     if(!p.isLast()) {
34044                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34045                     } else {
34046                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34047                     }
34048                 }
34049                 p = p.parentNode;
34050             }
34051             this.childIndent = buf.join("");
34052         }
34053         return this.childIndent;
34054     },
34055
34056     renderIndent : function(){
34057         if(this.rendered){
34058             var indent = "";
34059             var p = this.node.parentNode;
34060             if(p){
34061                 indent = p.ui.getChildIndent();
34062             }
34063             if(this.indentMarkup != indent){ // don't rerender if not required
34064                 this.indentNode.innerHTML = indent;
34065                 this.indentMarkup = indent;
34066             }
34067             this.updateExpandIcon();
34068         }
34069     }
34070 };
34071
34072 Roo.tree.RootTreeNodeUI = function(){
34073     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34074 };
34075 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34076     render : function(){
34077         if(!this.rendered){
34078             var targetNode = this.node.ownerTree.innerCt.dom;
34079             this.node.expanded = true;
34080             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34081             this.wrap = this.ctNode = targetNode.firstChild;
34082         }
34083     },
34084     collapse : function(){
34085     },
34086     expand : function(){
34087     }
34088 });/*
34089  * Based on:
34090  * Ext JS Library 1.1.1
34091  * Copyright(c) 2006-2007, Ext JS, LLC.
34092  *
34093  * Originally Released Under LGPL - original licence link has changed is not relivant.
34094  *
34095  * Fork - LGPL
34096  * <script type="text/javascript">
34097  */
34098 /**
34099  * @class Roo.tree.TreeLoader
34100  * @extends Roo.util.Observable
34101  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34102  * nodes from a specified URL. The response must be a javascript Array definition
34103  * who's elements are node definition objects. eg:
34104  * <pre><code>
34105 {  success : true,
34106    data :      [
34107    
34108     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34109     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34110     ]
34111 }
34112
34113
34114 </code></pre>
34115  * <br><br>
34116  * The old style respose with just an array is still supported, but not recommended.
34117  * <br><br>
34118  *
34119  * A server request is sent, and child nodes are loaded only when a node is expanded.
34120  * The loading node's id is passed to the server under the parameter name "node" to
34121  * enable the server to produce the correct child nodes.
34122  * <br><br>
34123  * To pass extra parameters, an event handler may be attached to the "beforeload"
34124  * event, and the parameters specified in the TreeLoader's baseParams property:
34125  * <pre><code>
34126     myTreeLoader.on("beforeload", function(treeLoader, node) {
34127         this.baseParams.category = node.attributes.category;
34128     }, this);
34129 </code></pre><
34130  * This would pass an HTTP parameter called "category" to the server containing
34131  * the value of the Node's "category" attribute.
34132  * @constructor
34133  * Creates a new Treeloader.
34134  * @param {Object} config A config object containing config properties.
34135  */
34136 Roo.tree.TreeLoader = function(config){
34137     this.baseParams = {};
34138     this.requestMethod = "POST";
34139     Roo.apply(this, config);
34140
34141     this.addEvents({
34142     
34143         /**
34144          * @event beforeload
34145          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34146          * @param {Object} This TreeLoader object.
34147          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34148          * @param {Object} callback The callback function specified in the {@link #load} call.
34149          */
34150         beforeload : true,
34151         /**
34152          * @event load
34153          * Fires when the node has been successfuly loaded.
34154          * @param {Object} This TreeLoader object.
34155          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34156          * @param {Object} response The response object containing the data from the server.
34157          */
34158         load : true,
34159         /**
34160          * @event loadexception
34161          * Fires if the network request failed.
34162          * @param {Object} This TreeLoader object.
34163          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34164          * @param {Object} response The response object containing the data from the server.
34165          */
34166         loadexception : true,
34167         /**
34168          * @event create
34169          * Fires before a node is created, enabling you to return custom Node types 
34170          * @param {Object} This TreeLoader object.
34171          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34172          */
34173         create : true
34174     });
34175
34176     Roo.tree.TreeLoader.superclass.constructor.call(this);
34177 };
34178
34179 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34180     /**
34181     * @cfg {String} dataUrl The URL from which to request a Json string which
34182     * specifies an array of node definition object representing the child nodes
34183     * to be loaded.
34184     */
34185     /**
34186     * @cfg {String} requestMethod either GET or POST
34187     * defaults to POST (due to BC)
34188     * to be loaded.
34189     */
34190     /**
34191     * @cfg {Object} baseParams (optional) An object containing properties which
34192     * specify HTTP parameters to be passed to each request for child nodes.
34193     */
34194     /**
34195     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34196     * created by this loader. If the attributes sent by the server have an attribute in this object,
34197     * they take priority.
34198     */
34199     /**
34200     * @cfg {Object} uiProviders (optional) An object containing properties which
34201     * 
34202     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34203     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34204     * <i>uiProvider</i> attribute of a returned child node is a string rather
34205     * than a reference to a TreeNodeUI implementation, this that string value
34206     * is used as a property name in the uiProviders object. You can define the provider named
34207     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34208     */
34209     uiProviders : {},
34210
34211     /**
34212     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34213     * child nodes before loading.
34214     */
34215     clearOnLoad : true,
34216
34217     /**
34218     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34219     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34220     * Grid query { data : [ .....] }
34221     */
34222     
34223     root : false,
34224      /**
34225     * @cfg {String} queryParam (optional) 
34226     * Name of the query as it will be passed on the querystring (defaults to 'node')
34227     * eg. the request will be ?node=[id]
34228     */
34229     
34230     
34231     queryParam: false,
34232     
34233     /**
34234      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34235      * This is called automatically when a node is expanded, but may be used to reload
34236      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34237      * @param {Roo.tree.TreeNode} node
34238      * @param {Function} callback
34239      */
34240     load : function(node, callback){
34241         if(this.clearOnLoad){
34242             while(node.firstChild){
34243                 node.removeChild(node.firstChild);
34244             }
34245         }
34246         if(node.attributes.children){ // preloaded json children
34247             var cs = node.attributes.children;
34248             for(var i = 0, len = cs.length; i < len; i++){
34249                 node.appendChild(this.createNode(cs[i]));
34250             }
34251             if(typeof callback == "function"){
34252                 callback();
34253             }
34254         }else if(this.dataUrl){
34255             this.requestData(node, callback);
34256         }
34257     },
34258
34259     getParams: function(node){
34260         var buf = [], bp = this.baseParams;
34261         for(var key in bp){
34262             if(typeof bp[key] != "function"){
34263                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34264             }
34265         }
34266         var n = this.queryParam === false ? 'node' : this.queryParam;
34267         buf.push(n + "=", encodeURIComponent(node.id));
34268         return buf.join("");
34269     },
34270
34271     requestData : function(node, callback){
34272         if(this.fireEvent("beforeload", this, node, callback) !== false){
34273             this.transId = Roo.Ajax.request({
34274                 method:this.requestMethod,
34275                 url: this.dataUrl||this.url,
34276                 success: this.handleResponse,
34277                 failure: this.handleFailure,
34278                 scope: this,
34279                 argument: {callback: callback, node: node},
34280                 params: this.getParams(node)
34281             });
34282         }else{
34283             // if the load is cancelled, make sure we notify
34284             // the node that we are done
34285             if(typeof callback == "function"){
34286                 callback();
34287             }
34288         }
34289     },
34290
34291     isLoading : function(){
34292         return this.transId ? true : false;
34293     },
34294
34295     abort : function(){
34296         if(this.isLoading()){
34297             Roo.Ajax.abort(this.transId);
34298         }
34299     },
34300
34301     // private
34302     createNode : function(attr)
34303     {
34304         // apply baseAttrs, nice idea Corey!
34305         if(this.baseAttrs){
34306             Roo.applyIf(attr, this.baseAttrs);
34307         }
34308         if(this.applyLoader !== false){
34309             attr.loader = this;
34310         }
34311         // uiProvider = depreciated..
34312         
34313         if(typeof(attr.uiProvider) == 'string'){
34314            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34315                 /**  eval:var:attr */ eval(attr.uiProvider);
34316         }
34317         if(typeof(this.uiProviders['default']) != 'undefined') {
34318             attr.uiProvider = this.uiProviders['default'];
34319         }
34320         
34321         this.fireEvent('create', this, attr);
34322         
34323         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34324         return(attr.leaf ?
34325                         new Roo.tree.TreeNode(attr) :
34326                         new Roo.tree.AsyncTreeNode(attr));
34327     },
34328
34329     processResponse : function(response, node, callback)
34330     {
34331         var json = response.responseText;
34332         try {
34333             
34334             var o = Roo.decode(json);
34335             
34336             if (this.root === false && typeof(o.success) != undefined) {
34337                 this.root = 'data'; // the default behaviour for list like data..
34338                 }
34339                 
34340             if (this.root !== false &&  !o.success) {
34341                 // it's a failure condition.
34342                 var a = response.argument;
34343                 this.fireEvent("loadexception", this, a.node, response);
34344                 Roo.log("Load failed - should have a handler really");
34345                 return;
34346             }
34347             
34348             
34349             
34350             if (this.root !== false) {
34351                  o = o[this.root];
34352             }
34353             
34354             for(var i = 0, len = o.length; i < len; i++){
34355                 var n = this.createNode(o[i]);
34356                 if(n){
34357                     node.appendChild(n);
34358                 }
34359             }
34360             if(typeof callback == "function"){
34361                 callback(this, node);
34362             }
34363         }catch(e){
34364             this.handleFailure(response);
34365         }
34366     },
34367
34368     handleResponse : function(response){
34369         this.transId = false;
34370         var a = response.argument;
34371         this.processResponse(response, a.node, a.callback);
34372         this.fireEvent("load", this, a.node, response);
34373     },
34374
34375     handleFailure : function(response)
34376     {
34377         // should handle failure better..
34378         this.transId = false;
34379         var a = response.argument;
34380         this.fireEvent("loadexception", this, a.node, response);
34381         if(typeof a.callback == "function"){
34382             a.callback(this, a.node);
34383         }
34384     }
34385 });/*
34386  * Based on:
34387  * Ext JS Library 1.1.1
34388  * Copyright(c) 2006-2007, Ext JS, LLC.
34389  *
34390  * Originally Released Under LGPL - original licence link has changed is not relivant.
34391  *
34392  * Fork - LGPL
34393  * <script type="text/javascript">
34394  */
34395
34396 /**
34397 * @class Roo.tree.TreeFilter
34398 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34399 * @param {TreePanel} tree
34400 * @param {Object} config (optional)
34401  */
34402 Roo.tree.TreeFilter = function(tree, config){
34403     this.tree = tree;
34404     this.filtered = {};
34405     Roo.apply(this, config);
34406 };
34407
34408 Roo.tree.TreeFilter.prototype = {
34409     clearBlank:false,
34410     reverse:false,
34411     autoClear:false,
34412     remove:false,
34413
34414      /**
34415      * Filter the data by a specific attribute.
34416      * @param {String/RegExp} value Either string that the attribute value
34417      * should start with or a RegExp to test against the attribute
34418      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34419      * @param {TreeNode} startNode (optional) The node to start the filter at.
34420      */
34421     filter : function(value, attr, startNode){
34422         attr = attr || "text";
34423         var f;
34424         if(typeof value == "string"){
34425             var vlen = value.length;
34426             // auto clear empty filter
34427             if(vlen == 0 && this.clearBlank){
34428                 this.clear();
34429                 return;
34430             }
34431             value = value.toLowerCase();
34432             f = function(n){
34433                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34434             };
34435         }else if(value.exec){ // regex?
34436             f = function(n){
34437                 return value.test(n.attributes[attr]);
34438             };
34439         }else{
34440             throw 'Illegal filter type, must be string or regex';
34441         }
34442         this.filterBy(f, null, startNode);
34443         },
34444
34445     /**
34446      * Filter by a function. The passed function will be called with each
34447      * node in the tree (or from the startNode). If the function returns true, the node is kept
34448      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34449      * @param {Function} fn The filter function
34450      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34451      */
34452     filterBy : function(fn, scope, startNode){
34453         startNode = startNode || this.tree.root;
34454         if(this.autoClear){
34455             this.clear();
34456         }
34457         var af = this.filtered, rv = this.reverse;
34458         var f = function(n){
34459             if(n == startNode){
34460                 return true;
34461             }
34462             if(af[n.id]){
34463                 return false;
34464             }
34465             var m = fn.call(scope || n, n);
34466             if(!m || rv){
34467                 af[n.id] = n;
34468                 n.ui.hide();
34469                 return false;
34470             }
34471             return true;
34472         };
34473         startNode.cascade(f);
34474         if(this.remove){
34475            for(var id in af){
34476                if(typeof id != "function"){
34477                    var n = af[id];
34478                    if(n && n.parentNode){
34479                        n.parentNode.removeChild(n);
34480                    }
34481                }
34482            }
34483         }
34484     },
34485
34486     /**
34487      * Clears the current filter. Note: with the "remove" option
34488      * set a filter cannot be cleared.
34489      */
34490     clear : function(){
34491         var t = this.tree;
34492         var af = this.filtered;
34493         for(var id in af){
34494             if(typeof id != "function"){
34495                 var n = af[id];
34496                 if(n){
34497                     n.ui.show();
34498                 }
34499             }
34500         }
34501         this.filtered = {};
34502     }
34503 };
34504 /*
34505  * Based on:
34506  * Ext JS Library 1.1.1
34507  * Copyright(c) 2006-2007, Ext JS, LLC.
34508  *
34509  * Originally Released Under LGPL - original licence link has changed is not relivant.
34510  *
34511  * Fork - LGPL
34512  * <script type="text/javascript">
34513  */
34514  
34515
34516 /**
34517  * @class Roo.tree.TreeSorter
34518  * Provides sorting of nodes in a TreePanel
34519  * 
34520  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34521  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34522  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34523  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34524  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34525  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34526  * @constructor
34527  * @param {TreePanel} tree
34528  * @param {Object} config
34529  */
34530 Roo.tree.TreeSorter = function(tree, config){
34531     Roo.apply(this, config);
34532     tree.on("beforechildrenrendered", this.doSort, this);
34533     tree.on("append", this.updateSort, this);
34534     tree.on("insert", this.updateSort, this);
34535     
34536     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34537     var p = this.property || "text";
34538     var sortType = this.sortType;
34539     var fs = this.folderSort;
34540     var cs = this.caseSensitive === true;
34541     var leafAttr = this.leafAttr || 'leaf';
34542
34543     this.sortFn = function(n1, n2){
34544         if(fs){
34545             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34546                 return 1;
34547             }
34548             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34549                 return -1;
34550             }
34551         }
34552         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34553         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34554         if(v1 < v2){
34555                         return dsc ? +1 : -1;
34556                 }else if(v1 > v2){
34557                         return dsc ? -1 : +1;
34558         }else{
34559                 return 0;
34560         }
34561     };
34562 };
34563
34564 Roo.tree.TreeSorter.prototype = {
34565     doSort : function(node){
34566         node.sort(this.sortFn);
34567     },
34568     
34569     compareNodes : function(n1, n2){
34570         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34571     },
34572     
34573     updateSort : function(tree, node){
34574         if(node.childrenRendered){
34575             this.doSort.defer(1, this, [node]);
34576         }
34577     }
34578 };/*
34579  * Based on:
34580  * Ext JS Library 1.1.1
34581  * Copyright(c) 2006-2007, Ext JS, LLC.
34582  *
34583  * Originally Released Under LGPL - original licence link has changed is not relivant.
34584  *
34585  * Fork - LGPL
34586  * <script type="text/javascript">
34587  */
34588
34589 if(Roo.dd.DropZone){
34590     
34591 Roo.tree.TreeDropZone = function(tree, config){
34592     this.allowParentInsert = false;
34593     this.allowContainerDrop = false;
34594     this.appendOnly = false;
34595     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34596     this.tree = tree;
34597     this.lastInsertClass = "x-tree-no-status";
34598     this.dragOverData = {};
34599 };
34600
34601 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34602     ddGroup : "TreeDD",
34603     scroll:  true,
34604     
34605     expandDelay : 1000,
34606     
34607     expandNode : function(node){
34608         if(node.hasChildNodes() && !node.isExpanded()){
34609             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34610         }
34611     },
34612     
34613     queueExpand : function(node){
34614         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34615     },
34616     
34617     cancelExpand : function(){
34618         if(this.expandProcId){
34619             clearTimeout(this.expandProcId);
34620             this.expandProcId = false;
34621         }
34622     },
34623     
34624     isValidDropPoint : function(n, pt, dd, e, data){
34625         if(!n || !data){ return false; }
34626         var targetNode = n.node;
34627         var dropNode = data.node;
34628         // default drop rules
34629         if(!(targetNode && targetNode.isTarget && pt)){
34630             return false;
34631         }
34632         if(pt == "append" && targetNode.allowChildren === false){
34633             return false;
34634         }
34635         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34636             return false;
34637         }
34638         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34639             return false;
34640         }
34641         // reuse the object
34642         var overEvent = this.dragOverData;
34643         overEvent.tree = this.tree;
34644         overEvent.target = targetNode;
34645         overEvent.data = data;
34646         overEvent.point = pt;
34647         overEvent.source = dd;
34648         overEvent.rawEvent = e;
34649         overEvent.dropNode = dropNode;
34650         overEvent.cancel = false;  
34651         var result = this.tree.fireEvent("nodedragover", overEvent);
34652         return overEvent.cancel === false && result !== false;
34653     },
34654     
34655     getDropPoint : function(e, n, dd)
34656     {
34657         var tn = n.node;
34658         if(tn.isRoot){
34659             return tn.allowChildren !== false ? "append" : false; // always append for root
34660         }
34661         var dragEl = n.ddel;
34662         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34663         var y = Roo.lib.Event.getPageY(e);
34664         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34665         
34666         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34667         var noAppend = tn.allowChildren === false;
34668         if(this.appendOnly || tn.parentNode.allowChildren === false){
34669             return noAppend ? false : "append";
34670         }
34671         var noBelow = false;
34672         if(!this.allowParentInsert){
34673             noBelow = tn.hasChildNodes() && tn.isExpanded();
34674         }
34675         var q = (b - t) / (noAppend ? 2 : 3);
34676         if(y >= t && y < (t + q)){
34677             return "above";
34678         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34679             return "below";
34680         }else{
34681             return "append";
34682         }
34683     },
34684     
34685     onNodeEnter : function(n, dd, e, data)
34686     {
34687         this.cancelExpand();
34688     },
34689     
34690     onNodeOver : function(n, dd, e, data)
34691     {
34692        
34693         var pt = this.getDropPoint(e, n, dd);
34694         var node = n.node;
34695         
34696         // auto node expand check
34697         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34698             this.queueExpand(node);
34699         }else if(pt != "append"){
34700             this.cancelExpand();
34701         }
34702         
34703         // set the insert point style on the target node
34704         var returnCls = this.dropNotAllowed;
34705         if(this.isValidDropPoint(n, pt, dd, e, data)){
34706            if(pt){
34707                var el = n.ddel;
34708                var cls;
34709                if(pt == "above"){
34710                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34711                    cls = "x-tree-drag-insert-above";
34712                }else if(pt == "below"){
34713                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34714                    cls = "x-tree-drag-insert-below";
34715                }else{
34716                    returnCls = "x-tree-drop-ok-append";
34717                    cls = "x-tree-drag-append";
34718                }
34719                if(this.lastInsertClass != cls){
34720                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34721                    this.lastInsertClass = cls;
34722                }
34723            }
34724        }
34725        return returnCls;
34726     },
34727     
34728     onNodeOut : function(n, dd, e, data){
34729         
34730         this.cancelExpand();
34731         this.removeDropIndicators(n);
34732     },
34733     
34734     onNodeDrop : function(n, dd, e, data){
34735         var point = this.getDropPoint(e, n, dd);
34736         var targetNode = n.node;
34737         targetNode.ui.startDrop();
34738         if(!this.isValidDropPoint(n, point, dd, e, data)){
34739             targetNode.ui.endDrop();
34740             return false;
34741         }
34742         // first try to find the drop node
34743         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34744         var dropEvent = {
34745             tree : this.tree,
34746             target: targetNode,
34747             data: data,
34748             point: point,
34749             source: dd,
34750             rawEvent: e,
34751             dropNode: dropNode,
34752             cancel: !dropNode   
34753         };
34754         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34755         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34756             targetNode.ui.endDrop();
34757             return false;
34758         }
34759         // allow target changing
34760         targetNode = dropEvent.target;
34761         if(point == "append" && !targetNode.isExpanded()){
34762             targetNode.expand(false, null, function(){
34763                 this.completeDrop(dropEvent);
34764             }.createDelegate(this));
34765         }else{
34766             this.completeDrop(dropEvent);
34767         }
34768         return true;
34769     },
34770     
34771     completeDrop : function(de){
34772         var ns = de.dropNode, p = de.point, t = de.target;
34773         if(!(ns instanceof Array)){
34774             ns = [ns];
34775         }
34776         var n;
34777         for(var i = 0, len = ns.length; i < len; i++){
34778             n = ns[i];
34779             if(p == "above"){
34780                 t.parentNode.insertBefore(n, t);
34781             }else if(p == "below"){
34782                 t.parentNode.insertBefore(n, t.nextSibling);
34783             }else{
34784                 t.appendChild(n);
34785             }
34786         }
34787         n.ui.focus();
34788         if(this.tree.hlDrop){
34789             n.ui.highlight();
34790         }
34791         t.ui.endDrop();
34792         this.tree.fireEvent("nodedrop", de);
34793     },
34794     
34795     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34796         if(this.tree.hlDrop){
34797             dropNode.ui.focus();
34798             dropNode.ui.highlight();
34799         }
34800         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34801     },
34802     
34803     getTree : function(){
34804         return this.tree;
34805     },
34806     
34807     removeDropIndicators : function(n){
34808         if(n && n.ddel){
34809             var el = n.ddel;
34810             Roo.fly(el).removeClass([
34811                     "x-tree-drag-insert-above",
34812                     "x-tree-drag-insert-below",
34813                     "x-tree-drag-append"]);
34814             this.lastInsertClass = "_noclass";
34815         }
34816     },
34817     
34818     beforeDragDrop : function(target, e, id){
34819         this.cancelExpand();
34820         return true;
34821     },
34822     
34823     afterRepair : function(data){
34824         if(data && Roo.enableFx){
34825             data.node.ui.highlight();
34826         }
34827         this.hideProxy();
34828     } 
34829     
34830 });
34831
34832 }
34833 /*
34834  * Based on:
34835  * Ext JS Library 1.1.1
34836  * Copyright(c) 2006-2007, Ext JS, LLC.
34837  *
34838  * Originally Released Under LGPL - original licence link has changed is not relivant.
34839  *
34840  * Fork - LGPL
34841  * <script type="text/javascript">
34842  */
34843  
34844
34845 if(Roo.dd.DragZone){
34846 Roo.tree.TreeDragZone = function(tree, config){
34847     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34848     this.tree = tree;
34849 };
34850
34851 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34852     ddGroup : "TreeDD",
34853    
34854     onBeforeDrag : function(data, e){
34855         var n = data.node;
34856         return n && n.draggable && !n.disabled;
34857     },
34858      
34859     
34860     onInitDrag : function(e){
34861         var data = this.dragData;
34862         this.tree.getSelectionModel().select(data.node);
34863         this.proxy.update("");
34864         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34865         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34866     },
34867     
34868     getRepairXY : function(e, data){
34869         return data.node.ui.getDDRepairXY();
34870     },
34871     
34872     onEndDrag : function(data, e){
34873         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34874         
34875         
34876     },
34877     
34878     onValidDrop : function(dd, e, id){
34879         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34880         this.hideProxy();
34881     },
34882     
34883     beforeInvalidDrop : function(e, id){
34884         // this scrolls the original position back into view
34885         var sm = this.tree.getSelectionModel();
34886         sm.clearSelections();
34887         sm.select(this.dragData.node);
34888     }
34889 });
34890 }/*
34891  * Based on:
34892  * Ext JS Library 1.1.1
34893  * Copyright(c) 2006-2007, Ext JS, LLC.
34894  *
34895  * Originally Released Under LGPL - original licence link has changed is not relivant.
34896  *
34897  * Fork - LGPL
34898  * <script type="text/javascript">
34899  */
34900 /**
34901  * @class Roo.tree.TreeEditor
34902  * @extends Roo.Editor
34903  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34904  * as the editor field.
34905  * @constructor
34906  * @param {Object} config (used to be the tree panel.)
34907  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34908  * 
34909  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34910  * @cfg {Roo.form.TextField|Object} field The field configuration
34911  *
34912  * 
34913  */
34914 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34915     var tree = config;
34916     var field;
34917     if (oldconfig) { // old style..
34918         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34919     } else {
34920         // new style..
34921         tree = config.tree;
34922         config.field = config.field  || {};
34923         config.field.xtype = 'TextField';
34924         field = Roo.factory(config.field, Roo.form);
34925     }
34926     config = config || {};
34927     
34928     
34929     this.addEvents({
34930         /**
34931          * @event beforenodeedit
34932          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34933          * false from the handler of this event.
34934          * @param {Editor} this
34935          * @param {Roo.tree.Node} node 
34936          */
34937         "beforenodeedit" : true
34938     });
34939     
34940     //Roo.log(config);
34941     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34942
34943     this.tree = tree;
34944
34945     tree.on('beforeclick', this.beforeNodeClick, this);
34946     tree.getTreeEl().on('mousedown', this.hide, this);
34947     this.on('complete', this.updateNode, this);
34948     this.on('beforestartedit', this.fitToTree, this);
34949     this.on('startedit', this.bindScroll, this, {delay:10});
34950     this.on('specialkey', this.onSpecialKey, this);
34951 };
34952
34953 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34954     /**
34955      * @cfg {String} alignment
34956      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34957      */
34958     alignment: "l-l",
34959     // inherit
34960     autoSize: false,
34961     /**
34962      * @cfg {Boolean} hideEl
34963      * True to hide the bound element while the editor is displayed (defaults to false)
34964      */
34965     hideEl : false,
34966     /**
34967      * @cfg {String} cls
34968      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34969      */
34970     cls: "x-small-editor x-tree-editor",
34971     /**
34972      * @cfg {Boolean} shim
34973      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34974      */
34975     shim:false,
34976     // inherit
34977     shadow:"frame",
34978     /**
34979      * @cfg {Number} maxWidth
34980      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34981      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34982      * scroll and client offsets into account prior to each edit.
34983      */
34984     maxWidth: 250,
34985
34986     editDelay : 350,
34987
34988     // private
34989     fitToTree : function(ed, el){
34990         var td = this.tree.getTreeEl().dom, nd = el.dom;
34991         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34992             td.scrollLeft = nd.offsetLeft;
34993         }
34994         var w = Math.min(
34995                 this.maxWidth,
34996                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34997         this.setSize(w, '');
34998         
34999         return this.fireEvent('beforenodeedit', this, this.editNode);
35000         
35001     },
35002
35003     // private
35004     triggerEdit : function(node){
35005         this.completeEdit();
35006         this.editNode = node;
35007         this.startEdit(node.ui.textNode, node.text);
35008     },
35009
35010     // private
35011     bindScroll : function(){
35012         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35013     },
35014
35015     // private
35016     beforeNodeClick : function(node, e){
35017         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35018         this.lastClick = new Date();
35019         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35020             e.stopEvent();
35021             this.triggerEdit(node);
35022             return false;
35023         }
35024         return true;
35025     },
35026
35027     // private
35028     updateNode : function(ed, value){
35029         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35030         this.editNode.setText(value);
35031     },
35032
35033     // private
35034     onHide : function(){
35035         Roo.tree.TreeEditor.superclass.onHide.call(this);
35036         if(this.editNode){
35037             this.editNode.ui.focus();
35038         }
35039     },
35040
35041     // private
35042     onSpecialKey : function(field, e){
35043         var k = e.getKey();
35044         if(k == e.ESC){
35045             e.stopEvent();
35046             this.cancelEdit();
35047         }else if(k == e.ENTER && !e.hasModifier()){
35048             e.stopEvent();
35049             this.completeEdit();
35050         }
35051     }
35052 });//<Script type="text/javascript">
35053 /*
35054  * Based on:
35055  * Ext JS Library 1.1.1
35056  * Copyright(c) 2006-2007, Ext JS, LLC.
35057  *
35058  * Originally Released Under LGPL - original licence link has changed is not relivant.
35059  *
35060  * Fork - LGPL
35061  * <script type="text/javascript">
35062  */
35063  
35064 /**
35065  * Not documented??? - probably should be...
35066  */
35067
35068 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35069     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35070     
35071     renderElements : function(n, a, targetNode, bulkRender){
35072         //consel.log("renderElements?");
35073         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35074
35075         var t = n.getOwnerTree();
35076         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35077         
35078         var cols = t.columns;
35079         var bw = t.borderWidth;
35080         var c = cols[0];
35081         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35082          var cb = typeof a.checked == "boolean";
35083         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35084         var colcls = 'x-t-' + tid + '-c0';
35085         var buf = [
35086             '<li class="x-tree-node">',
35087             
35088                 
35089                 '<div class="x-tree-node-el ', a.cls,'">',
35090                     // extran...
35091                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35092                 
35093                 
35094                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35095                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35096                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35097                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35098                            (a.iconCls ? ' '+a.iconCls : ''),
35099                            '" unselectable="on" />',
35100                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35101                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35102                              
35103                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35104                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35105                             '<span unselectable="on" qtip="' + tx + '">',
35106                              tx,
35107                              '</span></a>' ,
35108                     '</div>',
35109                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35110                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35111                  ];
35112         for(var i = 1, len = cols.length; i < len; i++){
35113             c = cols[i];
35114             colcls = 'x-t-' + tid + '-c' +i;
35115             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35116             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35117                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35118                       "</div>");
35119          }
35120          
35121          buf.push(
35122             '</a>',
35123             '<div class="x-clear"></div></div>',
35124             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35125             "</li>");
35126         
35127         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35128             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35129                                 n.nextSibling.ui.getEl(), buf.join(""));
35130         }else{
35131             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35132         }
35133         var el = this.wrap.firstChild;
35134         this.elRow = el;
35135         this.elNode = el.firstChild;
35136         this.ranchor = el.childNodes[1];
35137         this.ctNode = this.wrap.childNodes[1];
35138         var cs = el.firstChild.childNodes;
35139         this.indentNode = cs[0];
35140         this.ecNode = cs[1];
35141         this.iconNode = cs[2];
35142         var index = 3;
35143         if(cb){
35144             this.checkbox = cs[3];
35145             index++;
35146         }
35147         this.anchor = cs[index];
35148         
35149         this.textNode = cs[index].firstChild;
35150         
35151         //el.on("click", this.onClick, this);
35152         //el.on("dblclick", this.onDblClick, this);
35153         
35154         
35155        // console.log(this);
35156     },
35157     initEvents : function(){
35158         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35159         
35160             
35161         var a = this.ranchor;
35162
35163         var el = Roo.get(a);
35164
35165         if(Roo.isOpera){ // opera render bug ignores the CSS
35166             el.setStyle("text-decoration", "none");
35167         }
35168
35169         el.on("click", this.onClick, this);
35170         el.on("dblclick", this.onDblClick, this);
35171         el.on("contextmenu", this.onContextMenu, this);
35172         
35173     },
35174     
35175     /*onSelectedChange : function(state){
35176         if(state){
35177             this.focus();
35178             this.addClass("x-tree-selected");
35179         }else{
35180             //this.blur();
35181             this.removeClass("x-tree-selected");
35182         }
35183     },*/
35184     addClass : function(cls){
35185         if(this.elRow){
35186             Roo.fly(this.elRow).addClass(cls);
35187         }
35188         
35189     },
35190     
35191     
35192     removeClass : function(cls){
35193         if(this.elRow){
35194             Roo.fly(this.elRow).removeClass(cls);
35195         }
35196     }
35197
35198     
35199     
35200 });//<Script type="text/javascript">
35201
35202 /*
35203  * Based on:
35204  * Ext JS Library 1.1.1
35205  * Copyright(c) 2006-2007, Ext JS, LLC.
35206  *
35207  * Originally Released Under LGPL - original licence link has changed is not relivant.
35208  *
35209  * Fork - LGPL
35210  * <script type="text/javascript">
35211  */
35212  
35213
35214 /**
35215  * @class Roo.tree.ColumnTree
35216  * @extends Roo.data.TreePanel
35217  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35218  * @cfg {int} borderWidth  compined right/left border allowance
35219  * @constructor
35220  * @param {String/HTMLElement/Element} el The container element
35221  * @param {Object} config
35222  */
35223 Roo.tree.ColumnTree =  function(el, config)
35224 {
35225    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35226    this.addEvents({
35227         /**
35228         * @event resize
35229         * Fire this event on a container when it resizes
35230         * @param {int} w Width
35231         * @param {int} h Height
35232         */
35233        "resize" : true
35234     });
35235     this.on('resize', this.onResize, this);
35236 };
35237
35238 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35239     //lines:false,
35240     
35241     
35242     borderWidth: Roo.isBorderBox ? 0 : 2, 
35243     headEls : false,
35244     
35245     render : function(){
35246         // add the header.....
35247        
35248         Roo.tree.ColumnTree.superclass.render.apply(this);
35249         
35250         this.el.addClass('x-column-tree');
35251         
35252         this.headers = this.el.createChild(
35253             {cls:'x-tree-headers'},this.innerCt.dom);
35254    
35255         var cols = this.columns, c;
35256         var totalWidth = 0;
35257         this.headEls = [];
35258         var  len = cols.length;
35259         for(var i = 0; i < len; i++){
35260              c = cols[i];
35261              totalWidth += c.width;
35262             this.headEls.push(this.headers.createChild({
35263                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35264                  cn: {
35265                      cls:'x-tree-hd-text',
35266                      html: c.header
35267                  },
35268                  style:'width:'+(c.width-this.borderWidth)+'px;'
35269              }));
35270         }
35271         this.headers.createChild({cls:'x-clear'});
35272         // prevent floats from wrapping when clipped
35273         this.headers.setWidth(totalWidth);
35274         //this.innerCt.setWidth(totalWidth);
35275         this.innerCt.setStyle({ overflow: 'auto' });
35276         this.onResize(this.width, this.height);
35277              
35278         
35279     },
35280     onResize : function(w,h)
35281     {
35282         this.height = h;
35283         this.width = w;
35284         // resize cols..
35285         this.innerCt.setWidth(this.width);
35286         this.innerCt.setHeight(this.height-20);
35287         
35288         // headers...
35289         var cols = this.columns, c;
35290         var totalWidth = 0;
35291         var expEl = false;
35292         var len = cols.length;
35293         for(var i = 0; i < len; i++){
35294             c = cols[i];
35295             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35296                 // it's the expander..
35297                 expEl  = this.headEls[i];
35298                 continue;
35299             }
35300             totalWidth += c.width;
35301             
35302         }
35303         if (expEl) {
35304             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35305         }
35306         this.headers.setWidth(w-20);
35307
35308         
35309         
35310         
35311     }
35312 });
35313 /*
35314  * Based on:
35315  * Ext JS Library 1.1.1
35316  * Copyright(c) 2006-2007, Ext JS, LLC.
35317  *
35318  * Originally Released Under LGPL - original licence link has changed is not relivant.
35319  *
35320  * Fork - LGPL
35321  * <script type="text/javascript">
35322  */
35323  
35324 /**
35325  * @class Roo.menu.Menu
35326  * @extends Roo.util.Observable
35327  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35328  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35329  * @constructor
35330  * Creates a new Menu
35331  * @param {Object} config Configuration options
35332  */
35333 Roo.menu.Menu = function(config){
35334     Roo.apply(this, config);
35335     this.id = this.id || Roo.id();
35336     this.addEvents({
35337         /**
35338          * @event beforeshow
35339          * Fires before this menu is displayed
35340          * @param {Roo.menu.Menu} this
35341          */
35342         beforeshow : true,
35343         /**
35344          * @event beforehide
35345          * Fires before this menu is hidden
35346          * @param {Roo.menu.Menu} this
35347          */
35348         beforehide : true,
35349         /**
35350          * @event show
35351          * Fires after this menu is displayed
35352          * @param {Roo.menu.Menu} this
35353          */
35354         show : true,
35355         /**
35356          * @event hide
35357          * Fires after this menu is hidden
35358          * @param {Roo.menu.Menu} this
35359          */
35360         hide : true,
35361         /**
35362          * @event click
35363          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35364          * @param {Roo.menu.Menu} this
35365          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35366          * @param {Roo.EventObject} e
35367          */
35368         click : true,
35369         /**
35370          * @event mouseover
35371          * Fires when the mouse is hovering over this menu
35372          * @param {Roo.menu.Menu} this
35373          * @param {Roo.EventObject} e
35374          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35375          */
35376         mouseover : true,
35377         /**
35378          * @event mouseout
35379          * Fires when the mouse exits this menu
35380          * @param {Roo.menu.Menu} this
35381          * @param {Roo.EventObject} e
35382          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35383          */
35384         mouseout : true,
35385         /**
35386          * @event itemclick
35387          * Fires when a menu item contained in this menu is clicked
35388          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35389          * @param {Roo.EventObject} e
35390          */
35391         itemclick: true
35392     });
35393     if (this.registerMenu) {
35394         Roo.menu.MenuMgr.register(this);
35395     }
35396     
35397     var mis = this.items;
35398     this.items = new Roo.util.MixedCollection();
35399     if(mis){
35400         this.add.apply(this, mis);
35401     }
35402 };
35403
35404 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35405     /**
35406      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35407      */
35408     minWidth : 120,
35409     /**
35410      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35411      * for bottom-right shadow (defaults to "sides")
35412      */
35413     shadow : "sides",
35414     /**
35415      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35416      * this menu (defaults to "tl-tr?")
35417      */
35418     subMenuAlign : "tl-tr?",
35419     /**
35420      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35421      * relative to its element of origin (defaults to "tl-bl?")
35422      */
35423     defaultAlign : "tl-bl?",
35424     /**
35425      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35426      */
35427     allowOtherMenus : false,
35428     /**
35429      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35430      */
35431     registerMenu : true,
35432
35433     hidden:true,
35434
35435     // private
35436     render : function(){
35437         if(this.el){
35438             return;
35439         }
35440         var el = this.el = new Roo.Layer({
35441             cls: "x-menu",
35442             shadow:this.shadow,
35443             constrain: false,
35444             parentEl: this.parentEl || document.body,
35445             zindex:15000
35446         });
35447
35448         this.keyNav = new Roo.menu.MenuNav(this);
35449
35450         if(this.plain){
35451             el.addClass("x-menu-plain");
35452         }
35453         if(this.cls){
35454             el.addClass(this.cls);
35455         }
35456         // generic focus element
35457         this.focusEl = el.createChild({
35458             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35459         });
35460         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35461         ul.on("click", this.onClick, this);
35462         ul.on("mouseover", this.onMouseOver, this);
35463         ul.on("mouseout", this.onMouseOut, this);
35464         this.items.each(function(item){
35465             if (item.hidden) {
35466                 return;
35467             }
35468             
35469             var li = document.createElement("li");
35470             li.className = "x-menu-list-item";
35471             ul.dom.appendChild(li);
35472             item.render(li, this);
35473         }, this);
35474         this.ul = ul;
35475         this.autoWidth();
35476     },
35477
35478     // private
35479     autoWidth : function(){
35480         var el = this.el, ul = this.ul;
35481         if(!el){
35482             return;
35483         }
35484         var w = this.width;
35485         if(w){
35486             el.setWidth(w);
35487         }else if(Roo.isIE){
35488             el.setWidth(this.minWidth);
35489             var t = el.dom.offsetWidth; // force recalc
35490             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35491         }
35492     },
35493
35494     // private
35495     delayAutoWidth : function(){
35496         if(this.rendered){
35497             if(!this.awTask){
35498                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35499             }
35500             this.awTask.delay(20);
35501         }
35502     },
35503
35504     // private
35505     findTargetItem : function(e){
35506         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35507         if(t && t.menuItemId){
35508             return this.items.get(t.menuItemId);
35509         }
35510     },
35511
35512     // private
35513     onClick : function(e){
35514         var t;
35515         if(t = this.findTargetItem(e)){
35516             t.onClick(e);
35517             this.fireEvent("click", this, t, e);
35518         }
35519     },
35520
35521     // private
35522     setActiveItem : function(item, autoExpand){
35523         if(item != this.activeItem){
35524             if(this.activeItem){
35525                 this.activeItem.deactivate();
35526             }
35527             this.activeItem = item;
35528             item.activate(autoExpand);
35529         }else if(autoExpand){
35530             item.expandMenu();
35531         }
35532     },
35533
35534     // private
35535     tryActivate : function(start, step){
35536         var items = this.items;
35537         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35538             var item = items.get(i);
35539             if(!item.disabled && item.canActivate){
35540                 this.setActiveItem(item, false);
35541                 return item;
35542             }
35543         }
35544         return false;
35545     },
35546
35547     // private
35548     onMouseOver : function(e){
35549         var t;
35550         if(t = this.findTargetItem(e)){
35551             if(t.canActivate && !t.disabled){
35552                 this.setActiveItem(t, true);
35553             }
35554         }
35555         this.fireEvent("mouseover", this, e, t);
35556     },
35557
35558     // private
35559     onMouseOut : function(e){
35560         var t;
35561         if(t = this.findTargetItem(e)){
35562             if(t == this.activeItem && t.shouldDeactivate(e)){
35563                 this.activeItem.deactivate();
35564                 delete this.activeItem;
35565             }
35566         }
35567         this.fireEvent("mouseout", this, e, t);
35568     },
35569
35570     /**
35571      * Read-only.  Returns true if the menu is currently displayed, else false.
35572      * @type Boolean
35573      */
35574     isVisible : function(){
35575         return this.el && !this.hidden;
35576     },
35577
35578     /**
35579      * Displays this menu relative to another element
35580      * @param {String/HTMLElement/Roo.Element} element The element to align to
35581      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35582      * the element (defaults to this.defaultAlign)
35583      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35584      */
35585     show : function(el, pos, parentMenu){
35586         this.parentMenu = parentMenu;
35587         if(!this.el){
35588             this.render();
35589         }
35590         this.fireEvent("beforeshow", this);
35591         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35592     },
35593
35594     /**
35595      * Displays this menu at a specific xy position
35596      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35597      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35598      */
35599     showAt : function(xy, parentMenu, /* private: */_e){
35600         this.parentMenu = parentMenu;
35601         if(!this.el){
35602             this.render();
35603         }
35604         if(_e !== false){
35605             this.fireEvent("beforeshow", this);
35606             xy = this.el.adjustForConstraints(xy);
35607         }
35608         this.el.setXY(xy);
35609         this.el.show();
35610         this.hidden = false;
35611         this.focus();
35612         this.fireEvent("show", this);
35613     },
35614
35615     focus : function(){
35616         if(!this.hidden){
35617             this.doFocus.defer(50, this);
35618         }
35619     },
35620
35621     doFocus : function(){
35622         if(!this.hidden){
35623             this.focusEl.focus();
35624         }
35625     },
35626
35627     /**
35628      * Hides this menu and optionally all parent menus
35629      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35630      */
35631     hide : function(deep){
35632         if(this.el && this.isVisible()){
35633             this.fireEvent("beforehide", this);
35634             if(this.activeItem){
35635                 this.activeItem.deactivate();
35636                 this.activeItem = null;
35637             }
35638             this.el.hide();
35639             this.hidden = true;
35640             this.fireEvent("hide", this);
35641         }
35642         if(deep === true && this.parentMenu){
35643             this.parentMenu.hide(true);
35644         }
35645     },
35646
35647     /**
35648      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35649      * Any of the following are valid:
35650      * <ul>
35651      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35652      * <li>An HTMLElement object which will be converted to a menu item</li>
35653      * <li>A menu item config object that will be created as a new menu item</li>
35654      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35655      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35656      * </ul>
35657      * Usage:
35658      * <pre><code>
35659 // Create the menu
35660 var menu = new Roo.menu.Menu();
35661
35662 // Create a menu item to add by reference
35663 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35664
35665 // Add a bunch of items at once using different methods.
35666 // Only the last item added will be returned.
35667 var item = menu.add(
35668     menuItem,                // add existing item by ref
35669     'Dynamic Item',          // new TextItem
35670     '-',                     // new separator
35671     { text: 'Config Item' }  // new item by config
35672 );
35673 </code></pre>
35674      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35675      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35676      */
35677     add : function(){
35678         var a = arguments, l = a.length, item;
35679         for(var i = 0; i < l; i++){
35680             var el = a[i];
35681             if ((typeof(el) == "object") && el.xtype && el.xns) {
35682                 el = Roo.factory(el, Roo.menu);
35683             }
35684             
35685             if(el.render){ // some kind of Item
35686                 item = this.addItem(el);
35687             }else if(typeof el == "string"){ // string
35688                 if(el == "separator" || el == "-"){
35689                     item = this.addSeparator();
35690                 }else{
35691                     item = this.addText(el);
35692                 }
35693             }else if(el.tagName || el.el){ // element
35694                 item = this.addElement(el);
35695             }else if(typeof el == "object"){ // must be menu item config?
35696                 item = this.addMenuItem(el);
35697             }
35698         }
35699         return item;
35700     },
35701
35702     /**
35703      * Returns this menu's underlying {@link Roo.Element} object
35704      * @return {Roo.Element} The element
35705      */
35706     getEl : function(){
35707         if(!this.el){
35708             this.render();
35709         }
35710         return this.el;
35711     },
35712
35713     /**
35714      * Adds a separator bar to the menu
35715      * @return {Roo.menu.Item} The menu item that was added
35716      */
35717     addSeparator : function(){
35718         return this.addItem(new Roo.menu.Separator());
35719     },
35720
35721     /**
35722      * Adds an {@link Roo.Element} object to the menu
35723      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35724      * @return {Roo.menu.Item} The menu item that was added
35725      */
35726     addElement : function(el){
35727         return this.addItem(new Roo.menu.BaseItem(el));
35728     },
35729
35730     /**
35731      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35732      * @param {Roo.menu.Item} item The menu item to add
35733      * @return {Roo.menu.Item} The menu item that was added
35734      */
35735     addItem : function(item){
35736         this.items.add(item);
35737         if(this.ul){
35738             var li = document.createElement("li");
35739             li.className = "x-menu-list-item";
35740             this.ul.dom.appendChild(li);
35741             item.render(li, this);
35742             this.delayAutoWidth();
35743         }
35744         return item;
35745     },
35746
35747     /**
35748      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35749      * @param {Object} config A MenuItem config object
35750      * @return {Roo.menu.Item} The menu item that was added
35751      */
35752     addMenuItem : function(config){
35753         if(!(config instanceof Roo.menu.Item)){
35754             if(typeof config.checked == "boolean"){ // must be check menu item config?
35755                 config = new Roo.menu.CheckItem(config);
35756             }else{
35757                 config = new Roo.menu.Item(config);
35758             }
35759         }
35760         return this.addItem(config);
35761     },
35762
35763     /**
35764      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35765      * @param {String} text The text to display in the menu item
35766      * @return {Roo.menu.Item} The menu item that was added
35767      */
35768     addText : function(text){
35769         return this.addItem(new Roo.menu.TextItem({ text : text }));
35770     },
35771
35772     /**
35773      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35774      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35775      * @param {Roo.menu.Item} item The menu item to add
35776      * @return {Roo.menu.Item} The menu item that was added
35777      */
35778     insert : function(index, item){
35779         this.items.insert(index, item);
35780         if(this.ul){
35781             var li = document.createElement("li");
35782             li.className = "x-menu-list-item";
35783             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35784             item.render(li, this);
35785             this.delayAutoWidth();
35786         }
35787         return item;
35788     },
35789
35790     /**
35791      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35792      * @param {Roo.menu.Item} item The menu item to remove
35793      */
35794     remove : function(item){
35795         this.items.removeKey(item.id);
35796         item.destroy();
35797     },
35798
35799     /**
35800      * Removes and destroys all items in the menu
35801      */
35802     removeAll : function(){
35803         var f;
35804         while(f = this.items.first()){
35805             this.remove(f);
35806         }
35807     }
35808 });
35809
35810 // MenuNav is a private utility class used internally by the Menu
35811 Roo.menu.MenuNav = function(menu){
35812     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35813     this.scope = this.menu = menu;
35814 };
35815
35816 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35817     doRelay : function(e, h){
35818         var k = e.getKey();
35819         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35820             this.menu.tryActivate(0, 1);
35821             return false;
35822         }
35823         return h.call(this.scope || this, e, this.menu);
35824     },
35825
35826     up : function(e, m){
35827         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35828             m.tryActivate(m.items.length-1, -1);
35829         }
35830     },
35831
35832     down : function(e, m){
35833         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35834             m.tryActivate(0, 1);
35835         }
35836     },
35837
35838     right : function(e, m){
35839         if(m.activeItem){
35840             m.activeItem.expandMenu(true);
35841         }
35842     },
35843
35844     left : function(e, m){
35845         m.hide();
35846         if(m.parentMenu && m.parentMenu.activeItem){
35847             m.parentMenu.activeItem.activate();
35848         }
35849     },
35850
35851     enter : function(e, m){
35852         if(m.activeItem){
35853             e.stopPropagation();
35854             m.activeItem.onClick(e);
35855             m.fireEvent("click", this, m.activeItem);
35856             return true;
35857         }
35858     }
35859 });/*
35860  * Based on:
35861  * Ext JS Library 1.1.1
35862  * Copyright(c) 2006-2007, Ext JS, LLC.
35863  *
35864  * Originally Released Under LGPL - original licence link has changed is not relivant.
35865  *
35866  * Fork - LGPL
35867  * <script type="text/javascript">
35868  */
35869  
35870 /**
35871  * @class Roo.menu.MenuMgr
35872  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35873  * @singleton
35874  */
35875 Roo.menu.MenuMgr = function(){
35876    var menus, active, groups = {}, attached = false, lastShow = new Date();
35877
35878    // private - called when first menu is created
35879    function init(){
35880        menus = {};
35881        active = new Roo.util.MixedCollection();
35882        Roo.get(document).addKeyListener(27, function(){
35883            if(active.length > 0){
35884                hideAll();
35885            }
35886        });
35887    }
35888
35889    // private
35890    function hideAll(){
35891        if(active && active.length > 0){
35892            var c = active.clone();
35893            c.each(function(m){
35894                m.hide();
35895            });
35896        }
35897    }
35898
35899    // private
35900    function onHide(m){
35901        active.remove(m);
35902        if(active.length < 1){
35903            Roo.get(document).un("mousedown", onMouseDown);
35904            attached = false;
35905        }
35906    }
35907
35908    // private
35909    function onShow(m){
35910        var last = active.last();
35911        lastShow = new Date();
35912        active.add(m);
35913        if(!attached){
35914            Roo.get(document).on("mousedown", onMouseDown);
35915            attached = true;
35916        }
35917        if(m.parentMenu){
35918           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35919           m.parentMenu.activeChild = m;
35920        }else if(last && last.isVisible()){
35921           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35922        }
35923    }
35924
35925    // private
35926    function onBeforeHide(m){
35927        if(m.activeChild){
35928            m.activeChild.hide();
35929        }
35930        if(m.autoHideTimer){
35931            clearTimeout(m.autoHideTimer);
35932            delete m.autoHideTimer;
35933        }
35934    }
35935
35936    // private
35937    function onBeforeShow(m){
35938        var pm = m.parentMenu;
35939        if(!pm && !m.allowOtherMenus){
35940            hideAll();
35941        }else if(pm && pm.activeChild && active != m){
35942            pm.activeChild.hide();
35943        }
35944    }
35945
35946    // private
35947    function onMouseDown(e){
35948        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35949            hideAll();
35950        }
35951    }
35952
35953    // private
35954    function onBeforeCheck(mi, state){
35955        if(state){
35956            var g = groups[mi.group];
35957            for(var i = 0, l = g.length; i < l; i++){
35958                if(g[i] != mi){
35959                    g[i].setChecked(false);
35960                }
35961            }
35962        }
35963    }
35964
35965    return {
35966
35967        /**
35968         * Hides all menus that are currently visible
35969         */
35970        hideAll : function(){
35971             hideAll();  
35972        },
35973
35974        // private
35975        register : function(menu){
35976            if(!menus){
35977                init();
35978            }
35979            menus[menu.id] = menu;
35980            menu.on("beforehide", onBeforeHide);
35981            menu.on("hide", onHide);
35982            menu.on("beforeshow", onBeforeShow);
35983            menu.on("show", onShow);
35984            var g = menu.group;
35985            if(g && menu.events["checkchange"]){
35986                if(!groups[g]){
35987                    groups[g] = [];
35988                }
35989                groups[g].push(menu);
35990                menu.on("checkchange", onCheck);
35991            }
35992        },
35993
35994         /**
35995          * Returns a {@link Roo.menu.Menu} object
35996          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35997          * be used to generate and return a new Menu instance.
35998          */
35999        get : function(menu){
36000            if(typeof menu == "string"){ // menu id
36001                return menus[menu];
36002            }else if(menu.events){  // menu instance
36003                return menu;
36004            }else if(typeof menu.length == 'number'){ // array of menu items?
36005                return new Roo.menu.Menu({items:menu});
36006            }else{ // otherwise, must be a config
36007                return new Roo.menu.Menu(menu);
36008            }
36009        },
36010
36011        // private
36012        unregister : function(menu){
36013            delete menus[menu.id];
36014            menu.un("beforehide", onBeforeHide);
36015            menu.un("hide", onHide);
36016            menu.un("beforeshow", onBeforeShow);
36017            menu.un("show", onShow);
36018            var g = menu.group;
36019            if(g && menu.events["checkchange"]){
36020                groups[g].remove(menu);
36021                menu.un("checkchange", onCheck);
36022            }
36023        },
36024
36025        // private
36026        registerCheckable : function(menuItem){
36027            var g = menuItem.group;
36028            if(g){
36029                if(!groups[g]){
36030                    groups[g] = [];
36031                }
36032                groups[g].push(menuItem);
36033                menuItem.on("beforecheckchange", onBeforeCheck);
36034            }
36035        },
36036
36037        // private
36038        unregisterCheckable : function(menuItem){
36039            var g = menuItem.group;
36040            if(g){
36041                groups[g].remove(menuItem);
36042                menuItem.un("beforecheckchange", onBeforeCheck);
36043            }
36044        }
36045    };
36046 }();/*
36047  * Based on:
36048  * Ext JS Library 1.1.1
36049  * Copyright(c) 2006-2007, Ext JS, LLC.
36050  *
36051  * Originally Released Under LGPL - original licence link has changed is not relivant.
36052  *
36053  * Fork - LGPL
36054  * <script type="text/javascript">
36055  */
36056  
36057
36058 /**
36059  * @class Roo.menu.BaseItem
36060  * @extends Roo.Component
36061  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36062  * management and base configuration options shared by all menu components.
36063  * @constructor
36064  * Creates a new BaseItem
36065  * @param {Object} config Configuration options
36066  */
36067 Roo.menu.BaseItem = function(config){
36068     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36069
36070     this.addEvents({
36071         /**
36072          * @event click
36073          * Fires when this item is clicked
36074          * @param {Roo.menu.BaseItem} this
36075          * @param {Roo.EventObject} e
36076          */
36077         click: true,
36078         /**
36079          * @event activate
36080          * Fires when this item is activated
36081          * @param {Roo.menu.BaseItem} this
36082          */
36083         activate : true,
36084         /**
36085          * @event deactivate
36086          * Fires when this item is deactivated
36087          * @param {Roo.menu.BaseItem} this
36088          */
36089         deactivate : true
36090     });
36091
36092     if(this.handler){
36093         this.on("click", this.handler, this.scope, true);
36094     }
36095 };
36096
36097 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36098     /**
36099      * @cfg {Function} handler
36100      * A function that will handle the click event of this menu item (defaults to undefined)
36101      */
36102     /**
36103      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36104      */
36105     canActivate : false,
36106     
36107      /**
36108      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36109      */
36110     hidden: false,
36111     
36112     /**
36113      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36114      */
36115     activeClass : "x-menu-item-active",
36116     /**
36117      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36118      */
36119     hideOnClick : true,
36120     /**
36121      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36122      */
36123     hideDelay : 100,
36124
36125     // private
36126     ctype: "Roo.menu.BaseItem",
36127
36128     // private
36129     actionMode : "container",
36130
36131     // private
36132     render : function(container, parentMenu){
36133         this.parentMenu = parentMenu;
36134         Roo.menu.BaseItem.superclass.render.call(this, container);
36135         this.container.menuItemId = this.id;
36136     },
36137
36138     // private
36139     onRender : function(container, position){
36140         this.el = Roo.get(this.el);
36141         container.dom.appendChild(this.el.dom);
36142     },
36143
36144     // private
36145     onClick : function(e){
36146         if(!this.disabled && this.fireEvent("click", this, e) !== false
36147                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36148             this.handleClick(e);
36149         }else{
36150             e.stopEvent();
36151         }
36152     },
36153
36154     // private
36155     activate : function(){
36156         if(this.disabled){
36157             return false;
36158         }
36159         var li = this.container;
36160         li.addClass(this.activeClass);
36161         this.region = li.getRegion().adjust(2, 2, -2, -2);
36162         this.fireEvent("activate", this);
36163         return true;
36164     },
36165
36166     // private
36167     deactivate : function(){
36168         this.container.removeClass(this.activeClass);
36169         this.fireEvent("deactivate", this);
36170     },
36171
36172     // private
36173     shouldDeactivate : function(e){
36174         return !this.region || !this.region.contains(e.getPoint());
36175     },
36176
36177     // private
36178     handleClick : function(e){
36179         if(this.hideOnClick){
36180             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36181         }
36182     },
36183
36184     // private
36185     expandMenu : function(autoActivate){
36186         // do nothing
36187     },
36188
36189     // private
36190     hideMenu : function(){
36191         // do nothing
36192     }
36193 });/*
36194  * Based on:
36195  * Ext JS Library 1.1.1
36196  * Copyright(c) 2006-2007, Ext JS, LLC.
36197  *
36198  * Originally Released Under LGPL - original licence link has changed is not relivant.
36199  *
36200  * Fork - LGPL
36201  * <script type="text/javascript">
36202  */
36203  
36204 /**
36205  * @class Roo.menu.Adapter
36206  * @extends Roo.menu.BaseItem
36207  * 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.
36208  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36209  * @constructor
36210  * Creates a new Adapter
36211  * @param {Object} config Configuration options
36212  */
36213 Roo.menu.Adapter = function(component, config){
36214     Roo.menu.Adapter.superclass.constructor.call(this, config);
36215     this.component = component;
36216 };
36217 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36218     // private
36219     canActivate : true,
36220
36221     // private
36222     onRender : function(container, position){
36223         this.component.render(container);
36224         this.el = this.component.getEl();
36225     },
36226
36227     // private
36228     activate : function(){
36229         if(this.disabled){
36230             return false;
36231         }
36232         this.component.focus();
36233         this.fireEvent("activate", this);
36234         return true;
36235     },
36236
36237     // private
36238     deactivate : function(){
36239         this.fireEvent("deactivate", this);
36240     },
36241
36242     // private
36243     disable : function(){
36244         this.component.disable();
36245         Roo.menu.Adapter.superclass.disable.call(this);
36246     },
36247
36248     // private
36249     enable : function(){
36250         this.component.enable();
36251         Roo.menu.Adapter.superclass.enable.call(this);
36252     }
36253 });/*
36254  * Based on:
36255  * Ext JS Library 1.1.1
36256  * Copyright(c) 2006-2007, Ext JS, LLC.
36257  *
36258  * Originally Released Under LGPL - original licence link has changed is not relivant.
36259  *
36260  * Fork - LGPL
36261  * <script type="text/javascript">
36262  */
36263
36264 /**
36265  * @class Roo.menu.TextItem
36266  * @extends Roo.menu.BaseItem
36267  * Adds a static text string to a menu, usually used as either a heading or group separator.
36268  * Note: old style constructor with text is still supported.
36269  * 
36270  * @constructor
36271  * Creates a new TextItem
36272  * @param {Object} cfg Configuration
36273  */
36274 Roo.menu.TextItem = function(cfg){
36275     if (typeof(cfg) == 'string') {
36276         this.text = cfg;
36277     } else {
36278         Roo.apply(this,cfg);
36279     }
36280     
36281     Roo.menu.TextItem.superclass.constructor.call(this);
36282 };
36283
36284 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36285     /**
36286      * @cfg {Boolean} text Text to show on item.
36287      */
36288     text : '',
36289     
36290     /**
36291      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36292      */
36293     hideOnClick : false,
36294     /**
36295      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36296      */
36297     itemCls : "x-menu-text",
36298
36299     // private
36300     onRender : function(){
36301         var s = document.createElement("span");
36302         s.className = this.itemCls;
36303         s.innerHTML = this.text;
36304         this.el = s;
36305         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36306     }
36307 });/*
36308  * Based on:
36309  * Ext JS Library 1.1.1
36310  * Copyright(c) 2006-2007, Ext JS, LLC.
36311  *
36312  * Originally Released Under LGPL - original licence link has changed is not relivant.
36313  *
36314  * Fork - LGPL
36315  * <script type="text/javascript">
36316  */
36317
36318 /**
36319  * @class Roo.menu.Separator
36320  * @extends Roo.menu.BaseItem
36321  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36322  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36323  * @constructor
36324  * @param {Object} config Configuration options
36325  */
36326 Roo.menu.Separator = function(config){
36327     Roo.menu.Separator.superclass.constructor.call(this, config);
36328 };
36329
36330 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36331     /**
36332      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36333      */
36334     itemCls : "x-menu-sep",
36335     /**
36336      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36337      */
36338     hideOnClick : false,
36339
36340     // private
36341     onRender : function(li){
36342         var s = document.createElement("span");
36343         s.className = this.itemCls;
36344         s.innerHTML = "&#160;";
36345         this.el = s;
36346         li.addClass("x-menu-sep-li");
36347         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36348     }
36349 });/*
36350  * Based on:
36351  * Ext JS Library 1.1.1
36352  * Copyright(c) 2006-2007, Ext JS, LLC.
36353  *
36354  * Originally Released Under LGPL - original licence link has changed is not relivant.
36355  *
36356  * Fork - LGPL
36357  * <script type="text/javascript">
36358  */
36359 /**
36360  * @class Roo.menu.Item
36361  * @extends Roo.menu.BaseItem
36362  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36363  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36364  * activation and click handling.
36365  * @constructor
36366  * Creates a new Item
36367  * @param {Object} config Configuration options
36368  */
36369 Roo.menu.Item = function(config){
36370     Roo.menu.Item.superclass.constructor.call(this, config);
36371     if(this.menu){
36372         this.menu = Roo.menu.MenuMgr.get(this.menu);
36373     }
36374 };
36375 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36376     
36377     /**
36378      * @cfg {String} text
36379      * The text to show on the menu item.
36380      */
36381     text: '',
36382      /**
36383      * @cfg {String} HTML to render in menu
36384      * The text to show on the menu item (HTML version).
36385      */
36386     html: '',
36387     /**
36388      * @cfg {String} icon
36389      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36390      */
36391     icon: undefined,
36392     /**
36393      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36394      */
36395     itemCls : "x-menu-item",
36396     /**
36397      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36398      */
36399     canActivate : true,
36400     /**
36401      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36402      */
36403     showDelay: 200,
36404     // doc'd in BaseItem
36405     hideDelay: 200,
36406
36407     // private
36408     ctype: "Roo.menu.Item",
36409     
36410     // private
36411     onRender : function(container, position){
36412         var el = document.createElement("a");
36413         el.hideFocus = true;
36414         el.unselectable = "on";
36415         el.href = this.href || "#";
36416         if(this.hrefTarget){
36417             el.target = this.hrefTarget;
36418         }
36419         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36420         
36421         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36422         
36423         el.innerHTML = String.format(
36424                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36425                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36426         this.el = el;
36427         Roo.menu.Item.superclass.onRender.call(this, container, position);
36428     },
36429
36430     /**
36431      * Sets the text to display in this menu item
36432      * @param {String} text The text to display
36433      * @param {Boolean} isHTML true to indicate text is pure html.
36434      */
36435     setText : function(text, isHTML){
36436         if (isHTML) {
36437             this.html = text;
36438         } else {
36439             this.text = text;
36440             this.html = '';
36441         }
36442         if(this.rendered){
36443             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36444      
36445             this.el.update(String.format(
36446                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36447                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36448             this.parentMenu.autoWidth();
36449         }
36450     },
36451
36452     // private
36453     handleClick : function(e){
36454         if(!this.href){ // if no link defined, stop the event automatically
36455             e.stopEvent();
36456         }
36457         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36458     },
36459
36460     // private
36461     activate : function(autoExpand){
36462         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36463             this.focus();
36464             if(autoExpand){
36465                 this.expandMenu();
36466             }
36467         }
36468         return true;
36469     },
36470
36471     // private
36472     shouldDeactivate : function(e){
36473         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36474             if(this.menu && this.menu.isVisible()){
36475                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36476             }
36477             return true;
36478         }
36479         return false;
36480     },
36481
36482     // private
36483     deactivate : function(){
36484         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36485         this.hideMenu();
36486     },
36487
36488     // private
36489     expandMenu : function(autoActivate){
36490         if(!this.disabled && this.menu){
36491             clearTimeout(this.hideTimer);
36492             delete this.hideTimer;
36493             if(!this.menu.isVisible() && !this.showTimer){
36494                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36495             }else if (this.menu.isVisible() && autoActivate){
36496                 this.menu.tryActivate(0, 1);
36497             }
36498         }
36499     },
36500
36501     // private
36502     deferExpand : function(autoActivate){
36503         delete this.showTimer;
36504         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36505         if(autoActivate){
36506             this.menu.tryActivate(0, 1);
36507         }
36508     },
36509
36510     // private
36511     hideMenu : function(){
36512         clearTimeout(this.showTimer);
36513         delete this.showTimer;
36514         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36515             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36516         }
36517     },
36518
36519     // private
36520     deferHide : function(){
36521         delete this.hideTimer;
36522         this.menu.hide();
36523     }
36524 });/*
36525  * Based on:
36526  * Ext JS Library 1.1.1
36527  * Copyright(c) 2006-2007, Ext JS, LLC.
36528  *
36529  * Originally Released Under LGPL - original licence link has changed is not relivant.
36530  *
36531  * Fork - LGPL
36532  * <script type="text/javascript">
36533  */
36534  
36535 /**
36536  * @class Roo.menu.CheckItem
36537  * @extends Roo.menu.Item
36538  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36539  * @constructor
36540  * Creates a new CheckItem
36541  * @param {Object} config Configuration options
36542  */
36543 Roo.menu.CheckItem = function(config){
36544     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36545     this.addEvents({
36546         /**
36547          * @event beforecheckchange
36548          * Fires before the checked value is set, providing an opportunity to cancel if needed
36549          * @param {Roo.menu.CheckItem} this
36550          * @param {Boolean} checked The new checked value that will be set
36551          */
36552         "beforecheckchange" : true,
36553         /**
36554          * @event checkchange
36555          * Fires after the checked value has been set
36556          * @param {Roo.menu.CheckItem} this
36557          * @param {Boolean} checked The checked value that was set
36558          */
36559         "checkchange" : true
36560     });
36561     if(this.checkHandler){
36562         this.on('checkchange', this.checkHandler, this.scope);
36563     }
36564 };
36565 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36566     /**
36567      * @cfg {String} group
36568      * All check items with the same group name will automatically be grouped into a single-select
36569      * radio button group (defaults to '')
36570      */
36571     /**
36572      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36573      */
36574     itemCls : "x-menu-item x-menu-check-item",
36575     /**
36576      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36577      */
36578     groupClass : "x-menu-group-item",
36579
36580     /**
36581      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36582      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36583      * initialized with checked = true will be rendered as checked.
36584      */
36585     checked: false,
36586
36587     // private
36588     ctype: "Roo.menu.CheckItem",
36589
36590     // private
36591     onRender : function(c){
36592         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36593         if(this.group){
36594             this.el.addClass(this.groupClass);
36595         }
36596         Roo.menu.MenuMgr.registerCheckable(this);
36597         if(this.checked){
36598             this.checked = false;
36599             this.setChecked(true, true);
36600         }
36601     },
36602
36603     // private
36604     destroy : function(){
36605         if(this.rendered){
36606             Roo.menu.MenuMgr.unregisterCheckable(this);
36607         }
36608         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36609     },
36610
36611     /**
36612      * Set the checked state of this item
36613      * @param {Boolean} checked The new checked value
36614      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36615      */
36616     setChecked : function(state, suppressEvent){
36617         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36618             if(this.container){
36619                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36620             }
36621             this.checked = state;
36622             if(suppressEvent !== true){
36623                 this.fireEvent("checkchange", this, state);
36624             }
36625         }
36626     },
36627
36628     // private
36629     handleClick : function(e){
36630        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36631            this.setChecked(!this.checked);
36632        }
36633        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36634     }
36635 });/*
36636  * Based on:
36637  * Ext JS Library 1.1.1
36638  * Copyright(c) 2006-2007, Ext JS, LLC.
36639  *
36640  * Originally Released Under LGPL - original licence link has changed is not relivant.
36641  *
36642  * Fork - LGPL
36643  * <script type="text/javascript">
36644  */
36645  
36646 /**
36647  * @class Roo.menu.DateItem
36648  * @extends Roo.menu.Adapter
36649  * A menu item that wraps the {@link Roo.DatPicker} component.
36650  * @constructor
36651  * Creates a new DateItem
36652  * @param {Object} config Configuration options
36653  */
36654 Roo.menu.DateItem = function(config){
36655     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36656     /** The Roo.DatePicker object @type Roo.DatePicker */
36657     this.picker = this.component;
36658     this.addEvents({select: true});
36659     
36660     this.picker.on("render", function(picker){
36661         picker.getEl().swallowEvent("click");
36662         picker.container.addClass("x-menu-date-item");
36663     });
36664
36665     this.picker.on("select", this.onSelect, this);
36666 };
36667
36668 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36669     // private
36670     onSelect : function(picker, date){
36671         this.fireEvent("select", this, date, picker);
36672         Roo.menu.DateItem.superclass.handleClick.call(this);
36673     }
36674 });/*
36675  * Based on:
36676  * Ext JS Library 1.1.1
36677  * Copyright(c) 2006-2007, Ext JS, LLC.
36678  *
36679  * Originally Released Under LGPL - original licence link has changed is not relivant.
36680  *
36681  * Fork - LGPL
36682  * <script type="text/javascript">
36683  */
36684  
36685 /**
36686  * @class Roo.menu.ColorItem
36687  * @extends Roo.menu.Adapter
36688  * A menu item that wraps the {@link Roo.ColorPalette} component.
36689  * @constructor
36690  * Creates a new ColorItem
36691  * @param {Object} config Configuration options
36692  */
36693 Roo.menu.ColorItem = function(config){
36694     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36695     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36696     this.palette = this.component;
36697     this.relayEvents(this.palette, ["select"]);
36698     if(this.selectHandler){
36699         this.on('select', this.selectHandler, this.scope);
36700     }
36701 };
36702 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36703  * Based on:
36704  * Ext JS Library 1.1.1
36705  * Copyright(c) 2006-2007, Ext JS, LLC.
36706  *
36707  * Originally Released Under LGPL - original licence link has changed is not relivant.
36708  *
36709  * Fork - LGPL
36710  * <script type="text/javascript">
36711  */
36712  
36713
36714 /**
36715  * @class Roo.menu.DateMenu
36716  * @extends Roo.menu.Menu
36717  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36718  * @constructor
36719  * Creates a new DateMenu
36720  * @param {Object} config Configuration options
36721  */
36722 Roo.menu.DateMenu = function(config){
36723     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36724     this.plain = true;
36725     var di = new Roo.menu.DateItem(config);
36726     this.add(di);
36727     /**
36728      * The {@link Roo.DatePicker} instance for this DateMenu
36729      * @type DatePicker
36730      */
36731     this.picker = di.picker;
36732     /**
36733      * @event select
36734      * @param {DatePicker} picker
36735      * @param {Date} date
36736      */
36737     this.relayEvents(di, ["select"]);
36738     this.on('beforeshow', function(){
36739         if(this.picker){
36740             this.picker.hideMonthPicker(false);
36741         }
36742     }, this);
36743 };
36744 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36745     cls:'x-date-menu'
36746 });/*
36747  * Based on:
36748  * Ext JS Library 1.1.1
36749  * Copyright(c) 2006-2007, Ext JS, LLC.
36750  *
36751  * Originally Released Under LGPL - original licence link has changed is not relivant.
36752  *
36753  * Fork - LGPL
36754  * <script type="text/javascript">
36755  */
36756  
36757
36758 /**
36759  * @class Roo.menu.ColorMenu
36760  * @extends Roo.menu.Menu
36761  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36762  * @constructor
36763  * Creates a new ColorMenu
36764  * @param {Object} config Configuration options
36765  */
36766 Roo.menu.ColorMenu = function(config){
36767     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36768     this.plain = true;
36769     var ci = new Roo.menu.ColorItem(config);
36770     this.add(ci);
36771     /**
36772      * The {@link Roo.ColorPalette} instance for this ColorMenu
36773      * @type ColorPalette
36774      */
36775     this.palette = ci.palette;
36776     /**
36777      * @event select
36778      * @param {ColorPalette} palette
36779      * @param {String} color
36780      */
36781     this.relayEvents(ci, ["select"]);
36782 };
36783 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36784  * Based on:
36785  * Ext JS Library 1.1.1
36786  * Copyright(c) 2006-2007, Ext JS, LLC.
36787  *
36788  * Originally Released Under LGPL - original licence link has changed is not relivant.
36789  *
36790  * Fork - LGPL
36791  * <script type="text/javascript">
36792  */
36793  
36794 /**
36795  * @class Roo.form.Field
36796  * @extends Roo.BoxComponent
36797  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36798  * @constructor
36799  * Creates a new Field
36800  * @param {Object} config Configuration options
36801  */
36802 Roo.form.Field = function(config){
36803     Roo.form.Field.superclass.constructor.call(this, config);
36804 };
36805
36806 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36807     /**
36808      * @cfg {String} fieldLabel Label to use when rendering a form.
36809      */
36810        /**
36811      * @cfg {String} qtip Mouse over tip
36812      */
36813      
36814     /**
36815      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36816      */
36817     invalidClass : "x-form-invalid",
36818     /**
36819      * @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")
36820      */
36821     invalidText : "The value in this field is invalid",
36822     /**
36823      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36824      */
36825     focusClass : "x-form-focus",
36826     /**
36827      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36828       automatic validation (defaults to "keyup").
36829      */
36830     validationEvent : "keyup",
36831     /**
36832      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36833      */
36834     validateOnBlur : true,
36835     /**
36836      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36837      */
36838     validationDelay : 250,
36839     /**
36840      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36841      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36842      */
36843     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36844     /**
36845      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36846      */
36847     fieldClass : "x-form-field",
36848     /**
36849      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36850      *<pre>
36851 Value         Description
36852 -----------   ----------------------------------------------------------------------
36853 qtip          Display a quick tip when the user hovers over the field
36854 title         Display a default browser title attribute popup
36855 under         Add a block div beneath the field containing the error text
36856 side          Add an error icon to the right of the field with a popup on hover
36857 [element id]  Add the error text directly to the innerHTML of the specified element
36858 </pre>
36859      */
36860     msgTarget : 'qtip',
36861     /**
36862      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36863      */
36864     msgFx : 'normal',
36865
36866     /**
36867      * @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.
36868      */
36869     readOnly : false,
36870
36871     /**
36872      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36873      */
36874     disabled : false,
36875
36876     /**
36877      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36878      */
36879     inputType : undefined,
36880     
36881     /**
36882      * @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).
36883          */
36884         tabIndex : undefined,
36885         
36886     // private
36887     isFormField : true,
36888
36889     // private
36890     hasFocus : false,
36891     /**
36892      * @property {Roo.Element} fieldEl
36893      * Element Containing the rendered Field (with label etc.)
36894      */
36895     /**
36896      * @cfg {Mixed} value A value to initialize this field with.
36897      */
36898     value : undefined,
36899
36900     /**
36901      * @cfg {String} name The field's HTML name attribute.
36902      */
36903     /**
36904      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36905      */
36906
36907         // private ??
36908         initComponent : function(){
36909         Roo.form.Field.superclass.initComponent.call(this);
36910         this.addEvents({
36911             /**
36912              * @event focus
36913              * Fires when this field receives input focus.
36914              * @param {Roo.form.Field} this
36915              */
36916             focus : true,
36917             /**
36918              * @event blur
36919              * Fires when this field loses input focus.
36920              * @param {Roo.form.Field} this
36921              */
36922             blur : true,
36923             /**
36924              * @event specialkey
36925              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36926              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36927              * @param {Roo.form.Field} this
36928              * @param {Roo.EventObject} e The event object
36929              */
36930             specialkey : true,
36931             /**
36932              * @event change
36933              * Fires just before the field blurs if the field value has changed.
36934              * @param {Roo.form.Field} this
36935              * @param {Mixed} newValue The new value
36936              * @param {Mixed} oldValue The original value
36937              */
36938             change : true,
36939             /**
36940              * @event invalid
36941              * Fires after the field has been marked as invalid.
36942              * @param {Roo.form.Field} this
36943              * @param {String} msg The validation message
36944              */
36945             invalid : true,
36946             /**
36947              * @event valid
36948              * Fires after the field has been validated with no errors.
36949              * @param {Roo.form.Field} this
36950              */
36951             valid : true,
36952              /**
36953              * @event keyup
36954              * Fires after the key up
36955              * @param {Roo.form.Field} this
36956              * @param {Roo.EventObject}  e The event Object
36957              */
36958             keyup : true
36959         });
36960     },
36961
36962     /**
36963      * Returns the name attribute of the field if available
36964      * @return {String} name The field name
36965      */
36966     getName: function(){
36967          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36968     },
36969
36970     // private
36971     onRender : function(ct, position){
36972         Roo.form.Field.superclass.onRender.call(this, ct, position);
36973         if(!this.el){
36974             var cfg = this.getAutoCreate();
36975             if(!cfg.name){
36976                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36977             }
36978             if (!cfg.name.length) {
36979                 delete cfg.name;
36980             }
36981             if(this.inputType){
36982                 cfg.type = this.inputType;
36983             }
36984             this.el = ct.createChild(cfg, position);
36985         }
36986         var type = this.el.dom.type;
36987         if(type){
36988             if(type == 'password'){
36989                 type = 'text';
36990             }
36991             this.el.addClass('x-form-'+type);
36992         }
36993         if(this.readOnly){
36994             this.el.dom.readOnly = true;
36995         }
36996         if(this.tabIndex !== undefined){
36997             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36998         }
36999
37000         this.el.addClass([this.fieldClass, this.cls]);
37001         this.initValue();
37002     },
37003
37004     /**
37005      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37006      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37007      * @return {Roo.form.Field} this
37008      */
37009     applyTo : function(target){
37010         this.allowDomMove = false;
37011         this.el = Roo.get(target);
37012         this.render(this.el.dom.parentNode);
37013         return this;
37014     },
37015
37016     // private
37017     initValue : function(){
37018         if(this.value !== undefined){
37019             this.setValue(this.value);
37020         }else if(this.el.dom.value.length > 0){
37021             this.setValue(this.el.dom.value);
37022         }
37023     },
37024
37025     /**
37026      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37027      */
37028     isDirty : function() {
37029         if(this.disabled) {
37030             return false;
37031         }
37032         return String(this.getValue()) !== String(this.originalValue);
37033     },
37034
37035     // private
37036     afterRender : function(){
37037         Roo.form.Field.superclass.afterRender.call(this);
37038         this.initEvents();
37039     },
37040
37041     // private
37042     fireKey : function(e){
37043         //Roo.log('field ' + e.getKey());
37044         if(e.isNavKeyPress()){
37045             this.fireEvent("specialkey", this, e);
37046         }
37047     },
37048
37049     /**
37050      * Resets the current field value to the originally loaded value and clears any validation messages
37051      */
37052     reset : function(){
37053         this.setValue(this.resetValue);
37054         this.clearInvalid();
37055     },
37056
37057     // private
37058     initEvents : function(){
37059         // safari killled keypress - so keydown is now used..
37060         this.el.on("keydown" , this.fireKey,  this);
37061         this.el.on("focus", this.onFocus,  this);
37062         this.el.on("blur", this.onBlur,  this);
37063         this.el.relayEvent('keyup', this);
37064
37065         // reference to original value for reset
37066         this.originalValue = this.getValue();
37067         this.resetValue =  this.getValue();
37068     },
37069
37070     // private
37071     onFocus : function(){
37072         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37073             this.el.addClass(this.focusClass);
37074         }
37075         if(!this.hasFocus){
37076             this.hasFocus = true;
37077             this.startValue = this.getValue();
37078             this.fireEvent("focus", this);
37079         }
37080     },
37081
37082     beforeBlur : Roo.emptyFn,
37083
37084     // private
37085     onBlur : function(){
37086         this.beforeBlur();
37087         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37088             this.el.removeClass(this.focusClass);
37089         }
37090         this.hasFocus = false;
37091         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37092             this.validate();
37093         }
37094         var v = this.getValue();
37095         if(String(v) !== String(this.startValue)){
37096             this.fireEvent('change', this, v, this.startValue);
37097         }
37098         this.fireEvent("blur", this);
37099     },
37100
37101     /**
37102      * Returns whether or not the field value is currently valid
37103      * @param {Boolean} preventMark True to disable marking the field invalid
37104      * @return {Boolean} True if the value is valid, else false
37105      */
37106     isValid : function(preventMark){
37107         if(this.disabled){
37108             return true;
37109         }
37110         var restore = this.preventMark;
37111         this.preventMark = preventMark === true;
37112         var v = this.validateValue(this.processValue(this.getRawValue()));
37113         this.preventMark = restore;
37114         return v;
37115     },
37116
37117     /**
37118      * Validates the field value
37119      * @return {Boolean} True if the value is valid, else false
37120      */
37121     validate : function(){
37122         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37123             this.clearInvalid();
37124             return true;
37125         }
37126         return false;
37127     },
37128
37129     processValue : function(value){
37130         return value;
37131     },
37132
37133     // private
37134     // Subclasses should provide the validation implementation by overriding this
37135     validateValue : function(value){
37136         return true;
37137     },
37138
37139     /**
37140      * Mark this field as invalid
37141      * @param {String} msg The validation message
37142      */
37143     markInvalid : function(msg){
37144         if(!this.rendered || this.preventMark){ // not rendered
37145             return;
37146         }
37147         
37148         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37149         
37150         obj.el.addClass(this.invalidClass);
37151         msg = msg || this.invalidText;
37152         switch(this.msgTarget){
37153             case 'qtip':
37154                 obj.el.dom.qtip = msg;
37155                 obj.el.dom.qclass = 'x-form-invalid-tip';
37156                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37157                     Roo.QuickTips.enable();
37158                 }
37159                 break;
37160             case 'title':
37161                 this.el.dom.title = msg;
37162                 break;
37163             case 'under':
37164                 if(!this.errorEl){
37165                     var elp = this.el.findParent('.x-form-element', 5, true);
37166                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37167                     this.errorEl.setWidth(elp.getWidth(true)-20);
37168                 }
37169                 this.errorEl.update(msg);
37170                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37171                 break;
37172             case 'side':
37173                 if(!this.errorIcon){
37174                     var elp = this.el.findParent('.x-form-element', 5, true);
37175                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37176                 }
37177                 this.alignErrorIcon();
37178                 this.errorIcon.dom.qtip = msg;
37179                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37180                 this.errorIcon.show();
37181                 this.on('resize', this.alignErrorIcon, this);
37182                 break;
37183             default:
37184                 var t = Roo.getDom(this.msgTarget);
37185                 t.innerHTML = msg;
37186                 t.style.display = this.msgDisplay;
37187                 break;
37188         }
37189         this.fireEvent('invalid', this, msg);
37190     },
37191
37192     // private
37193     alignErrorIcon : function(){
37194         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37195     },
37196
37197     /**
37198      * Clear any invalid styles/messages for this field
37199      */
37200     clearInvalid : function(){
37201         if(!this.rendered || this.preventMark){ // not rendered
37202             return;
37203         }
37204         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37205         
37206         obj.el.removeClass(this.invalidClass);
37207         switch(this.msgTarget){
37208             case 'qtip':
37209                 obj.el.dom.qtip = '';
37210                 break;
37211             case 'title':
37212                 this.el.dom.title = '';
37213                 break;
37214             case 'under':
37215                 if(this.errorEl){
37216                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37217                 }
37218                 break;
37219             case 'side':
37220                 if(this.errorIcon){
37221                     this.errorIcon.dom.qtip = '';
37222                     this.errorIcon.hide();
37223                     this.un('resize', this.alignErrorIcon, this);
37224                 }
37225                 break;
37226             default:
37227                 var t = Roo.getDom(this.msgTarget);
37228                 t.innerHTML = '';
37229                 t.style.display = 'none';
37230                 break;
37231         }
37232         this.fireEvent('valid', this);
37233     },
37234
37235     /**
37236      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37237      * @return {Mixed} value The field value
37238      */
37239     getRawValue : function(){
37240         var v = this.el.getValue();
37241         
37242         return v;
37243     },
37244
37245     /**
37246      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37247      * @return {Mixed} value The field value
37248      */
37249     getValue : function(){
37250         var v = this.el.getValue();
37251          
37252         return v;
37253     },
37254
37255     /**
37256      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37257      * @param {Mixed} value The value to set
37258      */
37259     setRawValue : function(v){
37260         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37261     },
37262
37263     /**
37264      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37265      * @param {Mixed} value The value to set
37266      */
37267     setValue : function(v){
37268         this.value = v;
37269         if(this.rendered){
37270             this.el.dom.value = (v === null || v === undefined ? '' : v);
37271              this.validate();
37272         }
37273     },
37274
37275     adjustSize : function(w, h){
37276         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37277         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37278         return s;
37279     },
37280
37281     adjustWidth : function(tag, w){
37282         tag = tag.toLowerCase();
37283         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37284             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37285                 if(tag == 'input'){
37286                     return w + 2;
37287                 }
37288                 if(tag == 'textarea'){
37289                     return w-2;
37290                 }
37291             }else if(Roo.isOpera){
37292                 if(tag == 'input'){
37293                     return w + 2;
37294                 }
37295                 if(tag == 'textarea'){
37296                     return w-2;
37297                 }
37298             }
37299         }
37300         return w;
37301     }
37302 });
37303
37304
37305 // anything other than normal should be considered experimental
37306 Roo.form.Field.msgFx = {
37307     normal : {
37308         show: function(msgEl, f){
37309             msgEl.setDisplayed('block');
37310         },
37311
37312         hide : function(msgEl, f){
37313             msgEl.setDisplayed(false).update('');
37314         }
37315     },
37316
37317     slide : {
37318         show: function(msgEl, f){
37319             msgEl.slideIn('t', {stopFx:true});
37320         },
37321
37322         hide : function(msgEl, f){
37323             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37324         }
37325     },
37326
37327     slideRight : {
37328         show: function(msgEl, f){
37329             msgEl.fixDisplay();
37330             msgEl.alignTo(f.el, 'tl-tr');
37331             msgEl.slideIn('l', {stopFx:true});
37332         },
37333
37334         hide : function(msgEl, f){
37335             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37336         }
37337     }
37338 };/*
37339  * Based on:
37340  * Ext JS Library 1.1.1
37341  * Copyright(c) 2006-2007, Ext JS, LLC.
37342  *
37343  * Originally Released Under LGPL - original licence link has changed is not relivant.
37344  *
37345  * Fork - LGPL
37346  * <script type="text/javascript">
37347  */
37348  
37349
37350 /**
37351  * @class Roo.form.TextField
37352  * @extends Roo.form.Field
37353  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37354  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37355  * @constructor
37356  * Creates a new TextField
37357  * @param {Object} config Configuration options
37358  */
37359 Roo.form.TextField = function(config){
37360     Roo.form.TextField.superclass.constructor.call(this, config);
37361     this.addEvents({
37362         /**
37363          * @event autosize
37364          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37365          * according to the default logic, but this event provides a hook for the developer to apply additional
37366          * logic at runtime to resize the field if needed.
37367              * @param {Roo.form.Field} this This text field
37368              * @param {Number} width The new field width
37369              */
37370         autosize : true
37371     });
37372 };
37373
37374 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37375     /**
37376      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37377      */
37378     grow : false,
37379     /**
37380      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37381      */
37382     growMin : 30,
37383     /**
37384      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37385      */
37386     growMax : 800,
37387     /**
37388      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37389      */
37390     vtype : null,
37391     /**
37392      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37393      */
37394     maskRe : null,
37395     /**
37396      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37397      */
37398     disableKeyFilter : false,
37399     /**
37400      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37401      */
37402     allowBlank : true,
37403     /**
37404      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37405      */
37406     minLength : 0,
37407     /**
37408      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37409      */
37410     maxLength : Number.MAX_VALUE,
37411     /**
37412      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37413      */
37414     minLengthText : "The minimum length for this field is {0}",
37415     /**
37416      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37417      */
37418     maxLengthText : "The maximum length for this field is {0}",
37419     /**
37420      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37421      */
37422     selectOnFocus : false,
37423     /**
37424      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37425      */
37426     blankText : "This field is required",
37427     /**
37428      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37429      * If available, this function will be called only after the basic validators all return true, and will be passed the
37430      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37431      */
37432     validator : null,
37433     /**
37434      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37435      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37436      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37437      */
37438     regex : null,
37439     /**
37440      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37441      */
37442     regexText : "",
37443     /**
37444      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37445      */
37446     emptyText : null,
37447    
37448
37449     // private
37450     initEvents : function()
37451     {
37452         if (this.emptyText) {
37453             this.el.attr('placeholder', this.emptyText);
37454         }
37455         
37456         Roo.form.TextField.superclass.initEvents.call(this);
37457         if(this.validationEvent == 'keyup'){
37458             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37459             this.el.on('keyup', this.filterValidation, this);
37460         }
37461         else if(this.validationEvent !== false){
37462             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37463         }
37464         
37465         if(this.selectOnFocus){
37466             this.on("focus", this.preFocus, this);
37467             
37468         }
37469         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37470             this.el.on("keypress", this.filterKeys, this);
37471         }
37472         if(this.grow){
37473             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37474             this.el.on("click", this.autoSize,  this);
37475         }
37476         if(this.el.is('input[type=password]') && Roo.isSafari){
37477             this.el.on('keydown', this.SafariOnKeyDown, this);
37478         }
37479     },
37480
37481     processValue : function(value){
37482         if(this.stripCharsRe){
37483             var newValue = value.replace(this.stripCharsRe, '');
37484             if(newValue !== value){
37485                 this.setRawValue(newValue);
37486                 return newValue;
37487             }
37488         }
37489         return value;
37490     },
37491
37492     filterValidation : function(e){
37493         if(!e.isNavKeyPress()){
37494             this.validationTask.delay(this.validationDelay);
37495         }
37496     },
37497
37498     // private
37499     onKeyUp : function(e){
37500         if(!e.isNavKeyPress()){
37501             this.autoSize();
37502         }
37503     },
37504
37505     /**
37506      * Resets the current field value to the originally-loaded value and clears any validation messages.
37507      *  
37508      */
37509     reset : function(){
37510         Roo.form.TextField.superclass.reset.call(this);
37511        
37512     },
37513
37514     
37515     // private
37516     preFocus : function(){
37517         
37518         if(this.selectOnFocus){
37519             this.el.dom.select();
37520         }
37521     },
37522
37523     
37524     // private
37525     filterKeys : function(e){
37526         var k = e.getKey();
37527         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37528             return;
37529         }
37530         var c = e.getCharCode(), cc = String.fromCharCode(c);
37531         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37532             return;
37533         }
37534         if(!this.maskRe.test(cc)){
37535             e.stopEvent();
37536         }
37537     },
37538
37539     setValue : function(v){
37540         
37541         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37542         
37543         this.autoSize();
37544     },
37545
37546     /**
37547      * Validates a value according to the field's validation rules and marks the field as invalid
37548      * if the validation fails
37549      * @param {Mixed} value The value to validate
37550      * @return {Boolean} True if the value is valid, else false
37551      */
37552     validateValue : function(value){
37553         if(value.length < 1)  { // if it's blank
37554              if(this.allowBlank){
37555                 this.clearInvalid();
37556                 return true;
37557              }else{
37558                 this.markInvalid(this.blankText);
37559                 return false;
37560              }
37561         }
37562         if(value.length < this.minLength){
37563             this.markInvalid(String.format(this.minLengthText, this.minLength));
37564             return false;
37565         }
37566         if(value.length > this.maxLength){
37567             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37568             return false;
37569         }
37570         if(this.vtype){
37571             var vt = Roo.form.VTypes;
37572             if(!vt[this.vtype](value, this)){
37573                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37574                 return false;
37575             }
37576         }
37577         if(typeof this.validator == "function"){
37578             var msg = this.validator(value);
37579             if(msg !== true){
37580                 this.markInvalid(msg);
37581                 return false;
37582             }
37583         }
37584         if(this.regex && !this.regex.test(value)){
37585             this.markInvalid(this.regexText);
37586             return false;
37587         }
37588         return true;
37589     },
37590
37591     /**
37592      * Selects text in this field
37593      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37594      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37595      */
37596     selectText : function(start, end){
37597         var v = this.getRawValue();
37598         if(v.length > 0){
37599             start = start === undefined ? 0 : start;
37600             end = end === undefined ? v.length : end;
37601             var d = this.el.dom;
37602             if(d.setSelectionRange){
37603                 d.setSelectionRange(start, end);
37604             }else if(d.createTextRange){
37605                 var range = d.createTextRange();
37606                 range.moveStart("character", start);
37607                 range.moveEnd("character", v.length-end);
37608                 range.select();
37609             }
37610         }
37611     },
37612
37613     /**
37614      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37615      * This only takes effect if grow = true, and fires the autosize event.
37616      */
37617     autoSize : function(){
37618         if(!this.grow || !this.rendered){
37619             return;
37620         }
37621         if(!this.metrics){
37622             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37623         }
37624         var el = this.el;
37625         var v = el.dom.value;
37626         var d = document.createElement('div');
37627         d.appendChild(document.createTextNode(v));
37628         v = d.innerHTML;
37629         d = null;
37630         v += "&#160;";
37631         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37632         this.el.setWidth(w);
37633         this.fireEvent("autosize", this, w);
37634     },
37635     
37636     // private
37637     SafariOnKeyDown : function(event)
37638     {
37639         // this is a workaround for a password hang bug on chrome/ webkit.
37640         
37641         var isSelectAll = false;
37642         
37643         if(this.el.dom.selectionEnd > 0){
37644             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37645         }
37646         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37647             event.preventDefault();
37648             this.setValue('');
37649             return;
37650         }
37651         
37652         if(isSelectAll){ // backspace and delete key
37653             
37654             event.preventDefault();
37655             // this is very hacky as keydown always get's upper case.
37656             //
37657             var cc = String.fromCharCode(event.getCharCode());
37658             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37659             
37660         }
37661         
37662         
37663     }
37664 });/*
37665  * Based on:
37666  * Ext JS Library 1.1.1
37667  * Copyright(c) 2006-2007, Ext JS, LLC.
37668  *
37669  * Originally Released Under LGPL - original licence link has changed is not relivant.
37670  *
37671  * Fork - LGPL
37672  * <script type="text/javascript">
37673  */
37674  
37675 /**
37676  * @class Roo.form.Hidden
37677  * @extends Roo.form.TextField
37678  * Simple Hidden element used on forms 
37679  * 
37680  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37681  * 
37682  * @constructor
37683  * Creates a new Hidden form element.
37684  * @param {Object} config Configuration options
37685  */
37686
37687
37688
37689 // easy hidden field...
37690 Roo.form.Hidden = function(config){
37691     Roo.form.Hidden.superclass.constructor.call(this, config);
37692 };
37693   
37694 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37695     fieldLabel:      '',
37696     inputType:      'hidden',
37697     width:          50,
37698     allowBlank:     true,
37699     labelSeparator: '',
37700     hidden:         true,
37701     itemCls :       'x-form-item-display-none'
37702
37703
37704 });
37705
37706
37707 /*
37708  * Based on:
37709  * Ext JS Library 1.1.1
37710  * Copyright(c) 2006-2007, Ext JS, LLC.
37711  *
37712  * Originally Released Under LGPL - original licence link has changed is not relivant.
37713  *
37714  * Fork - LGPL
37715  * <script type="text/javascript">
37716  */
37717  
37718 /**
37719  * @class Roo.form.TriggerField
37720  * @extends Roo.form.TextField
37721  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37722  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37723  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37724  * for which you can provide a custom implementation.  For example:
37725  * <pre><code>
37726 var trigger = new Roo.form.TriggerField();
37727 trigger.onTriggerClick = myTriggerFn;
37728 trigger.applyTo('my-field');
37729 </code></pre>
37730  *
37731  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37732  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37733  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37734  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37735  * @constructor
37736  * Create a new TriggerField.
37737  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37738  * to the base TextField)
37739  */
37740 Roo.form.TriggerField = function(config){
37741     this.mimicing = false;
37742     Roo.form.TriggerField.superclass.constructor.call(this, config);
37743 };
37744
37745 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37746     /**
37747      * @cfg {String} triggerClass A CSS class to apply to the trigger
37748      */
37749     /**
37750      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37751      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37752      */
37753     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37754     /**
37755      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37756      */
37757     hideTrigger:false,
37758
37759     /** @cfg {Boolean} grow @hide */
37760     /** @cfg {Number} growMin @hide */
37761     /** @cfg {Number} growMax @hide */
37762
37763     /**
37764      * @hide 
37765      * @method
37766      */
37767     autoSize: Roo.emptyFn,
37768     // private
37769     monitorTab : true,
37770     // private
37771     deferHeight : true,
37772
37773     
37774     actionMode : 'wrap',
37775     // private
37776     onResize : function(w, h){
37777         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37778         if(typeof w == 'number'){
37779             var x = w - this.trigger.getWidth();
37780             this.el.setWidth(this.adjustWidth('input', x));
37781             this.trigger.setStyle('left', x+'px');
37782         }
37783     },
37784
37785     // private
37786     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37787
37788     // private
37789     getResizeEl : function(){
37790         return this.wrap;
37791     },
37792
37793     // private
37794     getPositionEl : function(){
37795         return this.wrap;
37796     },
37797
37798     // private
37799     alignErrorIcon : function(){
37800         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37801     },
37802
37803     // private
37804     onRender : function(ct, position){
37805         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37806         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37807         this.trigger = this.wrap.createChild(this.triggerConfig ||
37808                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37809         if(this.hideTrigger){
37810             this.trigger.setDisplayed(false);
37811         }
37812         this.initTrigger();
37813         if(!this.width){
37814             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37815         }
37816     },
37817
37818     // private
37819     initTrigger : function(){
37820         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37821         this.trigger.addClassOnOver('x-form-trigger-over');
37822         this.trigger.addClassOnClick('x-form-trigger-click');
37823     },
37824
37825     // private
37826     onDestroy : function(){
37827         if(this.trigger){
37828             this.trigger.removeAllListeners();
37829             this.trigger.remove();
37830         }
37831         if(this.wrap){
37832             this.wrap.remove();
37833         }
37834         Roo.form.TriggerField.superclass.onDestroy.call(this);
37835     },
37836
37837     // private
37838     onFocus : function(){
37839         Roo.form.TriggerField.superclass.onFocus.call(this);
37840         if(!this.mimicing){
37841             this.wrap.addClass('x-trigger-wrap-focus');
37842             this.mimicing = true;
37843             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37844             if(this.monitorTab){
37845                 this.el.on("keydown", this.checkTab, this);
37846             }
37847         }
37848     },
37849
37850     // private
37851     checkTab : function(e){
37852         if(e.getKey() == e.TAB){
37853             this.triggerBlur();
37854         }
37855     },
37856
37857     // private
37858     onBlur : function(){
37859         // do nothing
37860     },
37861
37862     // private
37863     mimicBlur : function(e, t){
37864         if(!this.wrap.contains(t) && this.validateBlur()){
37865             this.triggerBlur();
37866         }
37867     },
37868
37869     // private
37870     triggerBlur : function(){
37871         this.mimicing = false;
37872         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37873         if(this.monitorTab){
37874             this.el.un("keydown", this.checkTab, this);
37875         }
37876         this.wrap.removeClass('x-trigger-wrap-focus');
37877         Roo.form.TriggerField.superclass.onBlur.call(this);
37878     },
37879
37880     // private
37881     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37882     validateBlur : function(e, t){
37883         return true;
37884     },
37885
37886     // private
37887     onDisable : function(){
37888         Roo.form.TriggerField.superclass.onDisable.call(this);
37889         if(this.wrap){
37890             this.wrap.addClass('x-item-disabled');
37891         }
37892     },
37893
37894     // private
37895     onEnable : function(){
37896         Roo.form.TriggerField.superclass.onEnable.call(this);
37897         if(this.wrap){
37898             this.wrap.removeClass('x-item-disabled');
37899         }
37900     },
37901
37902     // private
37903     onShow : function(){
37904         var ae = this.getActionEl();
37905         
37906         if(ae){
37907             ae.dom.style.display = '';
37908             ae.dom.style.visibility = 'visible';
37909         }
37910     },
37911
37912     // private
37913     
37914     onHide : function(){
37915         var ae = this.getActionEl();
37916         ae.dom.style.display = 'none';
37917     },
37918
37919     /**
37920      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37921      * by an implementing function.
37922      * @method
37923      * @param {EventObject} e
37924      */
37925     onTriggerClick : Roo.emptyFn
37926 });
37927
37928 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37929 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37930 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37931 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37932     initComponent : function(){
37933         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37934
37935         this.triggerConfig = {
37936             tag:'span', cls:'x-form-twin-triggers', cn:[
37937             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37938             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37939         ]};
37940     },
37941
37942     getTrigger : function(index){
37943         return this.triggers[index];
37944     },
37945
37946     initTrigger : function(){
37947         var ts = this.trigger.select('.x-form-trigger', true);
37948         this.wrap.setStyle('overflow', 'hidden');
37949         var triggerField = this;
37950         ts.each(function(t, all, index){
37951             t.hide = function(){
37952                 var w = triggerField.wrap.getWidth();
37953                 this.dom.style.display = 'none';
37954                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37955             };
37956             t.show = function(){
37957                 var w = triggerField.wrap.getWidth();
37958                 this.dom.style.display = '';
37959                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37960             };
37961             var triggerIndex = 'Trigger'+(index+1);
37962
37963             if(this['hide'+triggerIndex]){
37964                 t.dom.style.display = 'none';
37965             }
37966             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37967             t.addClassOnOver('x-form-trigger-over');
37968             t.addClassOnClick('x-form-trigger-click');
37969         }, this);
37970         this.triggers = ts.elements;
37971     },
37972
37973     onTrigger1Click : Roo.emptyFn,
37974     onTrigger2Click : Roo.emptyFn
37975 });/*
37976  * Based on:
37977  * Ext JS Library 1.1.1
37978  * Copyright(c) 2006-2007, Ext JS, LLC.
37979  *
37980  * Originally Released Under LGPL - original licence link has changed is not relivant.
37981  *
37982  * Fork - LGPL
37983  * <script type="text/javascript">
37984  */
37985  
37986 /**
37987  * @class Roo.form.TextArea
37988  * @extends Roo.form.TextField
37989  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37990  * support for auto-sizing.
37991  * @constructor
37992  * Creates a new TextArea
37993  * @param {Object} config Configuration options
37994  */
37995 Roo.form.TextArea = function(config){
37996     Roo.form.TextArea.superclass.constructor.call(this, config);
37997     // these are provided exchanges for backwards compat
37998     // minHeight/maxHeight were replaced by growMin/growMax to be
37999     // compatible with TextField growing config values
38000     if(this.minHeight !== undefined){
38001         this.growMin = this.minHeight;
38002     }
38003     if(this.maxHeight !== undefined){
38004         this.growMax = this.maxHeight;
38005     }
38006 };
38007
38008 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38009     /**
38010      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38011      */
38012     growMin : 60,
38013     /**
38014      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38015      */
38016     growMax: 1000,
38017     /**
38018      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38019      * in the field (equivalent to setting overflow: hidden, defaults to false)
38020      */
38021     preventScrollbars: false,
38022     /**
38023      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38024      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38025      */
38026
38027     // private
38028     onRender : function(ct, position){
38029         if(!this.el){
38030             this.defaultAutoCreate = {
38031                 tag: "textarea",
38032                 style:"width:300px;height:60px;",
38033                 autocomplete: "off"
38034             };
38035         }
38036         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38037         if(this.grow){
38038             this.textSizeEl = Roo.DomHelper.append(document.body, {
38039                 tag: "pre", cls: "x-form-grow-sizer"
38040             });
38041             if(this.preventScrollbars){
38042                 this.el.setStyle("overflow", "hidden");
38043             }
38044             this.el.setHeight(this.growMin);
38045         }
38046     },
38047
38048     onDestroy : function(){
38049         if(this.textSizeEl){
38050             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38051         }
38052         Roo.form.TextArea.superclass.onDestroy.call(this);
38053     },
38054
38055     // private
38056     onKeyUp : function(e){
38057         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38058             this.autoSize();
38059         }
38060     },
38061
38062     /**
38063      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38064      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38065      */
38066     autoSize : function(){
38067         if(!this.grow || !this.textSizeEl){
38068             return;
38069         }
38070         var el = this.el;
38071         var v = el.dom.value;
38072         var ts = this.textSizeEl;
38073
38074         ts.innerHTML = '';
38075         ts.appendChild(document.createTextNode(v));
38076         v = ts.innerHTML;
38077
38078         Roo.fly(ts).setWidth(this.el.getWidth());
38079         if(v.length < 1){
38080             v = "&#160;&#160;";
38081         }else{
38082             if(Roo.isIE){
38083                 v = v.replace(/\n/g, '<p>&#160;</p>');
38084             }
38085             v += "&#160;\n&#160;";
38086         }
38087         ts.innerHTML = v;
38088         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38089         if(h != this.lastHeight){
38090             this.lastHeight = h;
38091             this.el.setHeight(h);
38092             this.fireEvent("autosize", this, h);
38093         }
38094     }
38095 });/*
38096  * Based on:
38097  * Ext JS Library 1.1.1
38098  * Copyright(c) 2006-2007, Ext JS, LLC.
38099  *
38100  * Originally Released Under LGPL - original licence link has changed is not relivant.
38101  *
38102  * Fork - LGPL
38103  * <script type="text/javascript">
38104  */
38105  
38106
38107 /**
38108  * @class Roo.form.NumberField
38109  * @extends Roo.form.TextField
38110  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38111  * @constructor
38112  * Creates a new NumberField
38113  * @param {Object} config Configuration options
38114  */
38115 Roo.form.NumberField = function(config){
38116     Roo.form.NumberField.superclass.constructor.call(this, config);
38117 };
38118
38119 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38120     /**
38121      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38122      */
38123     fieldClass: "x-form-field x-form-num-field",
38124     /**
38125      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38126      */
38127     allowDecimals : true,
38128     /**
38129      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38130      */
38131     decimalSeparator : ".",
38132     /**
38133      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38134      */
38135     decimalPrecision : 2,
38136     /**
38137      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38138      */
38139     allowNegative : true,
38140     /**
38141      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38142      */
38143     minValue : Number.NEGATIVE_INFINITY,
38144     /**
38145      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38146      */
38147     maxValue : Number.MAX_VALUE,
38148     /**
38149      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38150      */
38151     minText : "The minimum value for this field is {0}",
38152     /**
38153      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38154      */
38155     maxText : "The maximum value for this field is {0}",
38156     /**
38157      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38158      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38159      */
38160     nanText : "{0} is not a valid number",
38161
38162     // private
38163     initEvents : function(){
38164         Roo.form.NumberField.superclass.initEvents.call(this);
38165         var allowed = "0123456789";
38166         if(this.allowDecimals){
38167             allowed += this.decimalSeparator;
38168         }
38169         if(this.allowNegative){
38170             allowed += "-";
38171         }
38172         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38173         var keyPress = function(e){
38174             var k = e.getKey();
38175             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38176                 return;
38177             }
38178             var c = e.getCharCode();
38179             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38180                 e.stopEvent();
38181             }
38182         };
38183         this.el.on("keypress", keyPress, this);
38184     },
38185
38186     // private
38187     validateValue : function(value){
38188         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38189             return false;
38190         }
38191         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38192              return true;
38193         }
38194         var num = this.parseValue(value);
38195         if(isNaN(num)){
38196             this.markInvalid(String.format(this.nanText, value));
38197             return false;
38198         }
38199         if(num < this.minValue){
38200             this.markInvalid(String.format(this.minText, this.minValue));
38201             return false;
38202         }
38203         if(num > this.maxValue){
38204             this.markInvalid(String.format(this.maxText, this.maxValue));
38205             return false;
38206         }
38207         return true;
38208     },
38209
38210     getValue : function(){
38211         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38212     },
38213
38214     // private
38215     parseValue : function(value){
38216         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38217         return isNaN(value) ? '' : value;
38218     },
38219
38220     // private
38221     fixPrecision : function(value){
38222         var nan = isNaN(value);
38223         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38224             return nan ? '' : value;
38225         }
38226         return parseFloat(value).toFixed(this.decimalPrecision);
38227     },
38228
38229     setValue : function(v){
38230         v = this.fixPrecision(v);
38231         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38232     },
38233
38234     // private
38235     decimalPrecisionFcn : function(v){
38236         return Math.floor(v);
38237     },
38238
38239     beforeBlur : function(){
38240         var v = this.parseValue(this.getRawValue());
38241         if(v){
38242             this.setValue(v);
38243         }
38244     }
38245 });/*
38246  * Based on:
38247  * Ext JS Library 1.1.1
38248  * Copyright(c) 2006-2007, Ext JS, LLC.
38249  *
38250  * Originally Released Under LGPL - original licence link has changed is not relivant.
38251  *
38252  * Fork - LGPL
38253  * <script type="text/javascript">
38254  */
38255  
38256 /**
38257  * @class Roo.form.DateField
38258  * @extends Roo.form.TriggerField
38259  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38260 * @constructor
38261 * Create a new DateField
38262 * @param {Object} config
38263  */
38264 Roo.form.DateField = function(config){
38265     Roo.form.DateField.superclass.constructor.call(this, config);
38266     
38267       this.addEvents({
38268          
38269         /**
38270          * @event select
38271          * Fires when a date is selected
38272              * @param {Roo.form.DateField} combo This combo box
38273              * @param {Date} date The date selected
38274              */
38275         'select' : true
38276          
38277     });
38278     
38279     
38280     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38281     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38282     this.ddMatch = null;
38283     if(this.disabledDates){
38284         var dd = this.disabledDates;
38285         var re = "(?:";
38286         for(var i = 0; i < dd.length; i++){
38287             re += dd[i];
38288             if(i != dd.length-1) re += "|";
38289         }
38290         this.ddMatch = new RegExp(re + ")");
38291     }
38292 };
38293
38294 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38295     /**
38296      * @cfg {String} format
38297      * The default date format string which can be overriden for localization support.  The format must be
38298      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38299      */
38300     format : "m/d/y",
38301     /**
38302      * @cfg {String} altFormats
38303      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38304      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38305      */
38306     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38307     /**
38308      * @cfg {Array} disabledDays
38309      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38310      */
38311     disabledDays : null,
38312     /**
38313      * @cfg {String} disabledDaysText
38314      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38315      */
38316     disabledDaysText : "Disabled",
38317     /**
38318      * @cfg {Array} disabledDates
38319      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38320      * expression so they are very powerful. Some examples:
38321      * <ul>
38322      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38323      * <li>["03/08", "09/16"] would disable those days for every year</li>
38324      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38325      * <li>["03/../2006"] would disable every day in March 2006</li>
38326      * <li>["^03"] would disable every day in every March</li>
38327      * </ul>
38328      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38329      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38330      */
38331     disabledDates : null,
38332     /**
38333      * @cfg {String} disabledDatesText
38334      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38335      */
38336     disabledDatesText : "Disabled",
38337     /**
38338      * @cfg {Date/String} minValue
38339      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38340      * valid format (defaults to null).
38341      */
38342     minValue : null,
38343     /**
38344      * @cfg {Date/String} maxValue
38345      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38346      * valid format (defaults to null).
38347      */
38348     maxValue : null,
38349     /**
38350      * @cfg {String} minText
38351      * The error text to display when the date in the cell is before minValue (defaults to
38352      * 'The date in this field must be after {minValue}').
38353      */
38354     minText : "The date in this field must be equal to or after {0}",
38355     /**
38356      * @cfg {String} maxText
38357      * The error text to display when the date in the cell is after maxValue (defaults to
38358      * 'The date in this field must be before {maxValue}').
38359      */
38360     maxText : "The date in this field must be equal to or before {0}",
38361     /**
38362      * @cfg {String} invalidText
38363      * The error text to display when the date in the field is invalid (defaults to
38364      * '{value} is not a valid date - it must be in the format {format}').
38365      */
38366     invalidText : "{0} is not a valid date - it must be in the format {1}",
38367     /**
38368      * @cfg {String} triggerClass
38369      * An additional CSS class used to style the trigger button.  The trigger will always get the
38370      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38371      * which displays a calendar icon).
38372      */
38373     triggerClass : 'x-form-date-trigger',
38374     
38375
38376     /**
38377      * @cfg {Boolean} useIso
38378      * if enabled, then the date field will use a hidden field to store the 
38379      * real value as iso formated date. default (false)
38380      */ 
38381     useIso : false,
38382     /**
38383      * @cfg {String/Object} autoCreate
38384      * A DomHelper element spec, or true for a default element spec (defaults to
38385      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38386      */ 
38387     // private
38388     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38389     
38390     // private
38391     hiddenField: false,
38392     
38393     onRender : function(ct, position)
38394     {
38395         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38396         if (this.useIso) {
38397             //this.el.dom.removeAttribute('name'); 
38398             Roo.log("Changing name?");
38399             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38400             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38401                     'before', true);
38402             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38403             // prevent input submission
38404             this.hiddenName = this.name;
38405         }
38406             
38407             
38408     },
38409     
38410     // private
38411     validateValue : function(value)
38412     {
38413         value = this.formatDate(value);
38414         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38415             Roo.log('super failed');
38416             return false;
38417         }
38418         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38419              return true;
38420         }
38421         var svalue = value;
38422         value = this.parseDate(value);
38423         if(!value){
38424             Roo.log('parse date failed' + svalue);
38425             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38426             return false;
38427         }
38428         var time = value.getTime();
38429         if(this.minValue && time < this.minValue.getTime()){
38430             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38431             return false;
38432         }
38433         if(this.maxValue && time > this.maxValue.getTime()){
38434             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38435             return false;
38436         }
38437         if(this.disabledDays){
38438             var day = value.getDay();
38439             for(var i = 0; i < this.disabledDays.length; i++) {
38440                 if(day === this.disabledDays[i]){
38441                     this.markInvalid(this.disabledDaysText);
38442                     return false;
38443                 }
38444             }
38445         }
38446         var fvalue = this.formatDate(value);
38447         if(this.ddMatch && this.ddMatch.test(fvalue)){
38448             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38449             return false;
38450         }
38451         return true;
38452     },
38453
38454     // private
38455     // Provides logic to override the default TriggerField.validateBlur which just returns true
38456     validateBlur : function(){
38457         return !this.menu || !this.menu.isVisible();
38458     },
38459     
38460     getName: function()
38461     {
38462         // returns hidden if it's set..
38463         if (!this.rendered) {return ''};
38464         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38465         
38466     },
38467
38468     /**
38469      * Returns the current date value of the date field.
38470      * @return {Date} The date value
38471      */
38472     getValue : function(){
38473         
38474         return  this.hiddenField ?
38475                 this.hiddenField.value :
38476                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38477     },
38478
38479     /**
38480      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38481      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38482      * (the default format used is "m/d/y").
38483      * <br />Usage:
38484      * <pre><code>
38485 //All of these calls set the same date value (May 4, 2006)
38486
38487 //Pass a date object:
38488 var dt = new Date('5/4/06');
38489 dateField.setValue(dt);
38490
38491 //Pass a date string (default format):
38492 dateField.setValue('5/4/06');
38493
38494 //Pass a date string (custom format):
38495 dateField.format = 'Y-m-d';
38496 dateField.setValue('2006-5-4');
38497 </code></pre>
38498      * @param {String/Date} date The date or valid date string
38499      */
38500     setValue : function(date){
38501         if (this.hiddenField) {
38502             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38503         }
38504         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38505         // make sure the value field is always stored as a date..
38506         this.value = this.parseDate(date);
38507         
38508         
38509     },
38510
38511     // private
38512     parseDate : function(value){
38513         if(!value || value instanceof Date){
38514             return value;
38515         }
38516         var v = Date.parseDate(value, this.format);
38517          if (!v && this.useIso) {
38518             v = Date.parseDate(value, 'Y-m-d');
38519         }
38520         if(!v && this.altFormats){
38521             if(!this.altFormatsArray){
38522                 this.altFormatsArray = this.altFormats.split("|");
38523             }
38524             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38525                 v = Date.parseDate(value, this.altFormatsArray[i]);
38526             }
38527         }
38528         return v;
38529     },
38530
38531     // private
38532     formatDate : function(date, fmt){
38533         return (!date || !(date instanceof Date)) ?
38534                date : date.dateFormat(fmt || this.format);
38535     },
38536
38537     // private
38538     menuListeners : {
38539         select: function(m, d){
38540             
38541             this.setValue(d);
38542             this.fireEvent('select', this, d);
38543         },
38544         show : function(){ // retain focus styling
38545             this.onFocus();
38546         },
38547         hide : function(){
38548             this.focus.defer(10, this);
38549             var ml = this.menuListeners;
38550             this.menu.un("select", ml.select,  this);
38551             this.menu.un("show", ml.show,  this);
38552             this.menu.un("hide", ml.hide,  this);
38553         }
38554     },
38555
38556     // private
38557     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38558     onTriggerClick : function(){
38559         if(this.disabled){
38560             return;
38561         }
38562         if(this.menu == null){
38563             this.menu = new Roo.menu.DateMenu();
38564         }
38565         Roo.apply(this.menu.picker,  {
38566             showClear: this.allowBlank,
38567             minDate : this.minValue,
38568             maxDate : this.maxValue,
38569             disabledDatesRE : this.ddMatch,
38570             disabledDatesText : this.disabledDatesText,
38571             disabledDays : this.disabledDays,
38572             disabledDaysText : this.disabledDaysText,
38573             format : this.useIso ? 'Y-m-d' : this.format,
38574             minText : String.format(this.minText, this.formatDate(this.minValue)),
38575             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38576         });
38577         this.menu.on(Roo.apply({}, this.menuListeners, {
38578             scope:this
38579         }));
38580         this.menu.picker.setValue(this.getValue() || new Date());
38581         this.menu.show(this.el, "tl-bl?");
38582     },
38583
38584     beforeBlur : function(){
38585         var v = this.parseDate(this.getRawValue());
38586         if(v){
38587             this.setValue(v);
38588         }
38589     },
38590
38591     /*@
38592      * overide
38593      * 
38594      */
38595     isDirty : function() {
38596         if(this.disabled) {
38597             return false;
38598         }
38599         
38600         if(typeof(this.startValue) === 'undefined'){
38601             return false;
38602         }
38603         
38604         return String(this.getValue()) !== String(this.startValue);
38605         
38606     }
38607 });/*
38608  * Based on:
38609  * Ext JS Library 1.1.1
38610  * Copyright(c) 2006-2007, Ext JS, LLC.
38611  *
38612  * Originally Released Under LGPL - original licence link has changed is not relivant.
38613  *
38614  * Fork - LGPL
38615  * <script type="text/javascript">
38616  */
38617  
38618 /**
38619  * @class Roo.form.MonthField
38620  * @extends Roo.form.TriggerField
38621  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38622 * @constructor
38623 * Create a new MonthField
38624 * @param {Object} config
38625  */
38626 Roo.form.MonthField = function(config){
38627     
38628     Roo.form.MonthField.superclass.constructor.call(this, config);
38629     
38630       this.addEvents({
38631          
38632         /**
38633          * @event select
38634          * Fires when a date is selected
38635              * @param {Roo.form.MonthFieeld} combo This combo box
38636              * @param {Date} date The date selected
38637              */
38638         'select' : true
38639          
38640     });
38641     
38642     
38643     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38644     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38645     this.ddMatch = null;
38646     if(this.disabledDates){
38647         var dd = this.disabledDates;
38648         var re = "(?:";
38649         for(var i = 0; i < dd.length; i++){
38650             re += dd[i];
38651             if(i != dd.length-1) re += "|";
38652         }
38653         this.ddMatch = new RegExp(re + ")");
38654     }
38655 };
38656
38657 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38658     /**
38659      * @cfg {String} format
38660      * The default date format string which can be overriden for localization support.  The format must be
38661      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38662      */
38663     format : "M Y",
38664     /**
38665      * @cfg {String} altFormats
38666      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38667      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38668      */
38669     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38670     /**
38671      * @cfg {Array} disabledDays
38672      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38673      */
38674     disabledDays : [0,1,2,3,4,5,6],
38675     /**
38676      * @cfg {String} disabledDaysText
38677      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38678      */
38679     disabledDaysText : "Disabled",
38680     /**
38681      * @cfg {Array} disabledDates
38682      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38683      * expression so they are very powerful. Some examples:
38684      * <ul>
38685      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38686      * <li>["03/08", "09/16"] would disable those days for every year</li>
38687      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38688      * <li>["03/../2006"] would disable every day in March 2006</li>
38689      * <li>["^03"] would disable every day in every March</li>
38690      * </ul>
38691      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38692      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38693      */
38694     disabledDates : null,
38695     /**
38696      * @cfg {String} disabledDatesText
38697      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38698      */
38699     disabledDatesText : "Disabled",
38700     /**
38701      * @cfg {Date/String} minValue
38702      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38703      * valid format (defaults to null).
38704      */
38705     minValue : null,
38706     /**
38707      * @cfg {Date/String} maxValue
38708      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38709      * valid format (defaults to null).
38710      */
38711     maxValue : null,
38712     /**
38713      * @cfg {String} minText
38714      * The error text to display when the date in the cell is before minValue (defaults to
38715      * 'The date in this field must be after {minValue}').
38716      */
38717     minText : "The date in this field must be equal to or after {0}",
38718     /**
38719      * @cfg {String} maxTextf
38720      * The error text to display when the date in the cell is after maxValue (defaults to
38721      * 'The date in this field must be before {maxValue}').
38722      */
38723     maxText : "The date in this field must be equal to or before {0}",
38724     /**
38725      * @cfg {String} invalidText
38726      * The error text to display when the date in the field is invalid (defaults to
38727      * '{value} is not a valid date - it must be in the format {format}').
38728      */
38729     invalidText : "{0} is not a valid date - it must be in the format {1}",
38730     /**
38731      * @cfg {String} triggerClass
38732      * An additional CSS class used to style the trigger button.  The trigger will always get the
38733      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38734      * which displays a calendar icon).
38735      */
38736     triggerClass : 'x-form-date-trigger',
38737     
38738
38739     /**
38740      * @cfg {Boolean} useIso
38741      * if enabled, then the date field will use a hidden field to store the 
38742      * real value as iso formated date. default (true)
38743      */ 
38744     useIso : true,
38745     /**
38746      * @cfg {String/Object} autoCreate
38747      * A DomHelper element spec, or true for a default element spec (defaults to
38748      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38749      */ 
38750     // private
38751     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38752     
38753     // private
38754     hiddenField: false,
38755     
38756     hideMonthPicker : false,
38757     
38758     onRender : function(ct, position)
38759     {
38760         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38761         if (this.useIso) {
38762             this.el.dom.removeAttribute('name'); 
38763             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38764                     'before', true);
38765             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38766             // prevent input submission
38767             this.hiddenName = this.name;
38768         }
38769             
38770             
38771     },
38772     
38773     // private
38774     validateValue : function(value)
38775     {
38776         value = this.formatDate(value);
38777         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38778             return false;
38779         }
38780         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38781              return true;
38782         }
38783         var svalue = value;
38784         value = this.parseDate(value);
38785         if(!value){
38786             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38787             return false;
38788         }
38789         var time = value.getTime();
38790         if(this.minValue && time < this.minValue.getTime()){
38791             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38792             return false;
38793         }
38794         if(this.maxValue && time > this.maxValue.getTime()){
38795             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38796             return false;
38797         }
38798         /*if(this.disabledDays){
38799             var day = value.getDay();
38800             for(var i = 0; i < this.disabledDays.length; i++) {
38801                 if(day === this.disabledDays[i]){
38802                     this.markInvalid(this.disabledDaysText);
38803                     return false;
38804                 }
38805             }
38806         }
38807         */
38808         var fvalue = this.formatDate(value);
38809         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38810             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38811             return false;
38812         }
38813         */
38814         return true;
38815     },
38816
38817     // private
38818     // Provides logic to override the default TriggerField.validateBlur which just returns true
38819     validateBlur : function(){
38820         return !this.menu || !this.menu.isVisible();
38821     },
38822
38823     /**
38824      * Returns the current date value of the date field.
38825      * @return {Date} The date value
38826      */
38827     getValue : function(){
38828         
38829         
38830         
38831         return  this.hiddenField ?
38832                 this.hiddenField.value :
38833                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38834     },
38835
38836     /**
38837      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38838      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38839      * (the default format used is "m/d/y").
38840      * <br />Usage:
38841      * <pre><code>
38842 //All of these calls set the same date value (May 4, 2006)
38843
38844 //Pass a date object:
38845 var dt = new Date('5/4/06');
38846 monthField.setValue(dt);
38847
38848 //Pass a date string (default format):
38849 monthField.setValue('5/4/06');
38850
38851 //Pass a date string (custom format):
38852 monthField.format = 'Y-m-d';
38853 monthField.setValue('2006-5-4');
38854 </code></pre>
38855      * @param {String/Date} date The date or valid date string
38856      */
38857     setValue : function(date){
38858         Roo.log('month setValue' + date);
38859         // can only be first of month..
38860         
38861         var val = this.parseDate(date);
38862         
38863         if (this.hiddenField) {
38864             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38865         }
38866         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38867         this.value = this.parseDate(date);
38868     },
38869
38870     // private
38871     parseDate : function(value){
38872         if(!value || value instanceof Date){
38873             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38874             return value;
38875         }
38876         var v = Date.parseDate(value, this.format);
38877         if (!v && this.useIso) {
38878             v = Date.parseDate(value, 'Y-m-d');
38879         }
38880         if (v) {
38881             // 
38882             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38883         }
38884         
38885         
38886         if(!v && this.altFormats){
38887             if(!this.altFormatsArray){
38888                 this.altFormatsArray = this.altFormats.split("|");
38889             }
38890             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38891                 v = Date.parseDate(value, this.altFormatsArray[i]);
38892             }
38893         }
38894         return v;
38895     },
38896
38897     // private
38898     formatDate : function(date, fmt){
38899         return (!date || !(date instanceof Date)) ?
38900                date : date.dateFormat(fmt || this.format);
38901     },
38902
38903     // private
38904     menuListeners : {
38905         select: function(m, d){
38906             this.setValue(d);
38907             this.fireEvent('select', this, d);
38908         },
38909         show : function(){ // retain focus styling
38910             this.onFocus();
38911         },
38912         hide : function(){
38913             this.focus.defer(10, this);
38914             var ml = this.menuListeners;
38915             this.menu.un("select", ml.select,  this);
38916             this.menu.un("show", ml.show,  this);
38917             this.menu.un("hide", ml.hide,  this);
38918         }
38919     },
38920     // private
38921     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38922     onTriggerClick : function(){
38923         if(this.disabled){
38924             return;
38925         }
38926         if(this.menu == null){
38927             this.menu = new Roo.menu.DateMenu();
38928            
38929         }
38930         
38931         Roo.apply(this.menu.picker,  {
38932             
38933             showClear: this.allowBlank,
38934             minDate : this.minValue,
38935             maxDate : this.maxValue,
38936             disabledDatesRE : this.ddMatch,
38937             disabledDatesText : this.disabledDatesText,
38938             
38939             format : this.useIso ? 'Y-m-d' : this.format,
38940             minText : String.format(this.minText, this.formatDate(this.minValue)),
38941             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38942             
38943         });
38944          this.menu.on(Roo.apply({}, this.menuListeners, {
38945             scope:this
38946         }));
38947        
38948         
38949         var m = this.menu;
38950         var p = m.picker;
38951         
38952         // hide month picker get's called when we called by 'before hide';
38953         
38954         var ignorehide = true;
38955         p.hideMonthPicker  = function(disableAnim){
38956             if (ignorehide) {
38957                 return;
38958             }
38959              if(this.monthPicker){
38960                 Roo.log("hideMonthPicker called");
38961                 if(disableAnim === true){
38962                     this.monthPicker.hide();
38963                 }else{
38964                     this.monthPicker.slideOut('t', {duration:.2});
38965                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38966                     p.fireEvent("select", this, this.value);
38967                     m.hide();
38968                 }
38969             }
38970         }
38971         
38972         Roo.log('picker set value');
38973         Roo.log(this.getValue());
38974         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38975         m.show(this.el, 'tl-bl?');
38976         ignorehide  = false;
38977         // this will trigger hideMonthPicker..
38978         
38979         
38980         // hidden the day picker
38981         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38982         
38983         
38984         
38985       
38986         
38987         p.showMonthPicker.defer(100, p);
38988     
38989         
38990        
38991     },
38992
38993     beforeBlur : function(){
38994         var v = this.parseDate(this.getRawValue());
38995         if(v){
38996             this.setValue(v);
38997         }
38998     }
38999
39000     /** @cfg {Boolean} grow @hide */
39001     /** @cfg {Number} growMin @hide */
39002     /** @cfg {Number} growMax @hide */
39003     /**
39004      * @hide
39005      * @method autoSize
39006      */
39007 });/*
39008  * Based on:
39009  * Ext JS Library 1.1.1
39010  * Copyright(c) 2006-2007, Ext JS, LLC.
39011  *
39012  * Originally Released Under LGPL - original licence link has changed is not relivant.
39013  *
39014  * Fork - LGPL
39015  * <script type="text/javascript">
39016  */
39017  
39018
39019 /**
39020  * @class Roo.form.ComboBox
39021  * @extends Roo.form.TriggerField
39022  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39023  * @constructor
39024  * Create a new ComboBox.
39025  * @param {Object} config Configuration options
39026  */
39027 Roo.form.ComboBox = function(config){
39028     Roo.form.ComboBox.superclass.constructor.call(this, config);
39029     this.addEvents({
39030         /**
39031          * @event expand
39032          * Fires when the dropdown list is expanded
39033              * @param {Roo.form.ComboBox} combo This combo box
39034              */
39035         'expand' : true,
39036         /**
39037          * @event collapse
39038          * Fires when the dropdown list is collapsed
39039              * @param {Roo.form.ComboBox} combo This combo box
39040              */
39041         'collapse' : true,
39042         /**
39043          * @event beforeselect
39044          * Fires before a list item is selected. Return false to cancel the selection.
39045              * @param {Roo.form.ComboBox} combo This combo box
39046              * @param {Roo.data.Record} record The data record returned from the underlying store
39047              * @param {Number} index The index of the selected item in the dropdown list
39048              */
39049         'beforeselect' : true,
39050         /**
39051          * @event select
39052          * Fires when a list item is selected
39053              * @param {Roo.form.ComboBox} combo This combo box
39054              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39055              * @param {Number} index The index of the selected item in the dropdown list
39056              */
39057         'select' : true,
39058         /**
39059          * @event beforequery
39060          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39061          * The event object passed has these properties:
39062              * @param {Roo.form.ComboBox} combo This combo box
39063              * @param {String} query The query
39064              * @param {Boolean} forceAll true to force "all" query
39065              * @param {Boolean} cancel true to cancel the query
39066              * @param {Object} e The query event object
39067              */
39068         'beforequery': true,
39069          /**
39070          * @event add
39071          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39072              * @param {Roo.form.ComboBox} combo This combo box
39073              */
39074         'add' : true,
39075         /**
39076          * @event edit
39077          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39078              * @param {Roo.form.ComboBox} combo This combo box
39079              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39080              */
39081         'edit' : true
39082         
39083         
39084     });
39085     if(this.transform){
39086         this.allowDomMove = false;
39087         var s = Roo.getDom(this.transform);
39088         if(!this.hiddenName){
39089             this.hiddenName = s.name;
39090         }
39091         if(!this.store){
39092             this.mode = 'local';
39093             var d = [], opts = s.options;
39094             for(var i = 0, len = opts.length;i < len; i++){
39095                 var o = opts[i];
39096                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39097                 if(o.selected) {
39098                     this.value = value;
39099                 }
39100                 d.push([value, o.text]);
39101             }
39102             this.store = new Roo.data.SimpleStore({
39103                 'id': 0,
39104                 fields: ['value', 'text'],
39105                 data : d
39106             });
39107             this.valueField = 'value';
39108             this.displayField = 'text';
39109         }
39110         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39111         if(!this.lazyRender){
39112             this.target = true;
39113             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39114             s.parentNode.removeChild(s); // remove it
39115             this.render(this.el.parentNode);
39116         }else{
39117             s.parentNode.removeChild(s); // remove it
39118         }
39119
39120     }
39121     if (this.store) {
39122         this.store = Roo.factory(this.store, Roo.data);
39123     }
39124     
39125     this.selectedIndex = -1;
39126     if(this.mode == 'local'){
39127         if(config.queryDelay === undefined){
39128             this.queryDelay = 10;
39129         }
39130         if(config.minChars === undefined){
39131             this.minChars = 0;
39132         }
39133     }
39134 };
39135
39136 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39137     /**
39138      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39139      */
39140     /**
39141      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39142      * rendering into an Roo.Editor, defaults to false)
39143      */
39144     /**
39145      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39146      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39147      */
39148     /**
39149      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39150      */
39151     /**
39152      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39153      * the dropdown list (defaults to undefined, with no header element)
39154      */
39155
39156      /**
39157      * @cfg {String/Roo.Template} tpl The template to use to render the output
39158      */
39159      
39160     // private
39161     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39162     /**
39163      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39164      */
39165     listWidth: undefined,
39166     /**
39167      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39168      * mode = 'remote' or 'text' if mode = 'local')
39169      */
39170     displayField: undefined,
39171     /**
39172      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39173      * mode = 'remote' or 'value' if mode = 'local'). 
39174      * Note: use of a valueField requires the user make a selection
39175      * in order for a value to be mapped.
39176      */
39177     valueField: undefined,
39178     
39179     
39180     /**
39181      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39182      * field's data value (defaults to the underlying DOM element's name)
39183      */
39184     hiddenName: undefined,
39185     /**
39186      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39187      */
39188     listClass: '',
39189     /**
39190      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39191      */
39192     selectedClass: 'x-combo-selected',
39193     /**
39194      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39195      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39196      * which displays a downward arrow icon).
39197      */
39198     triggerClass : 'x-form-arrow-trigger',
39199     /**
39200      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39201      */
39202     shadow:'sides',
39203     /**
39204      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39205      * anchor positions (defaults to 'tl-bl')
39206      */
39207     listAlign: 'tl-bl?',
39208     /**
39209      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39210      */
39211     maxHeight: 300,
39212     /**
39213      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39214      * query specified by the allQuery config option (defaults to 'query')
39215      */
39216     triggerAction: 'query',
39217     /**
39218      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39219      * (defaults to 4, does not apply if editable = false)
39220      */
39221     minChars : 4,
39222     /**
39223      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39224      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39225      */
39226     typeAhead: false,
39227     /**
39228      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39229      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39230      */
39231     queryDelay: 500,
39232     /**
39233      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39234      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39235      */
39236     pageSize: 0,
39237     /**
39238      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39239      * when editable = true (defaults to false)
39240      */
39241     selectOnFocus:false,
39242     /**
39243      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39244      */
39245     queryParam: 'query',
39246     /**
39247      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39248      * when mode = 'remote' (defaults to 'Loading...')
39249      */
39250     loadingText: 'Loading...',
39251     /**
39252      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39253      */
39254     resizable: false,
39255     /**
39256      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39257      */
39258     handleHeight : 8,
39259     /**
39260      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39261      * traditional select (defaults to true)
39262      */
39263     editable: true,
39264     /**
39265      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39266      */
39267     allQuery: '',
39268     /**
39269      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39270      */
39271     mode: 'remote',
39272     /**
39273      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39274      * listWidth has a higher value)
39275      */
39276     minListWidth : 70,
39277     /**
39278      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39279      * allow the user to set arbitrary text into the field (defaults to false)
39280      */
39281     forceSelection:false,
39282     /**
39283      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39284      * if typeAhead = true (defaults to 250)
39285      */
39286     typeAheadDelay : 250,
39287     /**
39288      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39289      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39290      */
39291     valueNotFoundText : undefined,
39292     /**
39293      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39294      */
39295     blockFocus : false,
39296     
39297     /**
39298      * @cfg {Boolean} disableClear Disable showing of clear button.
39299      */
39300     disableClear : false,
39301     /**
39302      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39303      */
39304     alwaysQuery : false,
39305     
39306     //private
39307     addicon : false,
39308     editicon: false,
39309     
39310     // element that contains real text value.. (when hidden is used..)
39311      
39312     // private
39313     onRender : function(ct, position){
39314         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39315         if(this.hiddenName){
39316             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39317                     'before', true);
39318             this.hiddenField.value =
39319                 this.hiddenValue !== undefined ? this.hiddenValue :
39320                 this.value !== undefined ? this.value : '';
39321
39322             // prevent input submission
39323             this.el.dom.removeAttribute('name');
39324              
39325              
39326         }
39327         if(Roo.isGecko){
39328             this.el.dom.setAttribute('autocomplete', 'off');
39329         }
39330
39331         var cls = 'x-combo-list';
39332
39333         this.list = new Roo.Layer({
39334             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39335         });
39336
39337         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39338         this.list.setWidth(lw);
39339         this.list.swallowEvent('mousewheel');
39340         this.assetHeight = 0;
39341
39342         if(this.title){
39343             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39344             this.assetHeight += this.header.getHeight();
39345         }
39346
39347         this.innerList = this.list.createChild({cls:cls+'-inner'});
39348         this.innerList.on('mouseover', this.onViewOver, this);
39349         this.innerList.on('mousemove', this.onViewMove, this);
39350         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39351         
39352         if(this.allowBlank && !this.pageSize && !this.disableClear){
39353             this.footer = this.list.createChild({cls:cls+'-ft'});
39354             this.pageTb = new Roo.Toolbar(this.footer);
39355            
39356         }
39357         if(this.pageSize){
39358             this.footer = this.list.createChild({cls:cls+'-ft'});
39359             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39360                     {pageSize: this.pageSize});
39361             
39362         }
39363         
39364         if (this.pageTb && this.allowBlank && !this.disableClear) {
39365             var _this = this;
39366             this.pageTb.add(new Roo.Toolbar.Fill(), {
39367                 cls: 'x-btn-icon x-btn-clear',
39368                 text: '&#160;',
39369                 handler: function()
39370                 {
39371                     _this.collapse();
39372                     _this.clearValue();
39373                     _this.onSelect(false, -1);
39374                 }
39375             });
39376         }
39377         if (this.footer) {
39378             this.assetHeight += this.footer.getHeight();
39379         }
39380         
39381
39382         if(!this.tpl){
39383             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39384         }
39385
39386         this.view = new Roo.View(this.innerList, this.tpl, {
39387             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39388         });
39389
39390         this.view.on('click', this.onViewClick, this);
39391
39392         this.store.on('beforeload', this.onBeforeLoad, this);
39393         this.store.on('load', this.onLoad, this);
39394         this.store.on('loadexception', this.onLoadException, this);
39395
39396         if(this.resizable){
39397             this.resizer = new Roo.Resizable(this.list,  {
39398                pinned:true, handles:'se'
39399             });
39400             this.resizer.on('resize', function(r, w, h){
39401                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39402                 this.listWidth = w;
39403                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39404                 this.restrictHeight();
39405             }, this);
39406             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39407         }
39408         if(!this.editable){
39409             this.editable = true;
39410             this.setEditable(false);
39411         }  
39412         
39413         
39414         if (typeof(this.events.add.listeners) != 'undefined') {
39415             
39416             this.addicon = this.wrap.createChild(
39417                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39418        
39419             this.addicon.on('click', function(e) {
39420                 this.fireEvent('add', this);
39421             }, this);
39422         }
39423         if (typeof(this.events.edit.listeners) != 'undefined') {
39424             
39425             this.editicon = this.wrap.createChild(
39426                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39427             if (this.addicon) {
39428                 this.editicon.setStyle('margin-left', '40px');
39429             }
39430             this.editicon.on('click', function(e) {
39431                 
39432                 // we fire even  if inothing is selected..
39433                 this.fireEvent('edit', this, this.lastData );
39434                 
39435             }, this);
39436         }
39437         
39438         
39439         
39440     },
39441
39442     // private
39443     initEvents : function(){
39444         Roo.form.ComboBox.superclass.initEvents.call(this);
39445
39446         this.keyNav = new Roo.KeyNav(this.el, {
39447             "up" : function(e){
39448                 this.inKeyMode = true;
39449                 this.selectPrev();
39450             },
39451
39452             "down" : function(e){
39453                 if(!this.isExpanded()){
39454                     this.onTriggerClick();
39455                 }else{
39456                     this.inKeyMode = true;
39457                     this.selectNext();
39458                 }
39459             },
39460
39461             "enter" : function(e){
39462                 this.onViewClick();
39463                 //return true;
39464             },
39465
39466             "esc" : function(e){
39467                 this.collapse();
39468             },
39469
39470             "tab" : function(e){
39471                 this.onViewClick(false);
39472                 this.fireEvent("specialkey", this, e);
39473                 return true;
39474             },
39475
39476             scope : this,
39477
39478             doRelay : function(foo, bar, hname){
39479                 if(hname == 'down' || this.scope.isExpanded()){
39480                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39481                 }
39482                 return true;
39483             },
39484
39485             forceKeyDown: true
39486         });
39487         this.queryDelay = Math.max(this.queryDelay || 10,
39488                 this.mode == 'local' ? 10 : 250);
39489         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39490         if(this.typeAhead){
39491             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39492         }
39493         if(this.editable !== false){
39494             this.el.on("keyup", this.onKeyUp, this);
39495         }
39496         if(this.forceSelection){
39497             this.on('blur', this.doForce, this);
39498         }
39499     },
39500
39501     onDestroy : function(){
39502         if(this.view){
39503             this.view.setStore(null);
39504             this.view.el.removeAllListeners();
39505             this.view.el.remove();
39506             this.view.purgeListeners();
39507         }
39508         if(this.list){
39509             this.list.destroy();
39510         }
39511         if(this.store){
39512             this.store.un('beforeload', this.onBeforeLoad, this);
39513             this.store.un('load', this.onLoad, this);
39514             this.store.un('loadexception', this.onLoadException, this);
39515         }
39516         Roo.form.ComboBox.superclass.onDestroy.call(this);
39517     },
39518
39519     // private
39520     fireKey : function(e){
39521         if(e.isNavKeyPress() && !this.list.isVisible()){
39522             this.fireEvent("specialkey", this, e);
39523         }
39524     },
39525
39526     // private
39527     onResize: function(w, h){
39528         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39529         
39530         if(typeof w != 'number'){
39531             // we do not handle it!?!?
39532             return;
39533         }
39534         var tw = this.trigger.getWidth();
39535         tw += this.addicon ? this.addicon.getWidth() : 0;
39536         tw += this.editicon ? this.editicon.getWidth() : 0;
39537         var x = w - tw;
39538         this.el.setWidth( this.adjustWidth('input', x));
39539             
39540         this.trigger.setStyle('left', x+'px');
39541         
39542         if(this.list && this.listWidth === undefined){
39543             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39544             this.list.setWidth(lw);
39545             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39546         }
39547         
39548     
39549         
39550     },
39551
39552     /**
39553      * Allow or prevent the user from directly editing the field text.  If false is passed,
39554      * the user will only be able to select from the items defined in the dropdown list.  This method
39555      * is the runtime equivalent of setting the 'editable' config option at config time.
39556      * @param {Boolean} value True to allow the user to directly edit the field text
39557      */
39558     setEditable : function(value){
39559         if(value == this.editable){
39560             return;
39561         }
39562         this.editable = value;
39563         if(!value){
39564             this.el.dom.setAttribute('readOnly', true);
39565             this.el.on('mousedown', this.onTriggerClick,  this);
39566             this.el.addClass('x-combo-noedit');
39567         }else{
39568             this.el.dom.setAttribute('readOnly', false);
39569             this.el.un('mousedown', this.onTriggerClick,  this);
39570             this.el.removeClass('x-combo-noedit');
39571         }
39572     },
39573
39574     // private
39575     onBeforeLoad : function(){
39576         if(!this.hasFocus){
39577             return;
39578         }
39579         this.innerList.update(this.loadingText ?
39580                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39581         this.restrictHeight();
39582         this.selectedIndex = -1;
39583     },
39584
39585     // private
39586     onLoad : function(){
39587         if(!this.hasFocus){
39588             return;
39589         }
39590         if(this.store.getCount() > 0){
39591             this.expand();
39592             this.restrictHeight();
39593             if(this.lastQuery == this.allQuery){
39594                 if(this.editable){
39595                     this.el.dom.select();
39596                 }
39597                 if(!this.selectByValue(this.value, true)){
39598                     this.select(0, true);
39599                 }
39600             }else{
39601                 this.selectNext();
39602                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39603                     this.taTask.delay(this.typeAheadDelay);
39604                 }
39605             }
39606         }else{
39607             this.onEmptyResults();
39608         }
39609         //this.el.focus();
39610     },
39611     // private
39612     onLoadException : function()
39613     {
39614         this.collapse();
39615         Roo.log(this.store.reader.jsonData);
39616         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39617             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39618         }
39619         
39620         
39621     },
39622     // private
39623     onTypeAhead : function(){
39624         if(this.store.getCount() > 0){
39625             var r = this.store.getAt(0);
39626             var newValue = r.data[this.displayField];
39627             var len = newValue.length;
39628             var selStart = this.getRawValue().length;
39629             if(selStart != len){
39630                 this.setRawValue(newValue);
39631                 this.selectText(selStart, newValue.length);
39632             }
39633         }
39634     },
39635
39636     // private
39637     onSelect : function(record, index){
39638         if(this.fireEvent('beforeselect', this, record, index) !== false){
39639             this.setFromData(index > -1 ? record.data : false);
39640             this.collapse();
39641             this.fireEvent('select', this, record, index);
39642         }
39643     },
39644
39645     /**
39646      * Returns the currently selected field value or empty string if no value is set.
39647      * @return {String} value The selected value
39648      */
39649     getValue : function(){
39650         if(this.valueField){
39651             return typeof this.value != 'undefined' ? this.value : '';
39652         }else{
39653             return Roo.form.ComboBox.superclass.getValue.call(this);
39654         }
39655     },
39656
39657     /**
39658      * Clears any text/value currently set in the field
39659      */
39660     clearValue : function(){
39661         if(this.hiddenField){
39662             this.hiddenField.value = '';
39663         }
39664         this.value = '';
39665         this.setRawValue('');
39666         this.lastSelectionText = '';
39667         
39668     },
39669
39670     /**
39671      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39672      * will be displayed in the field.  If the value does not match the data value of an existing item,
39673      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39674      * Otherwise the field will be blank (although the value will still be set).
39675      * @param {String} value The value to match
39676      */
39677     setValue : function(v){
39678         var text = v;
39679         if(this.valueField){
39680             var r = this.findRecord(this.valueField, v);
39681             if(r){
39682                 text = r.data[this.displayField];
39683             }else if(this.valueNotFoundText !== undefined){
39684                 text = this.valueNotFoundText;
39685             }
39686         }
39687         this.lastSelectionText = text;
39688         if(this.hiddenField){
39689             this.hiddenField.value = v;
39690         }
39691         Roo.form.ComboBox.superclass.setValue.call(this, text);
39692         this.value = v;
39693     },
39694     /**
39695      * @property {Object} the last set data for the element
39696      */
39697     
39698     lastData : false,
39699     /**
39700      * Sets the value of the field based on a object which is related to the record format for the store.
39701      * @param {Object} value the value to set as. or false on reset?
39702      */
39703     setFromData : function(o){
39704         var dv = ''; // display value
39705         var vv = ''; // value value..
39706         this.lastData = o;
39707         if (this.displayField) {
39708             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39709         } else {
39710             // this is an error condition!!!
39711             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39712         }
39713         
39714         if(this.valueField){
39715             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39716         }
39717         if(this.hiddenField){
39718             this.hiddenField.value = vv;
39719             
39720             this.lastSelectionText = dv;
39721             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39722             this.value = vv;
39723             return;
39724         }
39725         // no hidden field.. - we store the value in 'value', but still display
39726         // display field!!!!
39727         this.lastSelectionText = dv;
39728         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39729         this.value = vv;
39730         
39731         
39732     },
39733     // private
39734     reset : function(){
39735         // overridden so that last data is reset..
39736         this.setValue(this.resetValue);
39737         this.clearInvalid();
39738         this.lastData = false;
39739         if (this.view) {
39740             this.view.clearSelections();
39741         }
39742     },
39743     // private
39744     findRecord : function(prop, value){
39745         var record;
39746         if(this.store.getCount() > 0){
39747             this.store.each(function(r){
39748                 if(r.data[prop] == value){
39749                     record = r;
39750                     return false;
39751                 }
39752                 return true;
39753             });
39754         }
39755         return record;
39756     },
39757     
39758     getName: function()
39759     {
39760         // returns hidden if it's set..
39761         if (!this.rendered) {return ''};
39762         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39763         
39764     },
39765     // private
39766     onViewMove : function(e, t){
39767         this.inKeyMode = false;
39768     },
39769
39770     // private
39771     onViewOver : function(e, t){
39772         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39773             return;
39774         }
39775         var item = this.view.findItemFromChild(t);
39776         if(item){
39777             var index = this.view.indexOf(item);
39778             this.select(index, false);
39779         }
39780     },
39781
39782     // private
39783     onViewClick : function(doFocus)
39784     {
39785         var index = this.view.getSelectedIndexes()[0];
39786         var r = this.store.getAt(index);
39787         if(r){
39788             this.onSelect(r, index);
39789         }
39790         if(doFocus !== false && !this.blockFocus){
39791             this.el.focus();
39792         }
39793     },
39794
39795     // private
39796     restrictHeight : function(){
39797         this.innerList.dom.style.height = '';
39798         var inner = this.innerList.dom;
39799         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39800         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39801         this.list.beginUpdate();
39802         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39803         this.list.alignTo(this.el, this.listAlign);
39804         this.list.endUpdate();
39805     },
39806
39807     // private
39808     onEmptyResults : function(){
39809         this.collapse();
39810     },
39811
39812     /**
39813      * Returns true if the dropdown list is expanded, else false.
39814      */
39815     isExpanded : function(){
39816         return this.list.isVisible();
39817     },
39818
39819     /**
39820      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39821      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39822      * @param {String} value The data value of the item to select
39823      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39824      * selected item if it is not currently in view (defaults to true)
39825      * @return {Boolean} True if the value matched an item in the list, else false
39826      */
39827     selectByValue : function(v, scrollIntoView){
39828         if(v !== undefined && v !== null){
39829             var r = this.findRecord(this.valueField || this.displayField, v);
39830             if(r){
39831                 this.select(this.store.indexOf(r), scrollIntoView);
39832                 return true;
39833             }
39834         }
39835         return false;
39836     },
39837
39838     /**
39839      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39840      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39841      * @param {Number} index The zero-based index of the list item to select
39842      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39843      * selected item if it is not currently in view (defaults to true)
39844      */
39845     select : function(index, scrollIntoView){
39846         this.selectedIndex = index;
39847         this.view.select(index);
39848         if(scrollIntoView !== false){
39849             var el = this.view.getNode(index);
39850             if(el){
39851                 this.innerList.scrollChildIntoView(el, false);
39852             }
39853         }
39854     },
39855
39856     // private
39857     selectNext : function(){
39858         var ct = this.store.getCount();
39859         if(ct > 0){
39860             if(this.selectedIndex == -1){
39861                 this.select(0);
39862             }else if(this.selectedIndex < ct-1){
39863                 this.select(this.selectedIndex+1);
39864             }
39865         }
39866     },
39867
39868     // private
39869     selectPrev : function(){
39870         var ct = this.store.getCount();
39871         if(ct > 0){
39872             if(this.selectedIndex == -1){
39873                 this.select(0);
39874             }else if(this.selectedIndex != 0){
39875                 this.select(this.selectedIndex-1);
39876             }
39877         }
39878     },
39879
39880     // private
39881     onKeyUp : function(e){
39882         if(this.editable !== false && !e.isSpecialKey()){
39883             this.lastKey = e.getKey();
39884             this.dqTask.delay(this.queryDelay);
39885         }
39886     },
39887
39888     // private
39889     validateBlur : function(){
39890         return !this.list || !this.list.isVisible();   
39891     },
39892
39893     // private
39894     initQuery : function(){
39895         this.doQuery(this.getRawValue());
39896     },
39897
39898     // private
39899     doForce : function(){
39900         if(this.el.dom.value.length > 0){
39901             this.el.dom.value =
39902                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39903              
39904         }
39905     },
39906
39907     /**
39908      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39909      * query allowing the query action to be canceled if needed.
39910      * @param {String} query The SQL query to execute
39911      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39912      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39913      * saved in the current store (defaults to false)
39914      */
39915     doQuery : function(q, forceAll){
39916         if(q === undefined || q === null){
39917             q = '';
39918         }
39919         var qe = {
39920             query: q,
39921             forceAll: forceAll,
39922             combo: this,
39923             cancel:false
39924         };
39925         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39926             return false;
39927         }
39928         q = qe.query;
39929         forceAll = qe.forceAll;
39930         if(forceAll === true || (q.length >= this.minChars)){
39931             if(this.lastQuery != q || this.alwaysQuery){
39932                 this.lastQuery = q;
39933                 if(this.mode == 'local'){
39934                     this.selectedIndex = -1;
39935                     if(forceAll){
39936                         this.store.clearFilter();
39937                     }else{
39938                         this.store.filter(this.displayField, q);
39939                     }
39940                     this.onLoad();
39941                 }else{
39942                     this.store.baseParams[this.queryParam] = q;
39943                     this.store.load({
39944                         params: this.getParams(q)
39945                     });
39946                     this.expand();
39947                 }
39948             }else{
39949                 this.selectedIndex = -1;
39950                 this.onLoad();   
39951             }
39952         }
39953     },
39954
39955     // private
39956     getParams : function(q){
39957         var p = {};
39958         //p[this.queryParam] = q;
39959         if(this.pageSize){
39960             p.start = 0;
39961             p.limit = this.pageSize;
39962         }
39963         return p;
39964     },
39965
39966     /**
39967      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39968      */
39969     collapse : function(){
39970         if(!this.isExpanded()){
39971             return;
39972         }
39973         this.list.hide();
39974         Roo.get(document).un('mousedown', this.collapseIf, this);
39975         Roo.get(document).un('mousewheel', this.collapseIf, this);
39976         if (!this.editable) {
39977             Roo.get(document).un('keydown', this.listKeyPress, this);
39978         }
39979         this.fireEvent('collapse', this);
39980     },
39981
39982     // private
39983     collapseIf : function(e){
39984         if(!e.within(this.wrap) && !e.within(this.list)){
39985             this.collapse();
39986         }
39987     },
39988
39989     /**
39990      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39991      */
39992     expand : function(){
39993         if(this.isExpanded() || !this.hasFocus){
39994             return;
39995         }
39996         this.list.alignTo(this.el, this.listAlign);
39997         this.list.show();
39998         Roo.get(document).on('mousedown', this.collapseIf, this);
39999         Roo.get(document).on('mousewheel', this.collapseIf, this);
40000         if (!this.editable) {
40001             Roo.get(document).on('keydown', this.listKeyPress, this);
40002         }
40003         
40004         this.fireEvent('expand', this);
40005     },
40006
40007     // private
40008     // Implements the default empty TriggerField.onTriggerClick function
40009     onTriggerClick : function(){
40010         if(this.disabled){
40011             return;
40012         }
40013         if(this.isExpanded()){
40014             this.collapse();
40015             if (!this.blockFocus) {
40016                 this.el.focus();
40017             }
40018             
40019         }else {
40020             this.hasFocus = true;
40021             if(this.triggerAction == 'all') {
40022                 this.doQuery(this.allQuery, true);
40023             } else {
40024                 this.doQuery(this.getRawValue());
40025             }
40026             if (!this.blockFocus) {
40027                 this.el.focus();
40028             }
40029         }
40030     },
40031     listKeyPress : function(e)
40032     {
40033         //Roo.log('listkeypress');
40034         // scroll to first matching element based on key pres..
40035         if (e.isSpecialKey()) {
40036             return false;
40037         }
40038         var k = String.fromCharCode(e.getKey()).toUpperCase();
40039         //Roo.log(k);
40040         var match  = false;
40041         var csel = this.view.getSelectedNodes();
40042         var cselitem = false;
40043         if (csel.length) {
40044             var ix = this.view.indexOf(csel[0]);
40045             cselitem  = this.store.getAt(ix);
40046             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40047                 cselitem = false;
40048             }
40049             
40050         }
40051         
40052         this.store.each(function(v) { 
40053             if (cselitem) {
40054                 // start at existing selection.
40055                 if (cselitem.id == v.id) {
40056                     cselitem = false;
40057                 }
40058                 return;
40059             }
40060                 
40061             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40062                 match = this.store.indexOf(v);
40063                 return false;
40064             }
40065         }, this);
40066         
40067         if (match === false) {
40068             return true; // no more action?
40069         }
40070         // scroll to?
40071         this.view.select(match);
40072         var sn = Roo.get(this.view.getSelectedNodes()[0])
40073         sn.scrollIntoView(sn.dom.parentNode, false);
40074     }
40075
40076     /** 
40077     * @cfg {Boolean} grow 
40078     * @hide 
40079     */
40080     /** 
40081     * @cfg {Number} growMin 
40082     * @hide 
40083     */
40084     /** 
40085     * @cfg {Number} growMax 
40086     * @hide 
40087     */
40088     /**
40089      * @hide
40090      * @method autoSize
40091      */
40092 });/*
40093  * Copyright(c) 2010-2012, Roo J Solutions Limited
40094  *
40095  * Licence LGPL
40096  *
40097  */
40098
40099 /**
40100  * @class Roo.form.ComboBoxArray
40101  * @extends Roo.form.TextField
40102  * A facebook style adder... for lists of email / people / countries  etc...
40103  * pick multiple items from a combo box, and shows each one.
40104  *
40105  *  Fred [x]  Brian [x]  [Pick another |v]
40106  *
40107  *
40108  *  For this to work: it needs various extra information
40109  *    - normal combo problay has
40110  *      name, hiddenName
40111  *    + displayField, valueField
40112  *
40113  *    For our purpose...
40114  *
40115  *
40116  *   If we change from 'extends' to wrapping...
40117  *   
40118  *  
40119  *
40120  
40121  
40122  * @constructor
40123  * Create a new ComboBoxArray.
40124  * @param {Object} config Configuration options
40125  */
40126  
40127
40128 Roo.form.ComboBoxArray = function(config)
40129 {
40130     
40131     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40132     
40133     this.items = new Roo.util.MixedCollection(false);
40134     
40135     // construct the child combo...
40136     
40137     
40138     
40139     
40140    
40141     
40142 }
40143
40144  
40145 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40146
40147     /**
40148      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40149      */
40150     
40151     lastData : false,
40152     
40153     // behavies liek a hiddne field
40154     inputType:      'hidden',
40155     /**
40156      * @cfg {Number} width The width of the box that displays the selected element
40157      */ 
40158     width:          300,
40159
40160     
40161     
40162     /**
40163      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40164      */
40165     name : false,
40166     /**
40167      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40168      */
40169     hiddenName : false,
40170     
40171     
40172     // private the array of items that are displayed..
40173     items  : false,
40174     // private - the hidden field el.
40175     hiddenEl : false,
40176     // private - the filed el..
40177     el : false,
40178     
40179     //validateValue : function() { return true; }, // all values are ok!
40180     //onAddClick: function() { },
40181     
40182     onRender : function(ct, position) 
40183     {
40184         
40185         // create the standard hidden element
40186         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40187         
40188         
40189         // give fake names to child combo;
40190         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40191         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40192         
40193         this.combo = Roo.factory(this.combo, Roo.form);
40194         this.combo.onRender(ct, position);
40195         if (typeof(this.combo.width) != 'undefined') {
40196             this.combo.onResize(this.combo.width,0);
40197         }
40198         
40199         this.combo.initEvents();
40200         
40201         // assigned so form know we need to do this..
40202         this.store          = this.combo.store;
40203         this.valueField     = this.combo.valueField;
40204         this.displayField   = this.combo.displayField ;
40205         
40206         
40207         this.combo.wrap.addClass('x-cbarray-grp');
40208         
40209         var cbwrap = this.combo.wrap.createChild(
40210             {tag: 'div', cls: 'x-cbarray-cb'},
40211             this.combo.el.dom
40212         );
40213         
40214              
40215         this.hiddenEl = this.combo.wrap.createChild({
40216             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40217         });
40218         this.el = this.combo.wrap.createChild({
40219             tag: 'input',  type:'hidden' , name: this.name, value : ''
40220         });
40221          //   this.el.dom.removeAttribute("name");
40222         
40223         
40224         this.outerWrap = this.combo.wrap;
40225         this.wrap = cbwrap;
40226         
40227         this.outerWrap.setWidth(this.width);
40228         this.outerWrap.dom.removeChild(this.el.dom);
40229         
40230         this.wrap.dom.appendChild(this.el.dom);
40231         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40232         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40233         
40234         this.combo.trigger.setStyle('position','relative');
40235         this.combo.trigger.setStyle('left', '0px');
40236         this.combo.trigger.setStyle('top', '2px');
40237         
40238         this.combo.el.setStyle('vertical-align', 'text-bottom');
40239         
40240         //this.trigger.setStyle('vertical-align', 'top');
40241         
40242         // this should use the code from combo really... on('add' ....)
40243         if (this.adder) {
40244             
40245         
40246             this.adder = this.outerWrap.createChild(
40247                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40248             var _t = this;
40249             this.adder.on('click', function(e) {
40250                 _t.fireEvent('adderclick', this, e);
40251             }, _t);
40252         }
40253         //var _t = this;
40254         //this.adder.on('click', this.onAddClick, _t);
40255         
40256         
40257         this.combo.on('select', function(cb, rec, ix) {
40258             this.addItem(rec.data);
40259             
40260             cb.setValue('');
40261             cb.el.dom.value = '';
40262             //cb.lastData = rec.data;
40263             // add to list
40264             
40265         }, this);
40266         
40267         
40268     },
40269     
40270     
40271     getName: function()
40272     {
40273         // returns hidden if it's set..
40274         if (!this.rendered) {return ''};
40275         return  this.hiddenName ? this.hiddenName : this.name;
40276         
40277     },
40278     
40279     
40280     onResize: function(w, h){
40281         
40282         return;
40283         // not sure if this is needed..
40284         //this.combo.onResize(w,h);
40285         
40286         if(typeof w != 'number'){
40287             // we do not handle it!?!?
40288             return;
40289         }
40290         var tw = this.combo.trigger.getWidth();
40291         tw += this.addicon ? this.addicon.getWidth() : 0;
40292         tw += this.editicon ? this.editicon.getWidth() : 0;
40293         var x = w - tw;
40294         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40295             
40296         this.combo.trigger.setStyle('left', '0px');
40297         
40298         if(this.list && this.listWidth === undefined){
40299             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40300             this.list.setWidth(lw);
40301             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40302         }
40303         
40304     
40305         
40306     },
40307     
40308     addItem: function(rec)
40309     {
40310         var valueField = this.combo.valueField;
40311         var displayField = this.combo.displayField;
40312         if (this.items.indexOfKey(rec[valueField]) > -1) {
40313             //console.log("GOT " + rec.data.id);
40314             return;
40315         }
40316         
40317         var x = new Roo.form.ComboBoxArray.Item({
40318             //id : rec[this.idField],
40319             data : rec,
40320             displayField : displayField ,
40321             tipField : displayField ,
40322             cb : this
40323         });
40324         // use the 
40325         this.items.add(rec[valueField],x);
40326         // add it before the element..
40327         this.updateHiddenEl();
40328         x.render(this.outerWrap, this.wrap.dom);
40329         // add the image handler..
40330     },
40331     
40332     updateHiddenEl : function()
40333     {
40334         this.validate();
40335         if (!this.hiddenEl) {
40336             return;
40337         }
40338         var ar = [];
40339         var idField = this.combo.valueField;
40340         
40341         this.items.each(function(f) {
40342             ar.push(f.data[idField]);
40343            
40344         });
40345         this.hiddenEl.dom.value = ar.join(',');
40346         this.validate();
40347     },
40348     
40349     reset : function()
40350     {
40351         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40352         this.items.each(function(f) {
40353            f.remove(); 
40354         });
40355         this.el.dom.value = '';
40356         if (this.hiddenEl) {
40357             this.hiddenEl.dom.value = '';
40358         }
40359         
40360     },
40361     getValue: function()
40362     {
40363         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40364     },
40365     setValue: function(v) // not a valid action - must use addItems..
40366     {
40367          
40368         this.reset();
40369         
40370         
40371         
40372         if (this.store.isLocal && (typeof(v) == 'string')) {
40373             // then we can use the store to find the values..
40374             // comma seperated at present.. this needs to allow JSON based encoding..
40375             this.hiddenEl.value  = v;
40376             var v_ar = [];
40377             Roo.each(v.split(','), function(k) {
40378                 Roo.log("CHECK " + this.valueField + ',' + k);
40379                 var li = this.store.query(this.valueField, k);
40380                 if (!li.length) {
40381                     return;
40382                 }
40383                 var add = {};
40384                 add[this.valueField] = k;
40385                 add[this.displayField] = li.item(0).data[this.displayField];
40386                 
40387                 this.addItem(add);
40388             }, this) 
40389              
40390         }
40391         if (typeof(v) == 'object') {
40392             // then let's assume it's an array of objects..
40393             Roo.each(v, function(l) {
40394                 this.addItem(l);
40395             }, this);
40396              
40397         }
40398         
40399         
40400     },
40401     setFromData: function(v)
40402     {
40403         // this recieves an object, if setValues is called.
40404         this.reset();
40405         this.el.dom.value = v[this.displayField];
40406         this.hiddenEl.dom.value = v[this.valueField];
40407         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40408             return;
40409         }
40410         var kv = v[this.valueField];
40411         var dv = v[this.displayField];
40412         kv = typeof(kv) != 'string' ? '' : kv;
40413         dv = typeof(dv) != 'string' ? '' : dv;
40414         
40415         
40416         var keys = kv.split(',');
40417         var display = dv.split(',');
40418         for (var i = 0 ; i < keys.length; i++) {
40419             
40420             add = {};
40421             add[this.valueField] = keys[i];
40422             add[this.displayField] = display[i];
40423             this.addItem(add);
40424         }
40425       
40426         
40427     },
40428     
40429     /**
40430      * Validates the combox array value
40431      * @return {Boolean} True if the value is valid, else false
40432      */
40433     validate : function(){
40434         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40435             this.clearInvalid();
40436             return true;
40437         }
40438         return false;
40439     },
40440     
40441     validateValue : function(value){
40442         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40443         
40444     },
40445     
40446     /*@
40447      * overide
40448      * 
40449      */
40450     isDirty : function() {
40451         if(this.disabled) {
40452             return false;
40453         }
40454         
40455         try {
40456             var d = Roo.decode(String(this.originalValue));
40457         } catch (e) {
40458             return String(this.getValue()) !== String(this.originalValue);
40459         }
40460         
40461         var originalValue = [];
40462         
40463         for (var i = 0; i < d.length; i++){
40464             originalValue.push(d[i][this.valueField]);
40465         }
40466         
40467         return String(this.getValue()) !== String(originalValue.join(','));
40468         
40469     }
40470     
40471 });
40472
40473
40474
40475 /**
40476  * @class Roo.form.ComboBoxArray.Item
40477  * @extends Roo.BoxComponent
40478  * A selected item in the list
40479  *  Fred [x]  Brian [x]  [Pick another |v]
40480  * 
40481  * @constructor
40482  * Create a new item.
40483  * @param {Object} config Configuration options
40484  */
40485  
40486 Roo.form.ComboBoxArray.Item = function(config) {
40487     config.id = Roo.id();
40488     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40489 }
40490
40491 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40492     data : {},
40493     cb: false,
40494     displayField : false,
40495     tipField : false,
40496     
40497     
40498     defaultAutoCreate : {
40499         tag: 'div',
40500         cls: 'x-cbarray-item',
40501         cn : [ 
40502             { tag: 'div' },
40503             {
40504                 tag: 'img',
40505                 width:16,
40506                 height : 16,
40507                 src : Roo.BLANK_IMAGE_URL ,
40508                 align: 'center'
40509             }
40510         ]
40511         
40512     },
40513     
40514  
40515     onRender : function(ct, position)
40516     {
40517         Roo.form.Field.superclass.onRender.call(this, ct, position);
40518         
40519         if(!this.el){
40520             var cfg = this.getAutoCreate();
40521             this.el = ct.createChild(cfg, position);
40522         }
40523         
40524         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40525         
40526         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40527             this.cb.renderer(this.data) :
40528             String.format('{0}',this.data[this.displayField]);
40529         
40530             
40531         this.el.child('div').dom.setAttribute('qtip',
40532                         String.format('{0}',this.data[this.tipField])
40533         );
40534         
40535         this.el.child('img').on('click', this.remove, this);
40536         
40537     },
40538    
40539     remove : function()
40540     {
40541         
40542         this.cb.items.remove(this);
40543         this.el.child('img').un('click', this.remove, this);
40544         this.el.remove();
40545         this.cb.updateHiddenEl();
40546     }
40547 });/*
40548  * Based on:
40549  * Ext JS Library 1.1.1
40550  * Copyright(c) 2006-2007, Ext JS, LLC.
40551  *
40552  * Originally Released Under LGPL - original licence link has changed is not relivant.
40553  *
40554  * Fork - LGPL
40555  * <script type="text/javascript">
40556  */
40557 /**
40558  * @class Roo.form.Checkbox
40559  * @extends Roo.form.Field
40560  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40561  * @constructor
40562  * Creates a new Checkbox
40563  * @param {Object} config Configuration options
40564  */
40565 Roo.form.Checkbox = function(config){
40566     Roo.form.Checkbox.superclass.constructor.call(this, config);
40567     this.addEvents({
40568         /**
40569          * @event check
40570          * Fires when the checkbox is checked or unchecked.
40571              * @param {Roo.form.Checkbox} this This checkbox
40572              * @param {Boolean} checked The new checked value
40573              */
40574         check : true
40575     });
40576 };
40577
40578 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40579     /**
40580      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40581      */
40582     focusClass : undefined,
40583     /**
40584      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40585      */
40586     fieldClass: "x-form-field",
40587     /**
40588      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40589      */
40590     checked: false,
40591     /**
40592      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40593      * {tag: "input", type: "checkbox", autocomplete: "off"})
40594      */
40595     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40596     /**
40597      * @cfg {String} boxLabel The text that appears beside the checkbox
40598      */
40599     boxLabel : "",
40600     /**
40601      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40602      */  
40603     inputValue : '1',
40604     /**
40605      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40606      */
40607      valueOff: '0', // value when not checked..
40608
40609     actionMode : 'viewEl', 
40610     //
40611     // private
40612     itemCls : 'x-menu-check-item x-form-item',
40613     groupClass : 'x-menu-group-item',
40614     inputType : 'hidden',
40615     
40616     
40617     inSetChecked: false, // check that we are not calling self...
40618     
40619     inputElement: false, // real input element?
40620     basedOn: false, // ????
40621     
40622     isFormField: true, // not sure where this is needed!!!!
40623
40624     onResize : function(){
40625         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40626         if(!this.boxLabel){
40627             this.el.alignTo(this.wrap, 'c-c');
40628         }
40629     },
40630
40631     initEvents : function(){
40632         Roo.form.Checkbox.superclass.initEvents.call(this);
40633         this.el.on("click", this.onClick,  this);
40634         this.el.on("change", this.onClick,  this);
40635     },
40636
40637
40638     getResizeEl : function(){
40639         return this.wrap;
40640     },
40641
40642     getPositionEl : function(){
40643         return this.wrap;
40644     },
40645
40646     // private
40647     onRender : function(ct, position){
40648         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40649         /*
40650         if(this.inputValue !== undefined){
40651             this.el.dom.value = this.inputValue;
40652         }
40653         */
40654         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40655         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40656         var viewEl = this.wrap.createChild({ 
40657             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40658         this.viewEl = viewEl;   
40659         this.wrap.on('click', this.onClick,  this); 
40660         
40661         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40662         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40663         
40664         
40665         
40666         if(this.boxLabel){
40667             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40668         //    viewEl.on('click', this.onClick,  this); 
40669         }
40670         //if(this.checked){
40671             this.setChecked(this.checked);
40672         //}else{
40673             //this.checked = this.el.dom;
40674         //}
40675
40676     },
40677
40678     // private
40679     initValue : Roo.emptyFn,
40680
40681     /**
40682      * Returns the checked state of the checkbox.
40683      * @return {Boolean} True if checked, else false
40684      */
40685     getValue : function(){
40686         if(this.el){
40687             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40688         }
40689         return this.valueOff;
40690         
40691     },
40692
40693         // private
40694     onClick : function(){ 
40695         this.setChecked(!this.checked);
40696
40697         //if(this.el.dom.checked != this.checked){
40698         //    this.setValue(this.el.dom.checked);
40699        // }
40700     },
40701
40702     /**
40703      * Sets the checked state of the checkbox.
40704      * On is always based on a string comparison between inputValue and the param.
40705      * @param {Boolean/String} value - the value to set 
40706      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40707      */
40708     setValue : function(v,suppressEvent){
40709         
40710         
40711         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40712         //if(this.el && this.el.dom){
40713         //    this.el.dom.checked = this.checked;
40714         //    this.el.dom.defaultChecked = this.checked;
40715         //}
40716         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40717         //this.fireEvent("check", this, this.checked);
40718     },
40719     // private..
40720     setChecked : function(state,suppressEvent)
40721     {
40722         if (this.inSetChecked) {
40723             this.checked = state;
40724             return;
40725         }
40726         
40727     
40728         if(this.wrap){
40729             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40730         }
40731         this.checked = state;
40732         if(suppressEvent !== true){
40733             this.fireEvent('check', this, state);
40734         }
40735         this.inSetChecked = true;
40736         this.el.dom.value = state ? this.inputValue : this.valueOff;
40737         this.inSetChecked = false;
40738         
40739     },
40740     // handle setting of hidden value by some other method!!?!?
40741     setFromHidden: function()
40742     {
40743         if(!this.el){
40744             return;
40745         }
40746         //console.log("SET FROM HIDDEN");
40747         //alert('setFrom hidden');
40748         this.setValue(this.el.dom.value);
40749     },
40750     
40751     onDestroy : function()
40752     {
40753         if(this.viewEl){
40754             Roo.get(this.viewEl).remove();
40755         }
40756          
40757         Roo.form.Checkbox.superclass.onDestroy.call(this);
40758     }
40759
40760 });/*
40761  * Based on:
40762  * Ext JS Library 1.1.1
40763  * Copyright(c) 2006-2007, Ext JS, LLC.
40764  *
40765  * Originally Released Under LGPL - original licence link has changed is not relivant.
40766  *
40767  * Fork - LGPL
40768  * <script type="text/javascript">
40769  */
40770  
40771 /**
40772  * @class Roo.form.Radio
40773  * @extends Roo.form.Checkbox
40774  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40775  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40776  * @constructor
40777  * Creates a new Radio
40778  * @param {Object} config Configuration options
40779  */
40780 Roo.form.Radio = function(){
40781     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40782 };
40783 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40784     inputType: 'radio',
40785
40786     /**
40787      * If this radio is part of a group, it will return the selected value
40788      * @return {String}
40789      */
40790     getGroupValue : function(){
40791         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40792     },
40793     
40794     
40795     onRender : function(ct, position){
40796         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40797         
40798         if(this.inputValue !== undefined){
40799             this.el.dom.value = this.inputValue;
40800         }
40801          
40802         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40803         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40804         //var viewEl = this.wrap.createChild({ 
40805         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40806         //this.viewEl = viewEl;   
40807         //this.wrap.on('click', this.onClick,  this); 
40808         
40809         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40810         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40811         
40812         
40813         
40814         if(this.boxLabel){
40815             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40816         //    viewEl.on('click', this.onClick,  this); 
40817         }
40818          if(this.checked){
40819             this.el.dom.checked =   'checked' ;
40820         }
40821          
40822     } 
40823     
40824     
40825 });//<script type="text/javascript">
40826
40827 /*
40828  * Ext JS Library 1.1.1
40829  * Copyright(c) 2006-2007, Ext JS, LLC.
40830  * licensing@extjs.com
40831  * 
40832  * http://www.extjs.com/license
40833  */
40834  
40835  /*
40836   * 
40837   * Known bugs:
40838   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40839   * - IE ? - no idea how much works there.
40840   * 
40841   * 
40842   * 
40843   */
40844  
40845
40846 /**
40847  * @class Ext.form.HtmlEditor
40848  * @extends Ext.form.Field
40849  * Provides a lightweight HTML Editor component.
40850  *
40851  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40852  * 
40853  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40854  * supported by this editor.</b><br/><br/>
40855  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40856  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40857  */
40858 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40859       /**
40860      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40861      */
40862     toolbars : false,
40863     /**
40864      * @cfg {String} createLinkText The default text for the create link prompt
40865      */
40866     createLinkText : 'Please enter the URL for the link:',
40867     /**
40868      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40869      */
40870     defaultLinkValue : 'http:/'+'/',
40871    
40872      /**
40873      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40874      *                        Roo.resizable.
40875      */
40876     resizable : false,
40877      /**
40878      * @cfg {Number} height (in pixels)
40879      */   
40880     height: 300,
40881    /**
40882      * @cfg {Number} width (in pixels)
40883      */   
40884     width: 500,
40885     
40886     /**
40887      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40888      * 
40889      */
40890     stylesheets: false,
40891     
40892     // id of frame..
40893     frameId: false,
40894     
40895     // private properties
40896     validationEvent : false,
40897     deferHeight: true,
40898     initialized : false,
40899     activated : false,
40900     sourceEditMode : false,
40901     onFocus : Roo.emptyFn,
40902     iframePad:3,
40903     hideMode:'offsets',
40904     
40905     defaultAutoCreate : { // modified by initCompnoent..
40906         tag: "textarea",
40907         style:"width:500px;height:300px;",
40908         autocomplete: "off"
40909     },
40910
40911     // private
40912     initComponent : function(){
40913         this.addEvents({
40914             /**
40915              * @event initialize
40916              * Fires when the editor is fully initialized (including the iframe)
40917              * @param {HtmlEditor} this
40918              */
40919             initialize: true,
40920             /**
40921              * @event activate
40922              * Fires when the editor is first receives the focus. Any insertion must wait
40923              * until after this event.
40924              * @param {HtmlEditor} this
40925              */
40926             activate: true,
40927              /**
40928              * @event beforesync
40929              * Fires before the textarea is updated with content from the editor iframe. Return false
40930              * to cancel the sync.
40931              * @param {HtmlEditor} this
40932              * @param {String} html
40933              */
40934             beforesync: true,
40935              /**
40936              * @event beforepush
40937              * Fires before the iframe editor is updated with content from the textarea. Return false
40938              * to cancel the push.
40939              * @param {HtmlEditor} this
40940              * @param {String} html
40941              */
40942             beforepush: true,
40943              /**
40944              * @event sync
40945              * Fires when the textarea is updated with content from the editor iframe.
40946              * @param {HtmlEditor} this
40947              * @param {String} html
40948              */
40949             sync: true,
40950              /**
40951              * @event push
40952              * Fires when the iframe editor is updated with content from the textarea.
40953              * @param {HtmlEditor} this
40954              * @param {String} html
40955              */
40956             push: true,
40957              /**
40958              * @event editmodechange
40959              * Fires when the editor switches edit modes
40960              * @param {HtmlEditor} this
40961              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40962              */
40963             editmodechange: true,
40964             /**
40965              * @event editorevent
40966              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40967              * @param {HtmlEditor} this
40968              */
40969             editorevent: true
40970         });
40971         this.defaultAutoCreate =  {
40972             tag: "textarea",
40973             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40974             autocomplete: "off"
40975         };
40976     },
40977
40978     /**
40979      * Protected method that will not generally be called directly. It
40980      * is called when the editor creates its toolbar. Override this method if you need to
40981      * add custom toolbar buttons.
40982      * @param {HtmlEditor} editor
40983      */
40984     createToolbar : function(editor){
40985         if (!editor.toolbars || !editor.toolbars.length) {
40986             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40987         }
40988         
40989         for (var i =0 ; i < editor.toolbars.length;i++) {
40990             editor.toolbars[i] = Roo.factory(
40991                     typeof(editor.toolbars[i]) == 'string' ?
40992                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40993                 Roo.form.HtmlEditor);
40994             editor.toolbars[i].init(editor);
40995         }
40996          
40997         
40998     },
40999
41000     /**
41001      * Protected method that will not generally be called directly. It
41002      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41003      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41004      */
41005     getDocMarkup : function(){
41006         // body styles..
41007         var st = '';
41008         if (this.stylesheets === false) {
41009             
41010             Roo.get(document.head).select('style').each(function(node) {
41011                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41012             });
41013             
41014             Roo.get(document.head).select('link').each(function(node) { 
41015                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41016             });
41017             
41018         } else if (!this.stylesheets.length) {
41019                 // simple..
41020                 st = '<style type="text/css">' +
41021                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41022                    '</style>';
41023         } else {
41024             Roo.each(this.stylesheets, function(s) {
41025                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41026             });
41027             
41028         }
41029         
41030         st +=  '<style type="text/css">' +
41031             'IMG { cursor: pointer } ' +
41032         '</style>';
41033
41034         
41035         return '<html><head>' + st  +
41036             //<style type="text/css">' +
41037             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41038             //'</style>' +
41039             ' </head><body class="roo-htmleditor-body"></body></html>';
41040     },
41041
41042     // private
41043     onRender : function(ct, position)
41044     {
41045         var _t = this;
41046         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
41047         this.el.dom.style.border = '0 none';
41048         this.el.dom.setAttribute('tabIndex', -1);
41049         this.el.addClass('x-hidden');
41050         if(Roo.isIE){ // fix IE 1px bogus margin
41051             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41052         }
41053         this.wrap = this.el.wrap({
41054             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
41055         });
41056         
41057         if (this.resizable) {
41058             this.resizeEl = new Roo.Resizable(this.wrap, {
41059                 pinned : true,
41060                 wrap: true,
41061                 dynamic : true,
41062                 minHeight : this.height,
41063                 height: this.height,
41064                 handles : this.resizable,
41065                 width: this.width,
41066                 listeners : {
41067                     resize : function(r, w, h) {
41068                         _t.onResize(w,h); // -something
41069                     }
41070                 }
41071             });
41072             
41073         }
41074
41075         this.frameId = Roo.id();
41076         
41077         this.createToolbar(this);
41078         
41079       
41080         
41081         var iframe = this.wrap.createChild({
41082             tag: 'iframe',
41083             id: this.frameId,
41084             name: this.frameId,
41085             frameBorder : 'no',
41086             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41087         }, this.el
41088         );
41089         
41090        // console.log(iframe);
41091         //this.wrap.dom.appendChild(iframe);
41092
41093         this.iframe = iframe.dom;
41094
41095          this.assignDocWin();
41096         
41097         this.doc.designMode = 'on';
41098        
41099         this.doc.open();
41100         this.doc.write(this.getDocMarkup());
41101         this.doc.close();
41102
41103         
41104         var task = { // must defer to wait for browser to be ready
41105             run : function(){
41106                 //console.log("run task?" + this.doc.readyState);
41107                 this.assignDocWin();
41108                 if(this.doc.body || this.doc.readyState == 'complete'){
41109                     try {
41110                         this.doc.designMode="on";
41111                     } catch (e) {
41112                         return;
41113                     }
41114                     Roo.TaskMgr.stop(task);
41115                     this.initEditor.defer(10, this);
41116                 }
41117             },
41118             interval : 10,
41119             duration:10000,
41120             scope: this
41121         };
41122         Roo.TaskMgr.start(task);
41123
41124         if(!this.width){
41125             this.setSize(this.wrap.getSize());
41126         }
41127         if (this.resizeEl) {
41128             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
41129             // should trigger onReize..
41130         }
41131     },
41132
41133     // private
41134     onResize : function(w, h)
41135     {
41136         //Roo.log('resize: ' +w + ',' + h );
41137         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
41138         if(this.el && this.iframe){
41139             if(typeof w == 'number'){
41140                 var aw = w - this.wrap.getFrameWidth('lr');
41141                 this.el.setWidth(this.adjustWidth('textarea', aw));
41142                 this.iframe.style.width = aw + 'px';
41143             }
41144             if(typeof h == 'number'){
41145                 var tbh = 0;
41146                 for (var i =0; i < this.toolbars.length;i++) {
41147                     // fixme - ask toolbars for heights?
41148                     tbh += this.toolbars[i].tb.el.getHeight();
41149                     if (this.toolbars[i].footer) {
41150                         tbh += this.toolbars[i].footer.el.getHeight();
41151                     }
41152                 }
41153                 
41154                 
41155                 
41156                 
41157                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
41158                 ah -= 5; // knock a few pixes off for look..
41159                 this.el.setHeight(this.adjustWidth('textarea', ah));
41160                 this.iframe.style.height = ah + 'px';
41161                 if(this.doc){
41162                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
41163                 }
41164             }
41165         }
41166     },
41167
41168     /**
41169      * Toggles the editor between standard and source edit mode.
41170      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41171      */
41172     toggleSourceEdit : function(sourceEditMode){
41173         
41174         this.sourceEditMode = sourceEditMode === true;
41175         
41176         if(this.sourceEditMode){
41177 //            Roo.log('in');
41178 //            Roo.log(this.syncValue());
41179             this.syncValue();
41180             this.iframe.className = 'x-hidden';
41181             this.el.removeClass('x-hidden');
41182             this.el.dom.removeAttribute('tabIndex');
41183             this.el.focus();
41184         }else{
41185 //            Roo.log('out')
41186 //            Roo.log(this.pushValue()); 
41187             this.pushValue();
41188             this.iframe.className = '';
41189             this.el.addClass('x-hidden');
41190             this.el.dom.setAttribute('tabIndex', -1);
41191             this.deferFocus();
41192         }
41193         this.setSize(this.wrap.getSize());
41194         this.fireEvent('editmodechange', this, this.sourceEditMode);
41195     },
41196
41197     // private used internally
41198     createLink : function(){
41199         var url = prompt(this.createLinkText, this.defaultLinkValue);
41200         if(url && url != 'http:/'+'/'){
41201             this.relayCmd('createlink', url);
41202         }
41203     },
41204
41205     // private (for BoxComponent)
41206     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41207
41208     // private (for BoxComponent)
41209     getResizeEl : function(){
41210         return this.wrap;
41211     },
41212
41213     // private (for BoxComponent)
41214     getPositionEl : function(){
41215         return this.wrap;
41216     },
41217
41218     // private
41219     initEvents : function(){
41220         this.originalValue = this.getValue();
41221     },
41222
41223     /**
41224      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41225      * @method
41226      */
41227     markInvalid : Roo.emptyFn,
41228     /**
41229      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41230      * @method
41231      */
41232     clearInvalid : Roo.emptyFn,
41233
41234     setValue : function(v){
41235         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
41236         this.pushValue();
41237     },
41238
41239     /**
41240      * Protected method that will not generally be called directly. If you need/want
41241      * custom HTML cleanup, this is the method you should override.
41242      * @param {String} html The HTML to be cleaned
41243      * return {String} The cleaned HTML
41244      */
41245     cleanHtml : function(html){
41246         html = String(html);
41247         if(html.length > 5){
41248             if(Roo.isSafari){ // strip safari nonsense
41249                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41250             }
41251         }
41252         if(html == '&nbsp;'){
41253             html = '';
41254         }
41255         return html;
41256     },
41257
41258     /**
41259      * Protected method that will not generally be called directly. Syncs the contents
41260      * of the editor iframe with the textarea.
41261      */
41262     syncValue : function(){
41263         if(this.initialized){
41264             var bd = (this.doc.body || this.doc.documentElement);
41265             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41266             var html = bd.innerHTML;
41267             if(Roo.isSafari){
41268                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41269                 var m = bs.match(/text-align:(.*?);/i);
41270                 if(m && m[1]){
41271                     html = '<div style="'+m[0]+'">' + html + '</div>';
41272                 }
41273             }
41274             html = this.cleanHtml(html);
41275             // fix up the special chars.. normaly like back quotes in word...
41276             // however we do not want to do this with chinese..
41277             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41278                 var cc = b.charCodeAt();
41279                 if (
41280                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41281                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41282                     (cc >= 0xf900 && cc < 0xfb00 )
41283                 ) {
41284                         return b;
41285                 }
41286                 return "&#"+cc+";" 
41287             });
41288             if(this.fireEvent('beforesync', this, html) !== false){
41289                 this.el.dom.value = html;
41290                 this.fireEvent('sync', this, html);
41291             }
41292         }
41293     },
41294
41295     /**
41296      * Protected method that will not generally be called directly. Pushes the value of the textarea
41297      * into the iframe editor.
41298      */
41299     pushValue : function(){
41300         if(this.initialized){
41301             var v = this.el.dom.value;
41302             
41303             if(v.length < 1){
41304                 v = '&#160;';
41305             }
41306             
41307             if(this.fireEvent('beforepush', this, v) !== false){
41308                 var d = (this.doc.body || this.doc.documentElement);
41309                 d.innerHTML = v;
41310                 this.cleanUpPaste();
41311                 this.el.dom.value = d.innerHTML;
41312                 this.fireEvent('push', this, v);
41313             }
41314         }
41315     },
41316
41317     // private
41318     deferFocus : function(){
41319         this.focus.defer(10, this);
41320     },
41321
41322     // doc'ed in Field
41323     focus : function(){
41324         if(this.win && !this.sourceEditMode){
41325             this.win.focus();
41326         }else{
41327             this.el.focus();
41328         }
41329     },
41330     
41331     assignDocWin: function()
41332     {
41333         var iframe = this.iframe;
41334         
41335          if(Roo.isIE){
41336             this.doc = iframe.contentWindow.document;
41337             this.win = iframe.contentWindow;
41338         } else {
41339             if (!Roo.get(this.frameId)) {
41340                 return;
41341             }
41342             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41343             this.win = Roo.get(this.frameId).dom.contentWindow;
41344         }
41345     },
41346     
41347     // private
41348     initEditor : function(){
41349         //console.log("INIT EDITOR");
41350         this.assignDocWin();
41351         
41352         
41353         
41354         this.doc.designMode="on";
41355         this.doc.open();
41356         this.doc.write(this.getDocMarkup());
41357         this.doc.close();
41358         
41359         var dbody = (this.doc.body || this.doc.documentElement);
41360         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41361         // this copies styles from the containing element into thsi one..
41362         // not sure why we need all of this..
41363         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41364         ss['background-attachment'] = 'fixed'; // w3c
41365         dbody.bgProperties = 'fixed'; // ie
41366         Roo.DomHelper.applyStyles(dbody, ss);
41367         Roo.EventManager.on(this.doc, {
41368             //'mousedown': this.onEditorEvent,
41369             'mouseup': this.onEditorEvent,
41370             'dblclick': this.onEditorEvent,
41371             'click': this.onEditorEvent,
41372             'keyup': this.onEditorEvent,
41373             buffer:100,
41374             scope: this
41375         });
41376         if(Roo.isGecko){
41377             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41378         }
41379         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41380             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41381         }
41382         this.initialized = true;
41383
41384         this.fireEvent('initialize', this);
41385         this.pushValue();
41386     },
41387
41388     // private
41389     onDestroy : function(){
41390         
41391         
41392         
41393         if(this.rendered){
41394             
41395             for (var i =0; i < this.toolbars.length;i++) {
41396                 // fixme - ask toolbars for heights?
41397                 this.toolbars[i].onDestroy();
41398             }
41399             
41400             this.wrap.dom.innerHTML = '';
41401             this.wrap.remove();
41402         }
41403     },
41404
41405     // private
41406     onFirstFocus : function(){
41407         
41408         this.assignDocWin();
41409         
41410         
41411         this.activated = true;
41412         for (var i =0; i < this.toolbars.length;i++) {
41413             this.toolbars[i].onFirstFocus();
41414         }
41415        
41416         if(Roo.isGecko){ // prevent silly gecko errors
41417             this.win.focus();
41418             var s = this.win.getSelection();
41419             if(!s.focusNode || s.focusNode.nodeType != 3){
41420                 var r = s.getRangeAt(0);
41421                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41422                 r.collapse(true);
41423                 this.deferFocus();
41424             }
41425             try{
41426                 this.execCmd('useCSS', true);
41427                 this.execCmd('styleWithCSS', false);
41428             }catch(e){}
41429         }
41430         this.fireEvent('activate', this);
41431     },
41432
41433     // private
41434     adjustFont: function(btn){
41435         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41436         //if(Roo.isSafari){ // safari
41437         //    adjust *= 2;
41438        // }
41439         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41440         if(Roo.isSafari){ // safari
41441             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41442             v =  (v < 10) ? 10 : v;
41443             v =  (v > 48) ? 48 : v;
41444             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41445             
41446         }
41447         
41448         
41449         v = Math.max(1, v+adjust);
41450         
41451         this.execCmd('FontSize', v  );
41452     },
41453
41454     onEditorEvent : function(e){
41455         this.fireEvent('editorevent', this, e);
41456       //  this.updateToolbar();
41457         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41458     },
41459
41460     insertTag : function(tg)
41461     {
41462         // could be a bit smarter... -> wrap the current selected tRoo..
41463         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41464             
41465             range = this.createRange(this.getSelection());
41466             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41467             wrappingNode.appendChild(range.extractContents());
41468             range.insertNode(wrappingNode);
41469
41470             return;
41471             
41472             
41473             
41474         }
41475         this.execCmd("formatblock",   tg);
41476         
41477     },
41478     
41479     insertText : function(txt)
41480     {
41481         
41482         
41483         var range = this.createRange();
41484         range.deleteContents();
41485                //alert(Sender.getAttribute('label'));
41486                
41487         range.insertNode(this.doc.createTextNode(txt));
41488     } ,
41489     
41490     // private
41491     relayBtnCmd : function(btn){
41492         this.relayCmd(btn.cmd);
41493     },
41494
41495     /**
41496      * Executes a Midas editor command on the editor document and performs necessary focus and
41497      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41498      * @param {String} cmd The Midas command
41499      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41500      */
41501     relayCmd : function(cmd, value){
41502         this.win.focus();
41503         this.execCmd(cmd, value);
41504         this.fireEvent('editorevent', this);
41505         //this.updateToolbar();
41506         this.deferFocus();
41507     },
41508
41509     /**
41510      * Executes a Midas editor command directly on the editor document.
41511      * For visual commands, you should use {@link #relayCmd} instead.
41512      * <b>This should only be called after the editor is initialized.</b>
41513      * @param {String} cmd The Midas command
41514      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41515      */
41516     execCmd : function(cmd, value){
41517         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41518         this.syncValue();
41519     },
41520  
41521  
41522    
41523     /**
41524      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41525      * to insert tRoo.
41526      * @param {String} text | dom node.. 
41527      */
41528     insertAtCursor : function(text)
41529     {
41530         
41531         
41532         
41533         if(!this.activated){
41534             return;
41535         }
41536         /*
41537         if(Roo.isIE){
41538             this.win.focus();
41539             var r = this.doc.selection.createRange();
41540             if(r){
41541                 r.collapse(true);
41542                 r.pasteHTML(text);
41543                 this.syncValue();
41544                 this.deferFocus();
41545             
41546             }
41547             return;
41548         }
41549         */
41550         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41551             this.win.focus();
41552             
41553             
41554             // from jquery ui (MIT licenced)
41555             var range, node;
41556             var win = this.win;
41557             
41558             if (win.getSelection && win.getSelection().getRangeAt) {
41559                 range = win.getSelection().getRangeAt(0);
41560                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41561                 range.insertNode(node);
41562             } else if (win.document.selection && win.document.selection.createRange) {
41563                 // no firefox support
41564                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41565                 win.document.selection.createRange().pasteHTML(txt);
41566             } else {
41567                 // no firefox support
41568                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41569                 this.execCmd('InsertHTML', txt);
41570             } 
41571             
41572             this.syncValue();
41573             
41574             this.deferFocus();
41575         }
41576     },
41577  // private
41578     mozKeyPress : function(e){
41579         if(e.ctrlKey){
41580             var c = e.getCharCode(), cmd;
41581           
41582             if(c > 0){
41583                 c = String.fromCharCode(c).toLowerCase();
41584                 switch(c){
41585                     case 'b':
41586                         cmd = 'bold';
41587                         break;
41588                     case 'i':
41589                         cmd = 'italic';
41590                         break;
41591                     
41592                     case 'u':
41593                         cmd = 'underline';
41594                         break;
41595                     
41596                     case 'v':
41597                         this.cleanUpPaste.defer(100, this);
41598                         return;
41599                         
41600                 }
41601                 if(cmd){
41602                     this.win.focus();
41603                     this.execCmd(cmd);
41604                     this.deferFocus();
41605                     e.preventDefault();
41606                 }
41607                 
41608             }
41609         }
41610     },
41611
41612     // private
41613     fixKeys : function(){ // load time branching for fastest keydown performance
41614         if(Roo.isIE){
41615             return function(e){
41616                 var k = e.getKey(), r;
41617                 if(k == e.TAB){
41618                     e.stopEvent();
41619                     r = this.doc.selection.createRange();
41620                     if(r){
41621                         r.collapse(true);
41622                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41623                         this.deferFocus();
41624                     }
41625                     return;
41626                 }
41627                 
41628                 if(k == e.ENTER){
41629                     r = this.doc.selection.createRange();
41630                     if(r){
41631                         var target = r.parentElement();
41632                         if(!target || target.tagName.toLowerCase() != 'li'){
41633                             e.stopEvent();
41634                             r.pasteHTML('<br />');
41635                             r.collapse(false);
41636                             r.select();
41637                         }
41638                     }
41639                 }
41640                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41641                     this.cleanUpPaste.defer(100, this);
41642                     return;
41643                 }
41644                 
41645                 
41646             };
41647         }else if(Roo.isOpera){
41648             return function(e){
41649                 var k = e.getKey();
41650                 if(k == e.TAB){
41651                     e.stopEvent();
41652                     this.win.focus();
41653                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41654                     this.deferFocus();
41655                 }
41656                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41657                     this.cleanUpPaste.defer(100, this);
41658                     return;
41659                 }
41660                 
41661             };
41662         }else if(Roo.isSafari){
41663             return function(e){
41664                 var k = e.getKey();
41665                 
41666                 if(k == e.TAB){
41667                     e.stopEvent();
41668                     this.execCmd('InsertText','\t');
41669                     this.deferFocus();
41670                     return;
41671                 }
41672                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41673                     this.cleanUpPaste.defer(100, this);
41674                     return;
41675                 }
41676                 
41677              };
41678         }
41679     }(),
41680     
41681     getAllAncestors: function()
41682     {
41683         var p = this.getSelectedNode();
41684         var a = [];
41685         if (!p) {
41686             a.push(p); // push blank onto stack..
41687             p = this.getParentElement();
41688         }
41689         
41690         
41691         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41692             a.push(p);
41693             p = p.parentNode;
41694         }
41695         a.push(this.doc.body);
41696         return a;
41697     },
41698     lastSel : false,
41699     lastSelNode : false,
41700     
41701     
41702     getSelection : function() 
41703     {
41704         this.assignDocWin();
41705         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41706     },
41707     
41708     getSelectedNode: function() 
41709     {
41710         // this may only work on Gecko!!!
41711         
41712         // should we cache this!!!!
41713         
41714         
41715         
41716          
41717         var range = this.createRange(this.getSelection()).cloneRange();
41718         
41719         if (Roo.isIE) {
41720             var parent = range.parentElement();
41721             while (true) {
41722                 var testRange = range.duplicate();
41723                 testRange.moveToElementText(parent);
41724                 if (testRange.inRange(range)) {
41725                     break;
41726                 }
41727                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41728                     break;
41729                 }
41730                 parent = parent.parentElement;
41731             }
41732             return parent;
41733         }
41734         
41735         // is ancestor a text element.
41736         var ac =  range.commonAncestorContainer;
41737         if (ac.nodeType == 3) {
41738             ac = ac.parentNode;
41739         }
41740         
41741         var ar = ac.childNodes;
41742          
41743         var nodes = [];
41744         var other_nodes = [];
41745         var has_other_nodes = false;
41746         for (var i=0;i<ar.length;i++) {
41747             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41748                 continue;
41749             }
41750             // fullly contained node.
41751             
41752             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41753                 nodes.push(ar[i]);
41754                 continue;
41755             }
41756             
41757             // probably selected..
41758             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41759                 other_nodes.push(ar[i]);
41760                 continue;
41761             }
41762             // outer..
41763             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41764                 continue;
41765             }
41766             
41767             
41768             has_other_nodes = true;
41769         }
41770         if (!nodes.length && other_nodes.length) {
41771             nodes= other_nodes;
41772         }
41773         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41774             return false;
41775         }
41776         
41777         return nodes[0];
41778     },
41779     createRange: function(sel)
41780     {
41781         // this has strange effects when using with 
41782         // top toolbar - not sure if it's a great idea.
41783         //this.editor.contentWindow.focus();
41784         if (typeof sel != "undefined") {
41785             try {
41786                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41787             } catch(e) {
41788                 return this.doc.createRange();
41789             }
41790         } else {
41791             return this.doc.createRange();
41792         }
41793     },
41794     getParentElement: function()
41795     {
41796         
41797         this.assignDocWin();
41798         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41799         
41800         var range = this.createRange(sel);
41801          
41802         try {
41803             var p = range.commonAncestorContainer;
41804             while (p.nodeType == 3) { // text node
41805                 p = p.parentNode;
41806             }
41807             return p;
41808         } catch (e) {
41809             return null;
41810         }
41811     
41812     },
41813     /***
41814      *
41815      * Range intersection.. the hard stuff...
41816      *  '-1' = before
41817      *  '0' = hits..
41818      *  '1' = after.
41819      *         [ -- selected range --- ]
41820      *   [fail]                        [fail]
41821      *
41822      *    basically..
41823      *      if end is before start or  hits it. fail.
41824      *      if start is after end or hits it fail.
41825      *
41826      *   if either hits (but other is outside. - then it's not 
41827      *   
41828      *    
41829      **/
41830     
41831     
41832     // @see http://www.thismuchiknow.co.uk/?p=64.
41833     rangeIntersectsNode : function(range, node)
41834     {
41835         var nodeRange = node.ownerDocument.createRange();
41836         try {
41837             nodeRange.selectNode(node);
41838         } catch (e) {
41839             nodeRange.selectNodeContents(node);
41840         }
41841     
41842         var rangeStartRange = range.cloneRange();
41843         rangeStartRange.collapse(true);
41844     
41845         var rangeEndRange = range.cloneRange();
41846         rangeEndRange.collapse(false);
41847     
41848         var nodeStartRange = nodeRange.cloneRange();
41849         nodeStartRange.collapse(true);
41850     
41851         var nodeEndRange = nodeRange.cloneRange();
41852         nodeEndRange.collapse(false);
41853     
41854         return rangeStartRange.compareBoundaryPoints(
41855                  Range.START_TO_START, nodeEndRange) == -1 &&
41856                rangeEndRange.compareBoundaryPoints(
41857                  Range.START_TO_START, nodeStartRange) == 1;
41858         
41859          
41860     },
41861     rangeCompareNode : function(range, node)
41862     {
41863         var nodeRange = node.ownerDocument.createRange();
41864         try {
41865             nodeRange.selectNode(node);
41866         } catch (e) {
41867             nodeRange.selectNodeContents(node);
41868         }
41869         
41870         
41871         range.collapse(true);
41872     
41873         nodeRange.collapse(true);
41874      
41875         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41876         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41877          
41878         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41879         
41880         var nodeIsBefore   =  ss == 1;
41881         var nodeIsAfter    = ee == -1;
41882         
41883         if (nodeIsBefore && nodeIsAfter)
41884             return 0; // outer
41885         if (!nodeIsBefore && nodeIsAfter)
41886             return 1; //right trailed.
41887         
41888         if (nodeIsBefore && !nodeIsAfter)
41889             return 2;  // left trailed.
41890         // fully contined.
41891         return 3;
41892     },
41893
41894     // private? - in a new class?
41895     cleanUpPaste :  function()
41896     {
41897         // cleans up the whole document..
41898          Roo.log('cleanuppaste');
41899         this.cleanUpChildren(this.doc.body);
41900         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41901         if (clean != this.doc.body.innerHTML) {
41902             this.doc.body.innerHTML = clean;
41903         }
41904         
41905     },
41906     
41907     cleanWordChars : function(input) {// change the chars to hex code
41908         var he = Roo.form.HtmlEditor;
41909         
41910         var output = input;
41911         Roo.each(he.swapCodes, function(sw) { 
41912             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41913             
41914             output = output.replace(swapper, sw[1]);
41915         });
41916         
41917         return output;
41918     },
41919     
41920     
41921     cleanUpChildren : function (n)
41922     {
41923         if (!n.childNodes.length) {
41924             return;
41925         }
41926         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41927            this.cleanUpChild(n.childNodes[i]);
41928         }
41929     },
41930     
41931     
41932         
41933     
41934     cleanUpChild : function (node)
41935     {
41936         var ed = this;
41937         //console.log(node);
41938         if (node.nodeName == "#text") {
41939             // clean up silly Windows -- stuff?
41940             return; 
41941         }
41942         if (node.nodeName == "#comment") {
41943             node.parentNode.removeChild(node);
41944             // clean up silly Windows -- stuff?
41945             return; 
41946         }
41947         
41948         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41949             // remove node.
41950             node.parentNode.removeChild(node);
41951             return;
41952             
41953         }
41954         
41955         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41956         
41957         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41958         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41959         
41960         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41961         //    remove_keep_children = true;
41962         //}
41963         
41964         if (remove_keep_children) {
41965             this.cleanUpChildren(node);
41966             // inserts everything just before this node...
41967             while (node.childNodes.length) {
41968                 var cn = node.childNodes[0];
41969                 node.removeChild(cn);
41970                 node.parentNode.insertBefore(cn, node);
41971             }
41972             node.parentNode.removeChild(node);
41973             return;
41974         }
41975         
41976         if (!node.attributes || !node.attributes.length) {
41977             this.cleanUpChildren(node);
41978             return;
41979         }
41980         
41981         function cleanAttr(n,v)
41982         {
41983             
41984             if (v.match(/^\./) || v.match(/^\//)) {
41985                 return;
41986             }
41987             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41988                 return;
41989             }
41990             if (v.match(/^#/)) {
41991                 return;
41992             }
41993 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41994             node.removeAttribute(n);
41995             
41996         }
41997         
41998         function cleanStyle(n,v)
41999         {
42000             if (v.match(/expression/)) { //XSS?? should we even bother..
42001                 node.removeAttribute(n);
42002                 return;
42003             }
42004             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
42005             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
42006             
42007             
42008             var parts = v.split(/;/);
42009             var clean = [];
42010             
42011             Roo.each(parts, function(p) {
42012                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42013                 if (!p.length) {
42014                     return true;
42015                 }
42016                 var l = p.split(':').shift().replace(/\s+/g,'');
42017                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42018                 
42019                 
42020                 if ( cblack.indexOf(l) > -1) {
42021 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42022                     //node.removeAttribute(n);
42023                     return true;
42024                 }
42025                 //Roo.log()
42026                 // only allow 'c whitelisted system attributes'
42027                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42028 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42029                     //node.removeAttribute(n);
42030                     return true;
42031                 }
42032                 
42033                 
42034                  
42035                 
42036                 clean.push(p);
42037                 return true;
42038             });
42039             if (clean.length) { 
42040                 node.setAttribute(n, clean.join(';'));
42041             } else {
42042                 node.removeAttribute(n);
42043             }
42044             
42045         }
42046         
42047         
42048         for (var i = node.attributes.length-1; i > -1 ; i--) {
42049             var a = node.attributes[i];
42050             //console.log(a);
42051             
42052             if (a.name.toLowerCase().substr(0,2)=='on')  {
42053                 node.removeAttribute(a.name);
42054                 continue;
42055             }
42056             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
42057                 node.removeAttribute(a.name);
42058                 continue;
42059             }
42060             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
42061                 cleanAttr(a.name,a.value); // fixme..
42062                 continue;
42063             }
42064             if (a.name == 'style') {
42065                 cleanStyle(a.name,a.value);
42066                 continue;
42067             }
42068             /// clean up MS crap..
42069             // tecnically this should be a list of valid class'es..
42070             
42071             
42072             if (a.name == 'class') {
42073                 if (a.value.match(/^Mso/)) {
42074                     node.className = '';
42075                 }
42076                 
42077                 if (a.value.match(/body/)) {
42078                     node.className = '';
42079                 }
42080                 continue;
42081             }
42082             
42083             // style cleanup!?
42084             // class cleanup?
42085             
42086         }
42087         
42088         
42089         this.cleanUpChildren(node);
42090         
42091         
42092     }
42093     
42094     
42095     // hide stuff that is not compatible
42096     /**
42097      * @event blur
42098      * @hide
42099      */
42100     /**
42101      * @event change
42102      * @hide
42103      */
42104     /**
42105      * @event focus
42106      * @hide
42107      */
42108     /**
42109      * @event specialkey
42110      * @hide
42111      */
42112     /**
42113      * @cfg {String} fieldClass @hide
42114      */
42115     /**
42116      * @cfg {String} focusClass @hide
42117      */
42118     /**
42119      * @cfg {String} autoCreate @hide
42120      */
42121     /**
42122      * @cfg {String} inputType @hide
42123      */
42124     /**
42125      * @cfg {String} invalidClass @hide
42126      */
42127     /**
42128      * @cfg {String} invalidText @hide
42129      */
42130     /**
42131      * @cfg {String} msgFx @hide
42132      */
42133     /**
42134      * @cfg {String} validateOnBlur @hide
42135      */
42136 });
42137
42138 Roo.form.HtmlEditor.white = [
42139         'area', 'br', 'img', 'input', 'hr', 'wbr',
42140         
42141        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42142        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42143        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42144        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42145        'table',   'ul',         'xmp', 
42146        
42147        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42148       'thead',   'tr', 
42149      
42150       'dir', 'menu', 'ol', 'ul', 'dl',
42151        
42152       'embed',  'object'
42153 ];
42154
42155
42156 Roo.form.HtmlEditor.black = [
42157     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42158         'applet', // 
42159         'base',   'basefont', 'bgsound', 'blink',  'body', 
42160         'frame',  'frameset', 'head',    'html',   'ilayer', 
42161         'iframe', 'layer',  'link',     'meta',    'object',   
42162         'script', 'style' ,'title',  'xml' // clean later..
42163 ];
42164 Roo.form.HtmlEditor.clean = [
42165     'script', 'style', 'title', 'xml'
42166 ];
42167 Roo.form.HtmlEditor.remove = [
42168     'font'
42169 ];
42170 // attributes..
42171
42172 Roo.form.HtmlEditor.ablack = [
42173     'on'
42174 ];
42175     
42176 Roo.form.HtmlEditor.aclean = [ 
42177     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42178 ];
42179
42180 // protocols..
42181 Roo.form.HtmlEditor.pwhite= [
42182         'http',  'https',  'mailto'
42183 ];
42184
42185 // white listed style attributes.
42186 Roo.form.HtmlEditor.cwhite= [
42187       //  'text-align', /// default is to allow most things..
42188       
42189          
42190 //        'font-size'//??
42191 ];
42192
42193 // black listed style attributes.
42194 Roo.form.HtmlEditor.cblack= [
42195       //  'font-size' -- this can be set by the project 
42196 ];
42197
42198
42199 Roo.form.HtmlEditor.swapCodes   =[ 
42200     [    8211, "--" ], 
42201     [    8212, "--" ], 
42202     [    8216,  "'" ],  
42203     [    8217, "'" ],  
42204     [    8220, '"' ],  
42205     [    8221, '"' ],  
42206     [    8226, "*" ],  
42207     [    8230, "..." ]
42208 ]; 
42209
42210     // <script type="text/javascript">
42211 /*
42212  * Based on
42213  * Ext JS Library 1.1.1
42214  * Copyright(c) 2006-2007, Ext JS, LLC.
42215  *  
42216  
42217  */
42218
42219 /**
42220  * @class Roo.form.HtmlEditorToolbar1
42221  * Basic Toolbar
42222  * 
42223  * Usage:
42224  *
42225  new Roo.form.HtmlEditor({
42226     ....
42227     toolbars : [
42228         new Roo.form.HtmlEditorToolbar1({
42229             disable : { fonts: 1 , format: 1, ..., ... , ...],
42230             btns : [ .... ]
42231         })
42232     }
42233      
42234  * 
42235  * @cfg {Object} disable List of elements to disable..
42236  * @cfg {Array} btns List of additional buttons.
42237  * 
42238  * 
42239  * NEEDS Extra CSS? 
42240  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42241  */
42242  
42243 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42244 {
42245     
42246     Roo.apply(this, config);
42247     
42248     // default disabled, based on 'good practice'..
42249     this.disable = this.disable || {};
42250     Roo.applyIf(this.disable, {
42251         fontSize : true,
42252         colors : true,
42253         specialElements : true
42254     });
42255     
42256     
42257     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42258     // dont call parent... till later.
42259 }
42260
42261 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42262     
42263     tb: false,
42264     
42265     rendered: false,
42266     
42267     editor : false,
42268     /**
42269      * @cfg {Object} disable  List of toolbar elements to disable
42270          
42271      */
42272     disable : false,
42273       /**
42274      * @cfg {Array} fontFamilies An array of available font families
42275      */
42276     fontFamilies : [
42277         'Arial',
42278         'Courier New',
42279         'Tahoma',
42280         'Times New Roman',
42281         'Verdana'
42282     ],
42283     
42284     specialChars : [
42285            "&#169;",
42286           "&#174;",     
42287           "&#8482;",    
42288           "&#163;" ,    
42289          // "&#8212;",    
42290           "&#8230;",    
42291           "&#247;" ,    
42292         //  "&#225;" ,     ?? a acute?
42293            "&#8364;"    , //Euro
42294        //   "&#8220;"    ,
42295         //  "&#8221;"    ,
42296         //  "&#8226;"    ,
42297           "&#176;"  //   , // degrees
42298
42299          // "&#233;"     , // e ecute
42300          // "&#250;"     , // u ecute?
42301     ],
42302     
42303     specialElements : [
42304         {
42305             text: "Insert Table",
42306             xtype: 'MenuItem',
42307             xns : Roo.Menu,
42308             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42309                 
42310         },
42311         {    
42312             text: "Insert Image",
42313             xtype: 'MenuItem',
42314             xns : Roo.Menu,
42315             ihtml : '<img src="about:blank"/>'
42316             
42317         }
42318         
42319          
42320     ],
42321     
42322     
42323     inputElements : [ 
42324             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42325             "input:submit", "input:button", "select", "textarea", "label" ],
42326     formats : [
42327         ["p"] ,  
42328         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42329         ["pre"],[ "code"], 
42330         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42331         ['div'],['span']
42332     ],
42333     
42334     cleanStyles : [
42335         "font-size"
42336     ],
42337      /**
42338      * @cfg {String} defaultFont default font to use.
42339      */
42340     defaultFont: 'tahoma',
42341    
42342     fontSelect : false,
42343     
42344     
42345     formatCombo : false,
42346     
42347     init : function(editor)
42348     {
42349         this.editor = editor;
42350         
42351         
42352         var fid = editor.frameId;
42353         var etb = this;
42354         function btn(id, toggle, handler){
42355             var xid = fid + '-'+ id ;
42356             return {
42357                 id : xid,
42358                 cmd : id,
42359                 cls : 'x-btn-icon x-edit-'+id,
42360                 enableToggle:toggle !== false,
42361                 scope: editor, // was editor...
42362                 handler:handler||editor.relayBtnCmd,
42363                 clickEvent:'mousedown',
42364                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42365                 tabIndex:-1
42366             };
42367         }
42368         
42369         
42370         
42371         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42372         this.tb = tb;
42373          // stop form submits
42374         tb.el.on('click', function(e){
42375             e.preventDefault(); // what does this do?
42376         });
42377
42378         if(!this.disable.font) { // && !Roo.isSafari){
42379             /* why no safari for fonts 
42380             editor.fontSelect = tb.el.createChild({
42381                 tag:'select',
42382                 tabIndex: -1,
42383                 cls:'x-font-select',
42384                 html: this.createFontOptions()
42385             });
42386             
42387             editor.fontSelect.on('change', function(){
42388                 var font = editor.fontSelect.dom.value;
42389                 editor.relayCmd('fontname', font);
42390                 editor.deferFocus();
42391             }, editor);
42392             
42393             tb.add(
42394                 editor.fontSelect.dom,
42395                 '-'
42396             );
42397             */
42398             
42399         };
42400         if(!this.disable.formats){
42401             this.formatCombo = new Roo.form.ComboBox({
42402                 store: new Roo.data.SimpleStore({
42403                     id : 'tag',
42404                     fields: ['tag'],
42405                     data : this.formats // from states.js
42406                 }),
42407                 blockFocus : true,
42408                 name : '',
42409                 //autoCreate : {tag: "div",  size: "20"},
42410                 displayField:'tag',
42411                 typeAhead: false,
42412                 mode: 'local',
42413                 editable : false,
42414                 triggerAction: 'all',
42415                 emptyText:'Add tag',
42416                 selectOnFocus:true,
42417                 width:135,
42418                 listeners : {
42419                     'select': function(c, r, i) {
42420                         editor.insertTag(r.get('tag'));
42421                         editor.focus();
42422                     }
42423                 }
42424
42425             });
42426             tb.addField(this.formatCombo);
42427             
42428         }
42429         
42430         if(!this.disable.format){
42431             tb.add(
42432                 btn('bold'),
42433                 btn('italic'),
42434                 btn('underline')
42435             );
42436         };
42437         if(!this.disable.fontSize){
42438             tb.add(
42439                 '-',
42440                 
42441                 
42442                 btn('increasefontsize', false, editor.adjustFont),
42443                 btn('decreasefontsize', false, editor.adjustFont)
42444             );
42445         };
42446         
42447         
42448         if(!this.disable.colors){
42449             tb.add(
42450                 '-', {
42451                     id:editor.frameId +'-forecolor',
42452                     cls:'x-btn-icon x-edit-forecolor',
42453                     clickEvent:'mousedown',
42454                     tooltip: this.buttonTips['forecolor'] || undefined,
42455                     tabIndex:-1,
42456                     menu : new Roo.menu.ColorMenu({
42457                         allowReselect: true,
42458                         focus: Roo.emptyFn,
42459                         value:'000000',
42460                         plain:true,
42461                         selectHandler: function(cp, color){
42462                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42463                             editor.deferFocus();
42464                         },
42465                         scope: editor,
42466                         clickEvent:'mousedown'
42467                     })
42468                 }, {
42469                     id:editor.frameId +'backcolor',
42470                     cls:'x-btn-icon x-edit-backcolor',
42471                     clickEvent:'mousedown',
42472                     tooltip: this.buttonTips['backcolor'] || undefined,
42473                     tabIndex:-1,
42474                     menu : new Roo.menu.ColorMenu({
42475                         focus: Roo.emptyFn,
42476                         value:'FFFFFF',
42477                         plain:true,
42478                         allowReselect: true,
42479                         selectHandler: function(cp, color){
42480                             if(Roo.isGecko){
42481                                 editor.execCmd('useCSS', false);
42482                                 editor.execCmd('hilitecolor', color);
42483                                 editor.execCmd('useCSS', true);
42484                                 editor.deferFocus();
42485                             }else{
42486                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42487                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42488                                 editor.deferFocus();
42489                             }
42490                         },
42491                         scope:editor,
42492                         clickEvent:'mousedown'
42493                     })
42494                 }
42495             );
42496         };
42497         // now add all the items...
42498         
42499
42500         if(!this.disable.alignments){
42501             tb.add(
42502                 '-',
42503                 btn('justifyleft'),
42504                 btn('justifycenter'),
42505                 btn('justifyright')
42506             );
42507         };
42508
42509         //if(!Roo.isSafari){
42510             if(!this.disable.links){
42511                 tb.add(
42512                     '-',
42513                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
42514                 );
42515             };
42516
42517             if(!this.disable.lists){
42518                 tb.add(
42519                     '-',
42520                     btn('insertorderedlist'),
42521                     btn('insertunorderedlist')
42522                 );
42523             }
42524             if(!this.disable.sourceEdit){
42525                 tb.add(
42526                     '-',
42527                     btn('sourceedit', true, function(btn){
42528                         this.toggleSourceEdit(btn.pressed);
42529                     })
42530                 );
42531             }
42532         //}
42533         
42534         var smenu = { };
42535         // special menu.. - needs to be tidied up..
42536         if (!this.disable.special) {
42537             smenu = {
42538                 text: "&#169;",
42539                 cls: 'x-edit-none',
42540                 
42541                 menu : {
42542                     items : []
42543                 }
42544             };
42545             for (var i =0; i < this.specialChars.length; i++) {
42546                 smenu.menu.items.push({
42547                     
42548                     html: this.specialChars[i],
42549                     handler: function(a,b) {
42550                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42551                         //editor.insertAtCursor(a.html);
42552                         
42553                     },
42554                     tabIndex:-1
42555                 });
42556             }
42557             
42558             
42559             tb.add(smenu);
42560             
42561             
42562         }
42563         
42564         var cmenu = { };
42565         if (!this.disable.cleanStyles) {
42566             cmenu = {
42567                 cls: 'x-btn-icon x-btn-clear',
42568                 
42569                 menu : {
42570                     items : []
42571                 }
42572             };
42573             for (var i =0; i < this.cleanStyles.length; i++) {
42574                 cmenu.menu.items.push({
42575                     actiontype : this.cleanStyles[i],
42576                     html: 'Remove ' + this.cleanStyles[i],
42577                     handler: function(a,b) {
42578                         Roo.log(a);
42579                         Roo.log(b);
42580                         var c = Roo.get(editor.doc.body);
42581                         c.select('[style]').each(function(s) {
42582                             s.dom.style.removeProperty(a.actiontype);
42583                         });
42584                         
42585                     },
42586                     tabIndex:-1
42587                 });
42588             }
42589             
42590             tb.add(cmenu);
42591         }
42592          
42593         if (!this.disable.specialElements) {
42594             var semenu = {
42595                 text: "Other;",
42596                 cls: 'x-edit-none',
42597                 menu : {
42598                     items : []
42599                 }
42600             };
42601             for (var i =0; i < this.specialElements.length; i++) {
42602                 semenu.menu.items.push(
42603                     Roo.apply({ 
42604                         handler: function(a,b) {
42605                             editor.insertAtCursor(this.ihtml);
42606                         }
42607                     }, this.specialElements[i])
42608                 );
42609                     
42610             }
42611             
42612             tb.add(semenu);
42613             
42614             
42615         }
42616          
42617         
42618         if (this.btns) {
42619             for(var i =0; i< this.btns.length;i++) {
42620                 var b = Roo.factory(this.btns[i],Roo.form);
42621                 b.cls =  'x-edit-none';
42622                 b.scope = editor;
42623                 tb.add(b);
42624             }
42625         
42626         }
42627         
42628         
42629         
42630         // disable everything...
42631         
42632         this.tb.items.each(function(item){
42633            if(item.id != editor.frameId+ '-sourceedit'){
42634                 item.disable();
42635             }
42636         });
42637         this.rendered = true;
42638         
42639         // the all the btns;
42640         editor.on('editorevent', this.updateToolbar, this);
42641         // other toolbars need to implement this..
42642         //editor.on('editmodechange', this.updateToolbar, this);
42643     },
42644     
42645     
42646     
42647     /**
42648      * Protected method that will not generally be called directly. It triggers
42649      * a toolbar update by reading the markup state of the current selection in the editor.
42650      */
42651     updateToolbar: function(){
42652
42653         if(!this.editor.activated){
42654             this.editor.onFirstFocus();
42655             return;
42656         }
42657
42658         var btns = this.tb.items.map, 
42659             doc = this.editor.doc,
42660             frameId = this.editor.frameId;
42661
42662         if(!this.disable.font && !Roo.isSafari){
42663             /*
42664             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42665             if(name != this.fontSelect.dom.value){
42666                 this.fontSelect.dom.value = name;
42667             }
42668             */
42669         }
42670         if(!this.disable.format){
42671             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42672             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42673             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42674         }
42675         if(!this.disable.alignments){
42676             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42677             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42678             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42679         }
42680         if(!Roo.isSafari && !this.disable.lists){
42681             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42682             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42683         }
42684         
42685         var ans = this.editor.getAllAncestors();
42686         if (this.formatCombo) {
42687             
42688             
42689             var store = this.formatCombo.store;
42690             this.formatCombo.setValue("");
42691             for (var i =0; i < ans.length;i++) {
42692                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42693                     // select it..
42694                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42695                     break;
42696                 }
42697             }
42698         }
42699         
42700         
42701         
42702         // hides menus... - so this cant be on a menu...
42703         Roo.menu.MenuMgr.hideAll();
42704
42705         //this.editorsyncValue();
42706     },
42707    
42708     
42709     createFontOptions : function(){
42710         var buf = [], fs = this.fontFamilies, ff, lc;
42711         
42712         
42713         
42714         for(var i = 0, len = fs.length; i< len; i++){
42715             ff = fs[i];
42716             lc = ff.toLowerCase();
42717             buf.push(
42718                 '<option value="',lc,'" style="font-family:',ff,';"',
42719                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42720                     ff,
42721                 '</option>'
42722             );
42723         }
42724         return buf.join('');
42725     },
42726     
42727     toggleSourceEdit : function(sourceEditMode){
42728         if(sourceEditMode === undefined){
42729             sourceEditMode = !this.sourceEditMode;
42730         }
42731         this.sourceEditMode = sourceEditMode === true;
42732         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42733         // just toggle the button?
42734         if(btn.pressed !== this.editor.sourceEditMode){
42735             btn.toggle(this.editor.sourceEditMode);
42736             return;
42737         }
42738         
42739         if(this.sourceEditMode){
42740             this.tb.items.each(function(item){
42741                 if(item.cmd != 'sourceedit'){
42742                     item.disable();
42743                 }
42744             });
42745           
42746         }else{
42747             if(this.initialized){
42748                 this.tb.items.each(function(item){
42749                     item.enable();
42750                 });
42751             }
42752             
42753         }
42754         // tell the editor that it's been pressed..
42755         this.editor.toggleSourceEdit(sourceEditMode);
42756        
42757     },
42758      /**
42759      * Object collection of toolbar tooltips for the buttons in the editor. The key
42760      * is the command id associated with that button and the value is a valid QuickTips object.
42761      * For example:
42762 <pre><code>
42763 {
42764     bold : {
42765         title: 'Bold (Ctrl+B)',
42766         text: 'Make the selected text bold.',
42767         cls: 'x-html-editor-tip'
42768     },
42769     italic : {
42770         title: 'Italic (Ctrl+I)',
42771         text: 'Make the selected text italic.',
42772         cls: 'x-html-editor-tip'
42773     },
42774     ...
42775 </code></pre>
42776     * @type Object
42777      */
42778     buttonTips : {
42779         bold : {
42780             title: 'Bold (Ctrl+B)',
42781             text: 'Make the selected text bold.',
42782             cls: 'x-html-editor-tip'
42783         },
42784         italic : {
42785             title: 'Italic (Ctrl+I)',
42786             text: 'Make the selected text italic.',
42787             cls: 'x-html-editor-tip'
42788         },
42789         underline : {
42790             title: 'Underline (Ctrl+U)',
42791             text: 'Underline the selected text.',
42792             cls: 'x-html-editor-tip'
42793         },
42794         increasefontsize : {
42795             title: 'Grow Text',
42796             text: 'Increase the font size.',
42797             cls: 'x-html-editor-tip'
42798         },
42799         decreasefontsize : {
42800             title: 'Shrink Text',
42801             text: 'Decrease the font size.',
42802             cls: 'x-html-editor-tip'
42803         },
42804         backcolor : {
42805             title: 'Text Highlight Color',
42806             text: 'Change the background color of the selected text.',
42807             cls: 'x-html-editor-tip'
42808         },
42809         forecolor : {
42810             title: 'Font Color',
42811             text: 'Change the color of the selected text.',
42812             cls: 'x-html-editor-tip'
42813         },
42814         justifyleft : {
42815             title: 'Align Text Left',
42816             text: 'Align text to the left.',
42817             cls: 'x-html-editor-tip'
42818         },
42819         justifycenter : {
42820             title: 'Center Text',
42821             text: 'Center text in the editor.',
42822             cls: 'x-html-editor-tip'
42823         },
42824         justifyright : {
42825             title: 'Align Text Right',
42826             text: 'Align text to the right.',
42827             cls: 'x-html-editor-tip'
42828         },
42829         insertunorderedlist : {
42830             title: 'Bullet List',
42831             text: 'Start a bulleted list.',
42832             cls: 'x-html-editor-tip'
42833         },
42834         insertorderedlist : {
42835             title: 'Numbered List',
42836             text: 'Start a numbered list.',
42837             cls: 'x-html-editor-tip'
42838         },
42839         createlink : {
42840             title: 'Hyperlink',
42841             text: 'Make the selected text a hyperlink.',
42842             cls: 'x-html-editor-tip'
42843         },
42844         sourceedit : {
42845             title: 'Source Edit',
42846             text: 'Switch to source editing mode.',
42847             cls: 'x-html-editor-tip'
42848         }
42849     },
42850     // private
42851     onDestroy : function(){
42852         if(this.rendered){
42853             
42854             this.tb.items.each(function(item){
42855                 if(item.menu){
42856                     item.menu.removeAll();
42857                     if(item.menu.el){
42858                         item.menu.el.destroy();
42859                     }
42860                 }
42861                 item.destroy();
42862             });
42863              
42864         }
42865     },
42866     onFirstFocus: function() {
42867         this.tb.items.each(function(item){
42868            item.enable();
42869         });
42870     }
42871 });
42872
42873
42874
42875
42876 // <script type="text/javascript">
42877 /*
42878  * Based on
42879  * Ext JS Library 1.1.1
42880  * Copyright(c) 2006-2007, Ext JS, LLC.
42881  *  
42882  
42883  */
42884
42885  
42886 /**
42887  * @class Roo.form.HtmlEditor.ToolbarContext
42888  * Context Toolbar
42889  * 
42890  * Usage:
42891  *
42892  new Roo.form.HtmlEditor({
42893     ....
42894     toolbars : [
42895         { xtype: 'ToolbarStandard', styles : {} }
42896         { xtype: 'ToolbarContext', disable : {} }
42897     ]
42898 })
42899
42900      
42901  * 
42902  * @config : {Object} disable List of elements to disable.. (not done yet.)
42903  * @config : {Object} styles  Map of styles available.
42904  * 
42905  */
42906
42907 Roo.form.HtmlEditor.ToolbarContext = function(config)
42908 {
42909     
42910     Roo.apply(this, config);
42911     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42912     // dont call parent... till later.
42913     this.styles = this.styles || {};
42914 }
42915
42916  
42917
42918 Roo.form.HtmlEditor.ToolbarContext.types = {
42919     'IMG' : {
42920         width : {
42921             title: "Width",
42922             width: 40
42923         },
42924         height:  {
42925             title: "Height",
42926             width: 40
42927         },
42928         align: {
42929             title: "Align",
42930             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42931             width : 80
42932             
42933         },
42934         border: {
42935             title: "Border",
42936             width: 40
42937         },
42938         alt: {
42939             title: "Alt",
42940             width: 120
42941         },
42942         src : {
42943             title: "Src",
42944             width: 220
42945         }
42946         
42947     },
42948     'A' : {
42949         name : {
42950             title: "Name",
42951             width: 50
42952         },
42953         target:  {
42954             title: "Target",
42955             width: 120
42956         },
42957         href:  {
42958             title: "Href",
42959             width: 220
42960         } // border?
42961         
42962     },
42963     'TABLE' : {
42964         rows : {
42965             title: "Rows",
42966             width: 20
42967         },
42968         cols : {
42969             title: "Cols",
42970             width: 20
42971         },
42972         width : {
42973             title: "Width",
42974             width: 40
42975         },
42976         height : {
42977             title: "Height",
42978             width: 40
42979         },
42980         border : {
42981             title: "Border",
42982             width: 20
42983         }
42984     },
42985     'TD' : {
42986         width : {
42987             title: "Width",
42988             width: 40
42989         },
42990         height : {
42991             title: "Height",
42992             width: 40
42993         },   
42994         align: {
42995             title: "Align",
42996             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42997             width: 80
42998         },
42999         valign: {
43000             title: "Valign",
43001             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43002             width: 80
43003         },
43004         colspan: {
43005             title: "Colspan",
43006             width: 20
43007             
43008         },
43009          'font-family'  : {
43010             title : "Font",
43011             style : 'fontFamily',
43012             displayField: 'display',
43013             optname : 'font-family',
43014             width: 140
43015         }
43016     },
43017     'INPUT' : {
43018         name : {
43019             title: "name",
43020             width: 120
43021         },
43022         value : {
43023             title: "Value",
43024             width: 120
43025         },
43026         width : {
43027             title: "Width",
43028             width: 40
43029         }
43030     },
43031     'LABEL' : {
43032         'for' : {
43033             title: "For",
43034             width: 120
43035         }
43036     },
43037     'TEXTAREA' : {
43038           name : {
43039             title: "name",
43040             width: 120
43041         },
43042         rows : {
43043             title: "Rows",
43044             width: 20
43045         },
43046         cols : {
43047             title: "Cols",
43048             width: 20
43049         }
43050     },
43051     'SELECT' : {
43052         name : {
43053             title: "name",
43054             width: 120
43055         },
43056         selectoptions : {
43057             title: "Options",
43058             width: 200
43059         }
43060     },
43061     
43062     // should we really allow this??
43063     // should this just be 
43064     'BODY' : {
43065         title : {
43066             title: "Title",
43067             width: 200,
43068             disabled : true
43069         }
43070     },
43071     'SPAN' : {
43072         'font-family'  : {
43073             title : "Font",
43074             style : 'fontFamily',
43075             displayField: 'display',
43076             optname : 'font-family',
43077             width: 140
43078         }
43079     },
43080     'DIV' : {
43081         'font-family'  : {
43082             title : "Font",
43083             style : 'fontFamily',
43084             displayField: 'display',
43085             optname : 'font-family',
43086             width: 140
43087         }
43088     },
43089      'P' : {
43090         'font-family'  : {
43091             title : "Font",
43092             style : 'fontFamily',
43093             displayField: 'display',
43094             optname : 'font-family',
43095             width: 140
43096         }
43097     },
43098     
43099     '*' : {
43100         // empty..
43101     }
43102
43103 };
43104
43105 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43106 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43107
43108 Roo.form.HtmlEditor.ToolbarContext.options = {
43109         'font-family'  : [ 
43110                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43111                 [ 'Courier New', 'Courier New'],
43112                 [ 'Tahoma', 'Tahoma'],
43113                 [ 'Times New Roman,serif', 'Times'],
43114                 [ 'Verdana','Verdana' ]
43115         ]
43116 };
43117
43118 // fixme - these need to be configurable..
43119  
43120
43121 Roo.form.HtmlEditor.ToolbarContext.types
43122
43123
43124 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43125     
43126     tb: false,
43127     
43128     rendered: false,
43129     
43130     editor : false,
43131     /**
43132      * @cfg {Object} disable  List of toolbar elements to disable
43133          
43134      */
43135     disable : false,
43136     /**
43137      * @cfg {Object} styles List of styles 
43138      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43139      *
43140      * These must be defined in the page, so they get rendered correctly..
43141      * .headline { }
43142      * TD.underline { }
43143      * 
43144      */
43145     styles : false,
43146     
43147     options: false,
43148     
43149     toolbars : false,
43150     
43151     init : function(editor)
43152     {
43153         this.editor = editor;
43154         
43155         
43156         var fid = editor.frameId;
43157         var etb = this;
43158         function btn(id, toggle, handler){
43159             var xid = fid + '-'+ id ;
43160             return {
43161                 id : xid,
43162                 cmd : id,
43163                 cls : 'x-btn-icon x-edit-'+id,
43164                 enableToggle:toggle !== false,
43165                 scope: editor, // was editor...
43166                 handler:handler||editor.relayBtnCmd,
43167                 clickEvent:'mousedown',
43168                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43169                 tabIndex:-1
43170             };
43171         }
43172         // create a new element.
43173         var wdiv = editor.wrap.createChild({
43174                 tag: 'div'
43175             }, editor.wrap.dom.firstChild.nextSibling, true);
43176         
43177         // can we do this more than once??
43178         
43179          // stop form submits
43180       
43181  
43182         // disable everything...
43183         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43184         this.toolbars = {};
43185            
43186         for (var i in  ty) {
43187           
43188             this.toolbars[i] = this.buildToolbar(ty[i],i);
43189         }
43190         this.tb = this.toolbars.BODY;
43191         this.tb.el.show();
43192         this.buildFooter();
43193         this.footer.show();
43194         editor.on('hide', function( ) { this.footer.hide() }, this);
43195         editor.on('show', function( ) { this.footer.show() }, this);
43196         
43197          
43198         this.rendered = true;
43199         
43200         // the all the btns;
43201         editor.on('editorevent', this.updateToolbar, this);
43202         // other toolbars need to implement this..
43203         //editor.on('editmodechange', this.updateToolbar, this);
43204     },
43205     
43206     
43207     
43208     /**
43209      * Protected method that will not generally be called directly. It triggers
43210      * a toolbar update by reading the markup state of the current selection in the editor.
43211      */
43212     updateToolbar: function(editor,ev,sel){
43213
43214         //Roo.log(ev);
43215         // capture mouse up - this is handy for selecting images..
43216         // perhaps should go somewhere else...
43217         if(!this.editor.activated){
43218              this.editor.onFirstFocus();
43219             return;
43220         }
43221         
43222         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43223         // selectNode - might want to handle IE?
43224         if (ev &&
43225             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43226             ev.target && ev.target.tagName == 'IMG') {
43227             // they have click on an image...
43228             // let's see if we can change the selection...
43229             sel = ev.target;
43230          
43231               var nodeRange = sel.ownerDocument.createRange();
43232             try {
43233                 nodeRange.selectNode(sel);
43234             } catch (e) {
43235                 nodeRange.selectNodeContents(sel);
43236             }
43237             //nodeRange.collapse(true);
43238             var s = editor.win.getSelection();
43239             s.removeAllRanges();
43240             s.addRange(nodeRange);
43241         }  
43242         
43243       
43244         var updateFooter = sel ? false : true;
43245         
43246         
43247         var ans = this.editor.getAllAncestors();
43248         
43249         // pick
43250         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43251         
43252         if (!sel) { 
43253             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
43254             sel = sel ? sel : this.editor.doc.body;
43255             sel = sel.tagName.length ? sel : this.editor.doc.body;
43256             
43257         }
43258         // pick a menu that exists..
43259         var tn = sel.tagName.toUpperCase();
43260         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43261         
43262         tn = sel.tagName.toUpperCase();
43263         
43264         var lastSel = this.tb.selectedNode
43265         
43266         this.tb.selectedNode = sel;
43267         
43268         // if current menu does not match..
43269         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43270                 
43271             this.tb.el.hide();
43272             ///console.log("show: " + tn);
43273             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43274             this.tb.el.show();
43275             // update name
43276             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43277             
43278             
43279             // update attributes
43280             if (this.tb.fields) {
43281                 this.tb.fields.each(function(e) {
43282                     if (e.stylename) {
43283                         e.setValue(sel.style[e.stylename]);
43284                         return;
43285                     } 
43286                    e.setValue(sel.getAttribute(e.attrname));
43287                 });
43288             }
43289             
43290             var hasStyles = false;
43291             for(var i in this.styles) {
43292                 hasStyles = true;
43293                 break;
43294             }
43295             
43296             // update styles
43297             if (hasStyles) { 
43298                 var st = this.tb.fields.item(0);
43299                 
43300                 st.store.removeAll();
43301                
43302                 
43303                 var cn = sel.className.split(/\s+/);
43304                 
43305                 var avs = [];
43306                 if (this.styles['*']) {
43307                     
43308                     Roo.each(this.styles['*'], function(v) {
43309                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43310                     });
43311                 }
43312                 if (this.styles[tn]) { 
43313                     Roo.each(this.styles[tn], function(v) {
43314                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43315                     });
43316                 }
43317                 
43318                 st.store.loadData(avs);
43319                 st.collapse();
43320                 st.setValue(cn);
43321             }
43322             // flag our selected Node.
43323             this.tb.selectedNode = sel;
43324            
43325            
43326             Roo.menu.MenuMgr.hideAll();
43327
43328         }
43329         
43330         if (!updateFooter) {
43331             //this.footDisp.dom.innerHTML = ''; 
43332             return;
43333         }
43334         // update the footer
43335         //
43336         var html = '';
43337         
43338         this.footerEls = ans.reverse();
43339         Roo.each(this.footerEls, function(a,i) {
43340             if (!a) { return; }
43341             html += html.length ? ' &gt; '  :  '';
43342             
43343             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43344             
43345         });
43346        
43347         // 
43348         var sz = this.footDisp.up('td').getSize();
43349         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43350         this.footDisp.dom.style.marginLeft = '5px';
43351         
43352         this.footDisp.dom.style.overflow = 'hidden';
43353         
43354         this.footDisp.dom.innerHTML = html;
43355             
43356         //this.editorsyncValue();
43357     },
43358      
43359     
43360    
43361        
43362     // private
43363     onDestroy : function(){
43364         if(this.rendered){
43365             
43366             this.tb.items.each(function(item){
43367                 if(item.menu){
43368                     item.menu.removeAll();
43369                     if(item.menu.el){
43370                         item.menu.el.destroy();
43371                     }
43372                 }
43373                 item.destroy();
43374             });
43375              
43376         }
43377     },
43378     onFirstFocus: function() {
43379         // need to do this for all the toolbars..
43380         this.tb.items.each(function(item){
43381            item.enable();
43382         });
43383     },
43384     buildToolbar: function(tlist, nm)
43385     {
43386         var editor = this.editor;
43387          // create a new element.
43388         var wdiv = editor.wrap.createChild({
43389                 tag: 'div'
43390             }, editor.wrap.dom.firstChild.nextSibling, true);
43391         
43392        
43393         var tb = new Roo.Toolbar(wdiv);
43394         // add the name..
43395         
43396         tb.add(nm+ ":&nbsp;");
43397         
43398         var styles = [];
43399         for(var i in this.styles) {
43400             styles.push(i);
43401         }
43402         
43403         // styles...
43404         if (styles && styles.length) {
43405             
43406             // this needs a multi-select checkbox...
43407             tb.addField( new Roo.form.ComboBox({
43408                 store: new Roo.data.SimpleStore({
43409                     id : 'val',
43410                     fields: ['val', 'selected'],
43411                     data : [] 
43412                 }),
43413                 name : '-roo-edit-className',
43414                 attrname : 'className',
43415                 displayField: 'val',
43416                 typeAhead: false,
43417                 mode: 'local',
43418                 editable : false,
43419                 triggerAction: 'all',
43420                 emptyText:'Select Style',
43421                 selectOnFocus:true,
43422                 width: 130,
43423                 listeners : {
43424                     'select': function(c, r, i) {
43425                         // initial support only for on class per el..
43426                         tb.selectedNode.className =  r ? r.get('val') : '';
43427                         editor.syncValue();
43428                     }
43429                 }
43430     
43431             }));
43432         }
43433         
43434         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43435         var tbops = tbc.options;
43436         
43437         for (var i in tlist) {
43438             
43439             var item = tlist[i];
43440             tb.add(item.title + ":&nbsp;");
43441             
43442             
43443             //optname == used so you can configure the options available..
43444             var opts = item.opts ? item.opts : false;
43445             if (item.optname) {
43446                 opts = tbops[item.optname];
43447            
43448             }
43449             
43450             if (opts) {
43451                 // opts == pulldown..
43452                 tb.addField( new Roo.form.ComboBox({
43453                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43454                         id : 'val',
43455                         fields: ['val', 'display'],
43456                         data : opts  
43457                     }),
43458                     name : '-roo-edit-' + i,
43459                     attrname : i,
43460                     stylename : item.style ? item.style : false,
43461                     displayField: item.displayField ? item.displayField : 'val',
43462                     valueField :  'val',
43463                     typeAhead: false,
43464                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43465                     editable : false,
43466                     triggerAction: 'all',
43467                     emptyText:'Select',
43468                     selectOnFocus:true,
43469                     width: item.width ? item.width  : 130,
43470                     listeners : {
43471                         'select': function(c, r, i) {
43472                             if (c.stylename) {
43473                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43474                                 return;
43475                             }
43476                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43477                         }
43478                     }
43479
43480                 }));
43481                 continue;
43482                     
43483                  
43484                 
43485                 tb.addField( new Roo.form.TextField({
43486                     name: i,
43487                     width: 100,
43488                     //allowBlank:false,
43489                     value: ''
43490                 }));
43491                 continue;
43492             }
43493             tb.addField( new Roo.form.TextField({
43494                 name: '-roo-edit-' + i,
43495                 attrname : i,
43496                 
43497                 width: item.width,
43498                 //allowBlank:true,
43499                 value: '',
43500                 listeners: {
43501                     'change' : function(f, nv, ov) {
43502                         tb.selectedNode.setAttribute(f.attrname, nv);
43503                     }
43504                 }
43505             }));
43506              
43507         }
43508         tb.addFill();
43509         var _this = this;
43510         tb.addButton( {
43511             text: 'Remove Tag',
43512     
43513             listeners : {
43514                 click : function ()
43515                 {
43516                     // remove
43517                     // undo does not work.
43518                      
43519                     var sn = tb.selectedNode;
43520                     
43521                     var pn = sn.parentNode;
43522                     
43523                     var stn =  sn.childNodes[0];
43524                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43525                     while (sn.childNodes.length) {
43526                         var node = sn.childNodes[0];
43527                         sn.removeChild(node);
43528                         //Roo.log(node);
43529                         pn.insertBefore(node, sn);
43530                         
43531                     }
43532                     pn.removeChild(sn);
43533                     var range = editor.createRange();
43534         
43535                     range.setStart(stn,0);
43536                     range.setEnd(en,0); //????
43537                     //range.selectNode(sel);
43538                     
43539                     
43540                     var selection = editor.getSelection();
43541                     selection.removeAllRanges();
43542                     selection.addRange(range);
43543                     
43544                     
43545                     
43546                     //_this.updateToolbar(null, null, pn);
43547                     _this.updateToolbar(null, null, null);
43548                     _this.footDisp.dom.innerHTML = ''; 
43549                 }
43550             }
43551             
43552                     
43553                 
43554             
43555         });
43556         
43557         
43558         tb.el.on('click', function(e){
43559             e.preventDefault(); // what does this do?
43560         });
43561         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43562         tb.el.hide();
43563         tb.name = nm;
43564         // dont need to disable them... as they will get hidden
43565         return tb;
43566          
43567         
43568     },
43569     buildFooter : function()
43570     {
43571         
43572         var fel = this.editor.wrap.createChild();
43573         this.footer = new Roo.Toolbar(fel);
43574         // toolbar has scrolly on left / right?
43575         var footDisp= new Roo.Toolbar.Fill();
43576         var _t = this;
43577         this.footer.add(
43578             {
43579                 text : '&lt;',
43580                 xtype: 'Button',
43581                 handler : function() {
43582                     _t.footDisp.scrollTo('left',0,true)
43583                 }
43584             }
43585         );
43586         this.footer.add( footDisp );
43587         this.footer.add( 
43588             {
43589                 text : '&gt;',
43590                 xtype: 'Button',
43591                 handler : function() {
43592                     // no animation..
43593                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43594                 }
43595             }
43596         );
43597         var fel = Roo.get(footDisp.el);
43598         fel.addClass('x-editor-context');
43599         this.footDispWrap = fel; 
43600         this.footDispWrap.overflow  = 'hidden';
43601         
43602         this.footDisp = fel.createChild();
43603         this.footDispWrap.on('click', this.onContextClick, this)
43604         
43605         
43606     },
43607     onContextClick : function (ev,dom)
43608     {
43609         ev.preventDefault();
43610         var  cn = dom.className;
43611         //Roo.log(cn);
43612         if (!cn.match(/x-ed-loc-/)) {
43613             return;
43614         }
43615         var n = cn.split('-').pop();
43616         var ans = this.footerEls;
43617         var sel = ans[n];
43618         
43619          // pick
43620         var range = this.editor.createRange();
43621         
43622         range.selectNodeContents(sel);
43623         //range.selectNode(sel);
43624         
43625         
43626         var selection = this.editor.getSelection();
43627         selection.removeAllRanges();
43628         selection.addRange(range);
43629         
43630         
43631         
43632         this.updateToolbar(null, null, sel);
43633         
43634         
43635     }
43636     
43637     
43638     
43639     
43640     
43641 });
43642
43643
43644
43645
43646
43647 /*
43648  * Based on:
43649  * Ext JS Library 1.1.1
43650  * Copyright(c) 2006-2007, Ext JS, LLC.
43651  *
43652  * Originally Released Under LGPL - original licence link has changed is not relivant.
43653  *
43654  * Fork - LGPL
43655  * <script type="text/javascript">
43656  */
43657  
43658 /**
43659  * @class Roo.form.BasicForm
43660  * @extends Roo.util.Observable
43661  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43662  * @constructor
43663  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43664  * @param {Object} config Configuration options
43665  */
43666 Roo.form.BasicForm = function(el, config){
43667     this.allItems = [];
43668     this.childForms = [];
43669     Roo.apply(this, config);
43670     /*
43671      * The Roo.form.Field items in this form.
43672      * @type MixedCollection
43673      */
43674      
43675      
43676     this.items = new Roo.util.MixedCollection(false, function(o){
43677         return o.id || (o.id = Roo.id());
43678     });
43679     this.addEvents({
43680         /**
43681          * @event beforeaction
43682          * Fires before any action is performed. Return false to cancel the action.
43683          * @param {Form} this
43684          * @param {Action} action The action to be performed
43685          */
43686         beforeaction: true,
43687         /**
43688          * @event actionfailed
43689          * Fires when an action fails.
43690          * @param {Form} this
43691          * @param {Action} action The action that failed
43692          */
43693         actionfailed : true,
43694         /**
43695          * @event actioncomplete
43696          * Fires when an action is completed.
43697          * @param {Form} this
43698          * @param {Action} action The action that completed
43699          */
43700         actioncomplete : true
43701     });
43702     if(el){
43703         this.initEl(el);
43704     }
43705     Roo.form.BasicForm.superclass.constructor.call(this);
43706 };
43707
43708 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43709     /**
43710      * @cfg {String} method
43711      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43712      */
43713     /**
43714      * @cfg {DataReader} reader
43715      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43716      * This is optional as there is built-in support for processing JSON.
43717      */
43718     /**
43719      * @cfg {DataReader} errorReader
43720      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43721      * This is completely optional as there is built-in support for processing JSON.
43722      */
43723     /**
43724      * @cfg {String} url
43725      * The URL to use for form actions if one isn't supplied in the action options.
43726      */
43727     /**
43728      * @cfg {Boolean} fileUpload
43729      * Set to true if this form is a file upload.
43730      */
43731      
43732     /**
43733      * @cfg {Object} baseParams
43734      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43735      */
43736      /**
43737      
43738     /**
43739      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43740      */
43741     timeout: 30,
43742
43743     // private
43744     activeAction : null,
43745
43746     /**
43747      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43748      * or setValues() data instead of when the form was first created.
43749      */
43750     trackResetOnLoad : false,
43751     
43752     
43753     /**
43754      * childForms - used for multi-tab forms
43755      * @type {Array}
43756      */
43757     childForms : false,
43758     
43759     /**
43760      * allItems - full list of fields.
43761      * @type {Array}
43762      */
43763     allItems : false,
43764     
43765     /**
43766      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43767      * element by passing it or its id or mask the form itself by passing in true.
43768      * @type Mixed
43769      */
43770     waitMsgTarget : false,
43771
43772     // private
43773     initEl : function(el){
43774         this.el = Roo.get(el);
43775         this.id = this.el.id || Roo.id();
43776         this.el.on('submit', this.onSubmit, this);
43777         this.el.addClass('x-form');
43778     },
43779
43780     // private
43781     onSubmit : function(e){
43782         e.stopEvent();
43783     },
43784
43785     /**
43786      * Returns true if client-side validation on the form is successful.
43787      * @return Boolean
43788      */
43789     isValid : function(){
43790         var valid = true;
43791         this.items.each(function(f){
43792            if(!f.validate()){
43793                valid = false;
43794            }
43795         });
43796         return valid;
43797     },
43798
43799     /**
43800      * Returns true if any fields in this form have changed since their original load.
43801      * @return Boolean
43802      */
43803     isDirty : function(){
43804         var dirty = false;
43805         this.items.each(function(f){
43806            if(f.isDirty()){
43807                dirty = true;
43808                return false;
43809            }
43810         });
43811         return dirty;
43812     },
43813
43814     /**
43815      * Performs a predefined action (submit or load) or custom actions you define on this form.
43816      * @param {String} actionName The name of the action type
43817      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43818      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43819      * accept other config options):
43820      * <pre>
43821 Property          Type             Description
43822 ----------------  ---------------  ----------------------------------------------------------------------------------
43823 url               String           The url for the action (defaults to the form's url)
43824 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43825 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43826 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43827                                    validate the form on the client (defaults to false)
43828      * </pre>
43829      * @return {BasicForm} this
43830      */
43831     doAction : function(action, options){
43832         if(typeof action == 'string'){
43833             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43834         }
43835         if(this.fireEvent('beforeaction', this, action) !== false){
43836             this.beforeAction(action);
43837             action.run.defer(100, action);
43838         }
43839         return this;
43840     },
43841
43842     /**
43843      * Shortcut to do a submit action.
43844      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43845      * @return {BasicForm} this
43846      */
43847     submit : function(options){
43848         this.doAction('submit', options);
43849         return this;
43850     },
43851
43852     /**
43853      * Shortcut to do a load action.
43854      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43855      * @return {BasicForm} this
43856      */
43857     load : function(options){
43858         this.doAction('load', options);
43859         return this;
43860     },
43861
43862     /**
43863      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43864      * @param {Record} record The record to edit
43865      * @return {BasicForm} this
43866      */
43867     updateRecord : function(record){
43868         record.beginEdit();
43869         var fs = record.fields;
43870         fs.each(function(f){
43871             var field = this.findField(f.name);
43872             if(field){
43873                 record.set(f.name, field.getValue());
43874             }
43875         }, this);
43876         record.endEdit();
43877         return this;
43878     },
43879
43880     /**
43881      * Loads an Roo.data.Record into this form.
43882      * @param {Record} record The record to load
43883      * @return {BasicForm} this
43884      */
43885     loadRecord : function(record){
43886         this.setValues(record.data);
43887         return this;
43888     },
43889
43890     // private
43891     beforeAction : function(action){
43892         var o = action.options;
43893         
43894        
43895         if(this.waitMsgTarget === true){
43896             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43897         }else if(this.waitMsgTarget){
43898             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43899             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43900         }else {
43901             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43902         }
43903          
43904     },
43905
43906     // private
43907     afterAction : function(action, success){
43908         this.activeAction = null;
43909         var o = action.options;
43910         
43911         if(this.waitMsgTarget === true){
43912             this.el.unmask();
43913         }else if(this.waitMsgTarget){
43914             this.waitMsgTarget.unmask();
43915         }else{
43916             Roo.MessageBox.updateProgress(1);
43917             Roo.MessageBox.hide();
43918         }
43919          
43920         if(success){
43921             if(o.reset){
43922                 this.reset();
43923             }
43924             Roo.callback(o.success, o.scope, [this, action]);
43925             this.fireEvent('actioncomplete', this, action);
43926             
43927         }else{
43928             
43929             // failure condition..
43930             // we have a scenario where updates need confirming.
43931             // eg. if a locking scenario exists..
43932             // we look for { errors : { needs_confirm : true }} in the response.
43933             if (
43934                 (typeof(action.result) != 'undefined')  &&
43935                 (typeof(action.result.errors) != 'undefined')  &&
43936                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43937            ){
43938                 var _t = this;
43939                 Roo.MessageBox.confirm(
43940                     "Change requires confirmation",
43941                     action.result.errorMsg,
43942                     function(r) {
43943                         if (r != 'yes') {
43944                             return;
43945                         }
43946                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43947                     }
43948                     
43949                 );
43950                 
43951                 
43952                 
43953                 return;
43954             }
43955             
43956             Roo.callback(o.failure, o.scope, [this, action]);
43957             // show an error message if no failed handler is set..
43958             if (!this.hasListener('actionfailed')) {
43959                 Roo.MessageBox.alert("Error",
43960                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43961                         action.result.errorMsg :
43962                         "Saving Failed, please check your entries or try again"
43963                 );
43964             }
43965             
43966             this.fireEvent('actionfailed', this, action);
43967         }
43968         
43969     },
43970
43971     /**
43972      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43973      * @param {String} id The value to search for
43974      * @return Field
43975      */
43976     findField : function(id){
43977         var field = this.items.get(id);
43978         if(!field){
43979             this.items.each(function(f){
43980                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43981                     field = f;
43982                     return false;
43983                 }
43984             });
43985         }
43986         return field || null;
43987     },
43988
43989     /**
43990      * Add a secondary form to this one, 
43991      * Used to provide tabbed forms. One form is primary, with hidden values 
43992      * which mirror the elements from the other forms.
43993      * 
43994      * @param {Roo.form.Form} form to add.
43995      * 
43996      */
43997     addForm : function(form)
43998     {
43999        
44000         if (this.childForms.indexOf(form) > -1) {
44001             // already added..
44002             return;
44003         }
44004         this.childForms.push(form);
44005         var n = '';
44006         Roo.each(form.allItems, function (fe) {
44007             
44008             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44009             if (this.findField(n)) { // already added..
44010                 return;
44011             }
44012             var add = new Roo.form.Hidden({
44013                 name : n
44014             });
44015             add.render(this.el);
44016             
44017             this.add( add );
44018         }, this);
44019         
44020     },
44021     /**
44022      * Mark fields in this form invalid in bulk.
44023      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44024      * @return {BasicForm} this
44025      */
44026     markInvalid : function(errors){
44027         if(errors instanceof Array){
44028             for(var i = 0, len = errors.length; i < len; i++){
44029                 var fieldError = errors[i];
44030                 var f = this.findField(fieldError.id);
44031                 if(f){
44032                     f.markInvalid(fieldError.msg);
44033                 }
44034             }
44035         }else{
44036             var field, id;
44037             for(id in errors){
44038                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44039                     field.markInvalid(errors[id]);
44040                 }
44041             }
44042         }
44043         Roo.each(this.childForms || [], function (f) {
44044             f.markInvalid(errors);
44045         });
44046         
44047         return this;
44048     },
44049
44050     /**
44051      * Set values for fields in this form in bulk.
44052      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44053      * @return {BasicForm} this
44054      */
44055     setValues : function(values){
44056         if(values instanceof Array){ // array of objects
44057             for(var i = 0, len = values.length; i < len; i++){
44058                 var v = values[i];
44059                 var f = this.findField(v.id);
44060                 if(f){
44061                     f.setValue(v.value);
44062                     if(this.trackResetOnLoad){
44063                         f.originalValue = f.getValue();
44064                     }
44065                 }
44066             }
44067         }else{ // object hash
44068             var field, id;
44069             for(id in values){
44070                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44071                     
44072                     if (field.setFromData && 
44073                         field.valueField && 
44074                         field.displayField &&
44075                         // combos' with local stores can 
44076                         // be queried via setValue()
44077                         // to set their value..
44078                         (field.store && !field.store.isLocal)
44079                         ) {
44080                         // it's a combo
44081                         var sd = { };
44082                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44083                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44084                         field.setFromData(sd);
44085                         
44086                     } else {
44087                         field.setValue(values[id]);
44088                     }
44089                     
44090                     
44091                     if(this.trackResetOnLoad){
44092                         field.originalValue = field.getValue();
44093                     }
44094                 }
44095             }
44096         }
44097          
44098         Roo.each(this.childForms || [], function (f) {
44099             f.setValues(values);
44100         });
44101                 
44102         return this;
44103     },
44104
44105     /**
44106      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44107      * they are returned as an array.
44108      * @param {Boolean} asString
44109      * @return {Object}
44110      */
44111     getValues : function(asString){
44112         if (this.childForms) {
44113             // copy values from the child forms
44114             Roo.each(this.childForms, function (f) {
44115                 this.setValues(f.getValues());
44116             }, this);
44117         }
44118         
44119         
44120         
44121         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44122         if(asString === true){
44123             return fs;
44124         }
44125         return Roo.urlDecode(fs);
44126     },
44127     
44128     /**
44129      * Returns the fields in this form as an object with key/value pairs. 
44130      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44131      * @return {Object}
44132      */
44133     getFieldValues : function(with_hidden)
44134     {
44135         if (this.childForms) {
44136             // copy values from the child forms
44137             // should this call getFieldValues - probably not as we do not currently copy
44138             // hidden fields when we generate..
44139             Roo.each(this.childForms, function (f) {
44140                 this.setValues(f.getValues());
44141             }, this);
44142         }
44143         
44144         var ret = {};
44145         this.items.each(function(f){
44146             if (!f.getName()) {
44147                 return;
44148             }
44149             var v = f.getValue();
44150             if (f.inputType =='radio') {
44151                 if (typeof(ret[f.getName()]) == 'undefined') {
44152                     ret[f.getName()] = ''; // empty..
44153                 }
44154                 
44155                 if (!f.el.dom.checked) {
44156                     return;
44157                     
44158                 }
44159                 v = f.el.dom.value;
44160                 
44161             }
44162             
44163             // not sure if this supported any more..
44164             if ((typeof(v) == 'object') && f.getRawValue) {
44165                 v = f.getRawValue() ; // dates..
44166             }
44167             // combo boxes where name != hiddenName...
44168             if (f.name != f.getName()) {
44169                 ret[f.name] = f.getRawValue();
44170             }
44171             ret[f.getName()] = v;
44172         });
44173         
44174         return ret;
44175     },
44176
44177     /**
44178      * Clears all invalid messages in this form.
44179      * @return {BasicForm} this
44180      */
44181     clearInvalid : function(){
44182         this.items.each(function(f){
44183            f.clearInvalid();
44184         });
44185         
44186         Roo.each(this.childForms || [], function (f) {
44187             f.clearInvalid();
44188         });
44189         
44190         
44191         return this;
44192     },
44193
44194     /**
44195      * Resets this form.
44196      * @return {BasicForm} this
44197      */
44198     reset : function(){
44199         this.items.each(function(f){
44200             f.reset();
44201         });
44202         
44203         Roo.each(this.childForms || [], function (f) {
44204             f.reset();
44205         });
44206        
44207         
44208         return this;
44209     },
44210
44211     /**
44212      * Add Roo.form components to this form.
44213      * @param {Field} field1
44214      * @param {Field} field2 (optional)
44215      * @param {Field} etc (optional)
44216      * @return {BasicForm} this
44217      */
44218     add : function(){
44219         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44220         return this;
44221     },
44222
44223
44224     /**
44225      * Removes a field from the items collection (does NOT remove its markup).
44226      * @param {Field} field
44227      * @return {BasicForm} this
44228      */
44229     remove : function(field){
44230         this.items.remove(field);
44231         return this;
44232     },
44233
44234     /**
44235      * Looks at the fields in this form, checks them for an id attribute,
44236      * and calls applyTo on the existing dom element with that id.
44237      * @return {BasicForm} this
44238      */
44239     render : function(){
44240         this.items.each(function(f){
44241             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44242                 f.applyTo(f.id);
44243             }
44244         });
44245         return this;
44246     },
44247
44248     /**
44249      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44250      * @param {Object} values
44251      * @return {BasicForm} this
44252      */
44253     applyToFields : function(o){
44254         this.items.each(function(f){
44255            Roo.apply(f, o);
44256         });
44257         return this;
44258     },
44259
44260     /**
44261      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44262      * @param {Object} values
44263      * @return {BasicForm} this
44264      */
44265     applyIfToFields : function(o){
44266         this.items.each(function(f){
44267            Roo.applyIf(f, o);
44268         });
44269         return this;
44270     }
44271 });
44272
44273 // back compat
44274 Roo.BasicForm = Roo.form.BasicForm;/*
44275  * Based on:
44276  * Ext JS Library 1.1.1
44277  * Copyright(c) 2006-2007, Ext JS, LLC.
44278  *
44279  * Originally Released Under LGPL - original licence link has changed is not relivant.
44280  *
44281  * Fork - LGPL
44282  * <script type="text/javascript">
44283  */
44284
44285 /**
44286  * @class Roo.form.Form
44287  * @extends Roo.form.BasicForm
44288  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44289  * @constructor
44290  * @param {Object} config Configuration options
44291  */
44292 Roo.form.Form = function(config){
44293     var xitems =  [];
44294     if (config.items) {
44295         xitems = config.items;
44296         delete config.items;
44297     }
44298    
44299     
44300     Roo.form.Form.superclass.constructor.call(this, null, config);
44301     this.url = this.url || this.action;
44302     if(!this.root){
44303         this.root = new Roo.form.Layout(Roo.applyIf({
44304             id: Roo.id()
44305         }, config));
44306     }
44307     this.active = this.root;
44308     /**
44309      * Array of all the buttons that have been added to this form via {@link addButton}
44310      * @type Array
44311      */
44312     this.buttons = [];
44313     this.allItems = [];
44314     this.addEvents({
44315         /**
44316          * @event clientvalidation
44317          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44318          * @param {Form} this
44319          * @param {Boolean} valid true if the form has passed client-side validation
44320          */
44321         clientvalidation: true,
44322         /**
44323          * @event rendered
44324          * Fires when the form is rendered
44325          * @param {Roo.form.Form} form
44326          */
44327         rendered : true
44328     });
44329     
44330     if (this.progressUrl) {
44331             // push a hidden field onto the list of fields..
44332             this.addxtype( {
44333                     xns: Roo.form, 
44334                     xtype : 'Hidden', 
44335                     name : 'UPLOAD_IDENTIFIER' 
44336             });
44337         }
44338         
44339     
44340     Roo.each(xitems, this.addxtype, this);
44341     
44342     
44343     
44344 };
44345
44346 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44347     /**
44348      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44349      */
44350     /**
44351      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44352      */
44353     /**
44354      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44355      */
44356     buttonAlign:'center',
44357
44358     /**
44359      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44360      */
44361     minButtonWidth:75,
44362
44363     /**
44364      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44365      * This property cascades to child containers if not set.
44366      */
44367     labelAlign:'left',
44368
44369     /**
44370      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44371      * fires a looping event with that state. This is required to bind buttons to the valid
44372      * state using the config value formBind:true on the button.
44373      */
44374     monitorValid : false,
44375
44376     /**
44377      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44378      */
44379     monitorPoll : 200,
44380     
44381     /**
44382      * @cfg {String} progressUrl - Url to return progress data 
44383      */
44384     
44385     progressUrl : false,
44386   
44387     /**
44388      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44389      * fields are added and the column is closed. If no fields are passed the column remains open
44390      * until end() is called.
44391      * @param {Object} config The config to pass to the column
44392      * @param {Field} field1 (optional)
44393      * @param {Field} field2 (optional)
44394      * @param {Field} etc (optional)
44395      * @return Column The column container object
44396      */
44397     column : function(c){
44398         var col = new Roo.form.Column(c);
44399         this.start(col);
44400         if(arguments.length > 1){ // duplicate code required because of Opera
44401             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44402             this.end();
44403         }
44404         return col;
44405     },
44406
44407     /**
44408      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44409      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44410      * until end() is called.
44411      * @param {Object} config The config to pass to the fieldset
44412      * @param {Field} field1 (optional)
44413      * @param {Field} field2 (optional)
44414      * @param {Field} etc (optional)
44415      * @return FieldSet The fieldset container object
44416      */
44417     fieldset : function(c){
44418         var fs = new Roo.form.FieldSet(c);
44419         this.start(fs);
44420         if(arguments.length > 1){ // duplicate code required because of Opera
44421             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44422             this.end();
44423         }
44424         return fs;
44425     },
44426
44427     /**
44428      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44429      * fields are added and the container is closed. If no fields are passed the container remains open
44430      * until end() is called.
44431      * @param {Object} config The config to pass to the Layout
44432      * @param {Field} field1 (optional)
44433      * @param {Field} field2 (optional)
44434      * @param {Field} etc (optional)
44435      * @return Layout The container object
44436      */
44437     container : function(c){
44438         var l = new Roo.form.Layout(c);
44439         this.start(l);
44440         if(arguments.length > 1){ // duplicate code required because of Opera
44441             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44442             this.end();
44443         }
44444         return l;
44445     },
44446
44447     /**
44448      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44449      * @param {Object} container A Roo.form.Layout or subclass of Layout
44450      * @return {Form} this
44451      */
44452     start : function(c){
44453         // cascade label info
44454         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44455         this.active.stack.push(c);
44456         c.ownerCt = this.active;
44457         this.active = c;
44458         return this;
44459     },
44460
44461     /**
44462      * Closes the current open container
44463      * @return {Form} this
44464      */
44465     end : function(){
44466         if(this.active == this.root){
44467             return this;
44468         }
44469         this.active = this.active.ownerCt;
44470         return this;
44471     },
44472
44473     /**
44474      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44475      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44476      * as the label of the field.
44477      * @param {Field} field1
44478      * @param {Field} field2 (optional)
44479      * @param {Field} etc. (optional)
44480      * @return {Form} this
44481      */
44482     add : function(){
44483         this.active.stack.push.apply(this.active.stack, arguments);
44484         this.allItems.push.apply(this.allItems,arguments);
44485         var r = [];
44486         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44487             if(a[i].isFormField){
44488                 r.push(a[i]);
44489             }
44490         }
44491         if(r.length > 0){
44492             Roo.form.Form.superclass.add.apply(this, r);
44493         }
44494         return this;
44495     },
44496     
44497
44498     
44499     
44500     
44501      /**
44502      * Find any element that has been added to a form, using it's ID or name
44503      * This can include framesets, columns etc. along with regular fields..
44504      * @param {String} id - id or name to find.
44505      
44506      * @return {Element} e - or false if nothing found.
44507      */
44508     findbyId : function(id)
44509     {
44510         var ret = false;
44511         if (!id) {
44512             return ret;
44513         }
44514         Roo.each(this.allItems, function(f){
44515             if (f.id == id || f.name == id ){
44516                 ret = f;
44517                 return false;
44518             }
44519         });
44520         return ret;
44521     },
44522
44523     
44524     
44525     /**
44526      * Render this form into the passed container. This should only be called once!
44527      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44528      * @return {Form} this
44529      */
44530     render : function(ct)
44531     {
44532         
44533         
44534         
44535         ct = Roo.get(ct);
44536         var o = this.autoCreate || {
44537             tag: 'form',
44538             method : this.method || 'POST',
44539             id : this.id || Roo.id()
44540         };
44541         this.initEl(ct.createChild(o));
44542
44543         this.root.render(this.el);
44544         
44545        
44546              
44547         this.items.each(function(f){
44548             f.render('x-form-el-'+f.id);
44549         });
44550
44551         if(this.buttons.length > 0){
44552             // tables are required to maintain order and for correct IE layout
44553             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44554                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44555                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44556             }}, null, true);
44557             var tr = tb.getElementsByTagName('tr')[0];
44558             for(var i = 0, len = this.buttons.length; i < len; i++) {
44559                 var b = this.buttons[i];
44560                 var td = document.createElement('td');
44561                 td.className = 'x-form-btn-td';
44562                 b.render(tr.appendChild(td));
44563             }
44564         }
44565         if(this.monitorValid){ // initialize after render
44566             this.startMonitoring();
44567         }
44568         this.fireEvent('rendered', this);
44569         return this;
44570     },
44571
44572     /**
44573      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44574      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44575      * object or a valid Roo.DomHelper element config
44576      * @param {Function} handler The function called when the button is clicked
44577      * @param {Object} scope (optional) The scope of the handler function
44578      * @return {Roo.Button}
44579      */
44580     addButton : function(config, handler, scope){
44581         var bc = {
44582             handler: handler,
44583             scope: scope,
44584             minWidth: this.minButtonWidth,
44585             hideParent:true
44586         };
44587         if(typeof config == "string"){
44588             bc.text = config;
44589         }else{
44590             Roo.apply(bc, config);
44591         }
44592         var btn = new Roo.Button(null, bc);
44593         this.buttons.push(btn);
44594         return btn;
44595     },
44596
44597      /**
44598      * Adds a series of form elements (using the xtype property as the factory method.
44599      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44600      * @param {Object} config 
44601      */
44602     
44603     addxtype : function()
44604     {
44605         var ar = Array.prototype.slice.call(arguments, 0);
44606         var ret = false;
44607         for(var i = 0; i < ar.length; i++) {
44608             if (!ar[i]) {
44609                 continue; // skip -- if this happends something invalid got sent, we 
44610                 // should ignore it, as basically that interface element will not show up
44611                 // and that should be pretty obvious!!
44612             }
44613             
44614             if (Roo.form[ar[i].xtype]) {
44615                 ar[i].form = this;
44616                 var fe = Roo.factory(ar[i], Roo.form);
44617                 if (!ret) {
44618                     ret = fe;
44619                 }
44620                 fe.form = this;
44621                 if (fe.store) {
44622                     fe.store.form = this;
44623                 }
44624                 if (fe.isLayout) {  
44625                          
44626                     this.start(fe);
44627                     this.allItems.push(fe);
44628                     if (fe.items && fe.addxtype) {
44629                         fe.addxtype.apply(fe, fe.items);
44630                         delete fe.items;
44631                     }
44632                      this.end();
44633                     continue;
44634                 }
44635                 
44636                 
44637                  
44638                 this.add(fe);
44639               //  console.log('adding ' + ar[i].xtype);
44640             }
44641             if (ar[i].xtype == 'Button') {  
44642                 //console.log('adding button');
44643                 //console.log(ar[i]);
44644                 this.addButton(ar[i]);
44645                 this.allItems.push(fe);
44646                 continue;
44647             }
44648             
44649             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44650                 alert('end is not supported on xtype any more, use items');
44651             //    this.end();
44652             //    //console.log('adding end');
44653             }
44654             
44655         }
44656         return ret;
44657     },
44658     
44659     /**
44660      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44661      * option "monitorValid"
44662      */
44663     startMonitoring : function(){
44664         if(!this.bound){
44665             this.bound = true;
44666             Roo.TaskMgr.start({
44667                 run : this.bindHandler,
44668                 interval : this.monitorPoll || 200,
44669                 scope: this
44670             });
44671         }
44672     },
44673
44674     /**
44675      * Stops monitoring of the valid state of this form
44676      */
44677     stopMonitoring : function(){
44678         this.bound = false;
44679     },
44680
44681     // private
44682     bindHandler : function(){
44683         if(!this.bound){
44684             return false; // stops binding
44685         }
44686         var valid = true;
44687         this.items.each(function(f){
44688             if(!f.isValid(true)){
44689                 valid = false;
44690                 return false;
44691             }
44692         });
44693         for(var i = 0, len = this.buttons.length; i < len; i++){
44694             var btn = this.buttons[i];
44695             if(btn.formBind === true && btn.disabled === valid){
44696                 btn.setDisabled(!valid);
44697             }
44698         }
44699         this.fireEvent('clientvalidation', this, valid);
44700     }
44701     
44702     
44703     
44704     
44705     
44706     
44707     
44708     
44709 });
44710
44711
44712 // back compat
44713 Roo.Form = Roo.form.Form;
44714 /*
44715  * Based on:
44716  * Ext JS Library 1.1.1
44717  * Copyright(c) 2006-2007, Ext JS, LLC.
44718  *
44719  * Originally Released Under LGPL - original licence link has changed is not relivant.
44720  *
44721  * Fork - LGPL
44722  * <script type="text/javascript">
44723  */
44724
44725 // as we use this in bootstrap.
44726 Roo.namespace('Roo.form');
44727  /**
44728  * @class Roo.form.Action
44729  * Internal Class used to handle form actions
44730  * @constructor
44731  * @param {Roo.form.BasicForm} el The form element or its id
44732  * @param {Object} config Configuration options
44733  */
44734
44735  
44736  
44737 // define the action interface
44738 Roo.form.Action = function(form, options){
44739     this.form = form;
44740     this.options = options || {};
44741 };
44742 /**
44743  * Client Validation Failed
44744  * @const 
44745  */
44746 Roo.form.Action.CLIENT_INVALID = 'client';
44747 /**
44748  * Server Validation Failed
44749  * @const 
44750  */
44751 Roo.form.Action.SERVER_INVALID = 'server';
44752  /**
44753  * Connect to Server Failed
44754  * @const 
44755  */
44756 Roo.form.Action.CONNECT_FAILURE = 'connect';
44757 /**
44758  * Reading Data from Server Failed
44759  * @const 
44760  */
44761 Roo.form.Action.LOAD_FAILURE = 'load';
44762
44763 Roo.form.Action.prototype = {
44764     type : 'default',
44765     failureType : undefined,
44766     response : undefined,
44767     result : undefined,
44768
44769     // interface method
44770     run : function(options){
44771
44772     },
44773
44774     // interface method
44775     success : function(response){
44776
44777     },
44778
44779     // interface method
44780     handleResponse : function(response){
44781
44782     },
44783
44784     // default connection failure
44785     failure : function(response){
44786         
44787         this.response = response;
44788         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44789         this.form.afterAction(this, false);
44790     },
44791
44792     processResponse : function(response){
44793         this.response = response;
44794         if(!response.responseText){
44795             return true;
44796         }
44797         this.result = this.handleResponse(response);
44798         return this.result;
44799     },
44800
44801     // utility functions used internally
44802     getUrl : function(appendParams){
44803         var url = this.options.url || this.form.url || this.form.el.dom.action;
44804         if(appendParams){
44805             var p = this.getParams();
44806             if(p){
44807                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44808             }
44809         }
44810         return url;
44811     },
44812
44813     getMethod : function(){
44814         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44815     },
44816
44817     getParams : function(){
44818         var bp = this.form.baseParams;
44819         var p = this.options.params;
44820         if(p){
44821             if(typeof p == "object"){
44822                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44823             }else if(typeof p == 'string' && bp){
44824                 p += '&' + Roo.urlEncode(bp);
44825             }
44826         }else if(bp){
44827             p = Roo.urlEncode(bp);
44828         }
44829         return p;
44830     },
44831
44832     createCallback : function(){
44833         return {
44834             success: this.success,
44835             failure: this.failure,
44836             scope: this,
44837             timeout: (this.form.timeout*1000),
44838             upload: this.form.fileUpload ? this.success : undefined
44839         };
44840     }
44841 };
44842
44843 Roo.form.Action.Submit = function(form, options){
44844     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44845 };
44846
44847 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44848     type : 'submit',
44849
44850     haveProgress : false,
44851     uploadComplete : false,
44852     
44853     // uploadProgress indicator.
44854     uploadProgress : function()
44855     {
44856         if (!this.form.progressUrl) {
44857             return;
44858         }
44859         
44860         if (!this.haveProgress) {
44861             Roo.MessageBox.progress("Uploading", "Uploading");
44862         }
44863         if (this.uploadComplete) {
44864            Roo.MessageBox.hide();
44865            return;
44866         }
44867         
44868         this.haveProgress = true;
44869    
44870         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44871         
44872         var c = new Roo.data.Connection();
44873         c.request({
44874             url : this.form.progressUrl,
44875             params: {
44876                 id : uid
44877             },
44878             method: 'GET',
44879             success : function(req){
44880                //console.log(data);
44881                 var rdata = false;
44882                 var edata;
44883                 try  {
44884                    rdata = Roo.decode(req.responseText)
44885                 } catch (e) {
44886                     Roo.log("Invalid data from server..");
44887                     Roo.log(edata);
44888                     return;
44889                 }
44890                 if (!rdata || !rdata.success) {
44891                     Roo.log(rdata);
44892                     Roo.MessageBox.alert(Roo.encode(rdata));
44893                     return;
44894                 }
44895                 var data = rdata.data;
44896                 
44897                 if (this.uploadComplete) {
44898                    Roo.MessageBox.hide();
44899                    return;
44900                 }
44901                    
44902                 if (data){
44903                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44904                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44905                     );
44906                 }
44907                 this.uploadProgress.defer(2000,this);
44908             },
44909        
44910             failure: function(data) {
44911                 Roo.log('progress url failed ');
44912                 Roo.log(data);
44913             },
44914             scope : this
44915         });
44916            
44917     },
44918     
44919     
44920     run : function()
44921     {
44922         // run get Values on the form, so it syncs any secondary forms.
44923         this.form.getValues();
44924         
44925         var o = this.options;
44926         var method = this.getMethod();
44927         var isPost = method == 'POST';
44928         if(o.clientValidation === false || this.form.isValid()){
44929             
44930             if (this.form.progressUrl) {
44931                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44932                     (new Date() * 1) + '' + Math.random());
44933                     
44934             } 
44935             
44936             
44937             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44938                 form:this.form.el.dom,
44939                 url:this.getUrl(!isPost),
44940                 method: method,
44941                 params:isPost ? this.getParams() : null,
44942                 isUpload: this.form.fileUpload
44943             }));
44944             
44945             this.uploadProgress();
44946
44947         }else if (o.clientValidation !== false){ // client validation failed
44948             this.failureType = Roo.form.Action.CLIENT_INVALID;
44949             this.form.afterAction(this, false);
44950         }
44951     },
44952
44953     success : function(response)
44954     {
44955         this.uploadComplete= true;
44956         if (this.haveProgress) {
44957             Roo.MessageBox.hide();
44958         }
44959         
44960         
44961         var result = this.processResponse(response);
44962         if(result === true || result.success){
44963             this.form.afterAction(this, true);
44964             return;
44965         }
44966         if(result.errors){
44967             this.form.markInvalid(result.errors);
44968             this.failureType = Roo.form.Action.SERVER_INVALID;
44969         }
44970         this.form.afterAction(this, false);
44971     },
44972     failure : function(response)
44973     {
44974         this.uploadComplete= true;
44975         if (this.haveProgress) {
44976             Roo.MessageBox.hide();
44977         }
44978         
44979         this.response = response;
44980         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44981         this.form.afterAction(this, false);
44982     },
44983     
44984     handleResponse : function(response){
44985         if(this.form.errorReader){
44986             var rs = this.form.errorReader.read(response);
44987             var errors = [];
44988             if(rs.records){
44989                 for(var i = 0, len = rs.records.length; i < len; i++) {
44990                     var r = rs.records[i];
44991                     errors[i] = r.data;
44992                 }
44993             }
44994             if(errors.length < 1){
44995                 errors = null;
44996             }
44997             return {
44998                 success : rs.success,
44999                 errors : errors
45000             };
45001         }
45002         var ret = false;
45003         try {
45004             ret = Roo.decode(response.responseText);
45005         } catch (e) {
45006             ret = {
45007                 success: false,
45008                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45009                 errors : []
45010             };
45011         }
45012         return ret;
45013         
45014     }
45015 });
45016
45017
45018 Roo.form.Action.Load = function(form, options){
45019     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45020     this.reader = this.form.reader;
45021 };
45022
45023 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45024     type : 'load',
45025
45026     run : function(){
45027         
45028         Roo.Ajax.request(Roo.apply(
45029                 this.createCallback(), {
45030                     method:this.getMethod(),
45031                     url:this.getUrl(false),
45032                     params:this.getParams()
45033         }));
45034     },
45035
45036     success : function(response){
45037         
45038         var result = this.processResponse(response);
45039         if(result === true || !result.success || !result.data){
45040             this.failureType = Roo.form.Action.LOAD_FAILURE;
45041             this.form.afterAction(this, false);
45042             return;
45043         }
45044         this.form.clearInvalid();
45045         this.form.setValues(result.data);
45046         this.form.afterAction(this, true);
45047     },
45048
45049     handleResponse : function(response){
45050         if(this.form.reader){
45051             var rs = this.form.reader.read(response);
45052             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45053             return {
45054                 success : rs.success,
45055                 data : data
45056             };
45057         }
45058         return Roo.decode(response.responseText);
45059     }
45060 });
45061
45062 Roo.form.Action.ACTION_TYPES = {
45063     'load' : Roo.form.Action.Load,
45064     'submit' : Roo.form.Action.Submit
45065 };/*
45066  * Based on:
45067  * Ext JS Library 1.1.1
45068  * Copyright(c) 2006-2007, Ext JS, LLC.
45069  *
45070  * Originally Released Under LGPL - original licence link has changed is not relivant.
45071  *
45072  * Fork - LGPL
45073  * <script type="text/javascript">
45074  */
45075  
45076 /**
45077  * @class Roo.form.Layout
45078  * @extends Roo.Component
45079  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45080  * @constructor
45081  * @param {Object} config Configuration options
45082  */
45083 Roo.form.Layout = function(config){
45084     var xitems = [];
45085     if (config.items) {
45086         xitems = config.items;
45087         delete config.items;
45088     }
45089     Roo.form.Layout.superclass.constructor.call(this, config);
45090     this.stack = [];
45091     Roo.each(xitems, this.addxtype, this);
45092      
45093 };
45094
45095 Roo.extend(Roo.form.Layout, Roo.Component, {
45096     /**
45097      * @cfg {String/Object} autoCreate
45098      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45099      */
45100     /**
45101      * @cfg {String/Object/Function} style
45102      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45103      * a function which returns such a specification.
45104      */
45105     /**
45106      * @cfg {String} labelAlign
45107      * Valid values are "left," "top" and "right" (defaults to "left")
45108      */
45109     /**
45110      * @cfg {Number} labelWidth
45111      * Fixed width in pixels of all field labels (defaults to undefined)
45112      */
45113     /**
45114      * @cfg {Boolean} clear
45115      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45116      */
45117     clear : true,
45118     /**
45119      * @cfg {String} labelSeparator
45120      * The separator to use after field labels (defaults to ':')
45121      */
45122     labelSeparator : ':',
45123     /**
45124      * @cfg {Boolean} hideLabels
45125      * True to suppress the display of field labels in this layout (defaults to false)
45126      */
45127     hideLabels : false,
45128
45129     // private
45130     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45131     
45132     isLayout : true,
45133     
45134     // private
45135     onRender : function(ct, position){
45136         if(this.el){ // from markup
45137             this.el = Roo.get(this.el);
45138         }else {  // generate
45139             var cfg = this.getAutoCreate();
45140             this.el = ct.createChild(cfg, position);
45141         }
45142         if(this.style){
45143             this.el.applyStyles(this.style);
45144         }
45145         if(this.labelAlign){
45146             this.el.addClass('x-form-label-'+this.labelAlign);
45147         }
45148         if(this.hideLabels){
45149             this.labelStyle = "display:none";
45150             this.elementStyle = "padding-left:0;";
45151         }else{
45152             if(typeof this.labelWidth == 'number'){
45153                 this.labelStyle = "width:"+this.labelWidth+"px;";
45154                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45155             }
45156             if(this.labelAlign == 'top'){
45157                 this.labelStyle = "width:auto;";
45158                 this.elementStyle = "padding-left:0;";
45159             }
45160         }
45161         var stack = this.stack;
45162         var slen = stack.length;
45163         if(slen > 0){
45164             if(!this.fieldTpl){
45165                 var t = new Roo.Template(
45166                     '<div class="x-form-item {5}">',
45167                         '<label for="{0}" style="{2}">{1}{4}</label>',
45168                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45169                         '</div>',
45170                     '</div><div class="x-form-clear-left"></div>'
45171                 );
45172                 t.disableFormats = true;
45173                 t.compile();
45174                 Roo.form.Layout.prototype.fieldTpl = t;
45175             }
45176             for(var i = 0; i < slen; i++) {
45177                 if(stack[i].isFormField){
45178                     this.renderField(stack[i]);
45179                 }else{
45180                     this.renderComponent(stack[i]);
45181                 }
45182             }
45183         }
45184         if(this.clear){
45185             this.el.createChild({cls:'x-form-clear'});
45186         }
45187     },
45188
45189     // private
45190     renderField : function(f){
45191         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45192                f.id, //0
45193                f.fieldLabel, //1
45194                f.labelStyle||this.labelStyle||'', //2
45195                this.elementStyle||'', //3
45196                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45197                f.itemCls||this.itemCls||''  //5
45198        ], true).getPrevSibling());
45199     },
45200
45201     // private
45202     renderComponent : function(c){
45203         c.render(c.isLayout ? this.el : this.el.createChild());    
45204     },
45205     /**
45206      * Adds a object form elements (using the xtype property as the factory method.)
45207      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45208      * @param {Object} config 
45209      */
45210     addxtype : function(o)
45211     {
45212         // create the lement.
45213         o.form = this.form;
45214         var fe = Roo.factory(o, Roo.form);
45215         this.form.allItems.push(fe);
45216         this.stack.push(fe);
45217         
45218         if (fe.isFormField) {
45219             this.form.items.add(fe);
45220         }
45221          
45222         return fe;
45223     }
45224 });
45225
45226 /**
45227  * @class Roo.form.Column
45228  * @extends Roo.form.Layout
45229  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45230  * @constructor
45231  * @param {Object} config Configuration options
45232  */
45233 Roo.form.Column = function(config){
45234     Roo.form.Column.superclass.constructor.call(this, config);
45235 };
45236
45237 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45238     /**
45239      * @cfg {Number/String} width
45240      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45241      */
45242     /**
45243      * @cfg {String/Object} autoCreate
45244      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45245      */
45246
45247     // private
45248     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45249
45250     // private
45251     onRender : function(ct, position){
45252         Roo.form.Column.superclass.onRender.call(this, ct, position);
45253         if(this.width){
45254             this.el.setWidth(this.width);
45255         }
45256     }
45257 });
45258
45259
45260 /**
45261  * @class Roo.form.Row
45262  * @extends Roo.form.Layout
45263  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45264  * @constructor
45265  * @param {Object} config Configuration options
45266  */
45267
45268  
45269 Roo.form.Row = function(config){
45270     Roo.form.Row.superclass.constructor.call(this, config);
45271 };
45272  
45273 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45274       /**
45275      * @cfg {Number/String} width
45276      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45277      */
45278     /**
45279      * @cfg {Number/String} height
45280      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45281      */
45282     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45283     
45284     padWidth : 20,
45285     // private
45286     onRender : function(ct, position){
45287         //console.log('row render');
45288         if(!this.rowTpl){
45289             var t = new Roo.Template(
45290                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45291                     '<label for="{0}" style="{2}">{1}{4}</label>',
45292                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45293                     '</div>',
45294                 '</div>'
45295             );
45296             t.disableFormats = true;
45297             t.compile();
45298             Roo.form.Layout.prototype.rowTpl = t;
45299         }
45300         this.fieldTpl = this.rowTpl;
45301         
45302         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45303         var labelWidth = 100;
45304         
45305         if ((this.labelAlign != 'top')) {
45306             if (typeof this.labelWidth == 'number') {
45307                 labelWidth = this.labelWidth
45308             }
45309             this.padWidth =  20 + labelWidth;
45310             
45311         }
45312         
45313         Roo.form.Column.superclass.onRender.call(this, ct, position);
45314         if(this.width){
45315             this.el.setWidth(this.width);
45316         }
45317         if(this.height){
45318             this.el.setHeight(this.height);
45319         }
45320     },
45321     
45322     // private
45323     renderField : function(f){
45324         f.fieldEl = this.fieldTpl.append(this.el, [
45325                f.id, f.fieldLabel,
45326                f.labelStyle||this.labelStyle||'',
45327                this.elementStyle||'',
45328                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45329                f.itemCls||this.itemCls||'',
45330                f.width ? f.width + this.padWidth : 160 + this.padWidth
45331        ],true);
45332     }
45333 });
45334  
45335
45336 /**
45337  * @class Roo.form.FieldSet
45338  * @extends Roo.form.Layout
45339  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45340  * @constructor
45341  * @param {Object} config Configuration options
45342  */
45343 Roo.form.FieldSet = function(config){
45344     Roo.form.FieldSet.superclass.constructor.call(this, config);
45345 };
45346
45347 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45348     /**
45349      * @cfg {String} legend
45350      * The text to display as the legend for the FieldSet (defaults to '')
45351      */
45352     /**
45353      * @cfg {String/Object} autoCreate
45354      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45355      */
45356
45357     // private
45358     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45359
45360     // private
45361     onRender : function(ct, position){
45362         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45363         if(this.legend){
45364             this.setLegend(this.legend);
45365         }
45366     },
45367
45368     // private
45369     setLegend : function(text){
45370         if(this.rendered){
45371             this.el.child('legend').update(text);
45372         }
45373     }
45374 });/*
45375  * Based on:
45376  * Ext JS Library 1.1.1
45377  * Copyright(c) 2006-2007, Ext JS, LLC.
45378  *
45379  * Originally Released Under LGPL - original licence link has changed is not relivant.
45380  *
45381  * Fork - LGPL
45382  * <script type="text/javascript">
45383  */
45384 /**
45385  * @class Roo.form.VTypes
45386  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45387  * @singleton
45388  */
45389 Roo.form.VTypes = function(){
45390     // closure these in so they are only created once.
45391     var alpha = /^[a-zA-Z_]+$/;
45392     var alphanum = /^[a-zA-Z0-9_]+$/;
45393     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45394     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45395
45396     // All these messages and functions are configurable
45397     return {
45398         /**
45399          * The function used to validate email addresses
45400          * @param {String} value The email address
45401          */
45402         'email' : function(v){
45403             return email.test(v);
45404         },
45405         /**
45406          * The error text to display when the email validation function returns false
45407          * @type String
45408          */
45409         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45410         /**
45411          * The keystroke filter mask to be applied on email input
45412          * @type RegExp
45413          */
45414         'emailMask' : /[a-z0-9_\.\-@]/i,
45415
45416         /**
45417          * The function used to validate URLs
45418          * @param {String} value The URL
45419          */
45420         'url' : function(v){
45421             return url.test(v);
45422         },
45423         /**
45424          * The error text to display when the url validation function returns false
45425          * @type String
45426          */
45427         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45428         
45429         /**
45430          * The function used to validate alpha values
45431          * @param {String} value The value
45432          */
45433         'alpha' : function(v){
45434             return alpha.test(v);
45435         },
45436         /**
45437          * The error text to display when the alpha validation function returns false
45438          * @type String
45439          */
45440         'alphaText' : 'This field should only contain letters and _',
45441         /**
45442          * The keystroke filter mask to be applied on alpha input
45443          * @type RegExp
45444          */
45445         'alphaMask' : /[a-z_]/i,
45446
45447         /**
45448          * The function used to validate alphanumeric values
45449          * @param {String} value The value
45450          */
45451         'alphanum' : function(v){
45452             return alphanum.test(v);
45453         },
45454         /**
45455          * The error text to display when the alphanumeric validation function returns false
45456          * @type String
45457          */
45458         'alphanumText' : 'This field should only contain letters, numbers and _',
45459         /**
45460          * The keystroke filter mask to be applied on alphanumeric input
45461          * @type RegExp
45462          */
45463         'alphanumMask' : /[a-z0-9_]/i
45464     };
45465 }();//<script type="text/javascript">
45466
45467 /**
45468  * @class Roo.form.FCKeditor
45469  * @extends Roo.form.TextArea
45470  * Wrapper around the FCKEditor http://www.fckeditor.net
45471  * @constructor
45472  * Creates a new FCKeditor
45473  * @param {Object} config Configuration options
45474  */
45475 Roo.form.FCKeditor = function(config){
45476     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45477     this.addEvents({
45478          /**
45479          * @event editorinit
45480          * Fired when the editor is initialized - you can add extra handlers here..
45481          * @param {FCKeditor} this
45482          * @param {Object} the FCK object.
45483          */
45484         editorinit : true
45485     });
45486     
45487     
45488 };
45489 Roo.form.FCKeditor.editors = { };
45490 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45491 {
45492     //defaultAutoCreate : {
45493     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45494     //},
45495     // private
45496     /**
45497      * @cfg {Object} fck options - see fck manual for details.
45498      */
45499     fckconfig : false,
45500     
45501     /**
45502      * @cfg {Object} fck toolbar set (Basic or Default)
45503      */
45504     toolbarSet : 'Basic',
45505     /**
45506      * @cfg {Object} fck BasePath
45507      */ 
45508     basePath : '/fckeditor/',
45509     
45510     
45511     frame : false,
45512     
45513     value : '',
45514     
45515    
45516     onRender : function(ct, position)
45517     {
45518         if(!this.el){
45519             this.defaultAutoCreate = {
45520                 tag: "textarea",
45521                 style:"width:300px;height:60px;",
45522                 autocomplete: "off"
45523             };
45524         }
45525         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45526         /*
45527         if(this.grow){
45528             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45529             if(this.preventScrollbars){
45530                 this.el.setStyle("overflow", "hidden");
45531             }
45532             this.el.setHeight(this.growMin);
45533         }
45534         */
45535         //console.log('onrender' + this.getId() );
45536         Roo.form.FCKeditor.editors[this.getId()] = this;
45537          
45538
45539         this.replaceTextarea() ;
45540         
45541     },
45542     
45543     getEditor : function() {
45544         return this.fckEditor;
45545     },
45546     /**
45547      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45548      * @param {Mixed} value The value to set
45549      */
45550     
45551     
45552     setValue : function(value)
45553     {
45554         //console.log('setValue: ' + value);
45555         
45556         if(typeof(value) == 'undefined') { // not sure why this is happending...
45557             return;
45558         }
45559         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45560         
45561         //if(!this.el || !this.getEditor()) {
45562         //    this.value = value;
45563             //this.setValue.defer(100,this,[value]);    
45564         //    return;
45565         //} 
45566         
45567         if(!this.getEditor()) {
45568             return;
45569         }
45570         
45571         this.getEditor().SetData(value);
45572         
45573         //
45574
45575     },
45576
45577     /**
45578      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45579      * @return {Mixed} value The field value
45580      */
45581     getValue : function()
45582     {
45583         
45584         if (this.frame && this.frame.dom.style.display == 'none') {
45585             return Roo.form.FCKeditor.superclass.getValue.call(this);
45586         }
45587         
45588         if(!this.el || !this.getEditor()) {
45589            
45590            // this.getValue.defer(100,this); 
45591             return this.value;
45592         }
45593        
45594         
45595         var value=this.getEditor().GetData();
45596         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45597         return Roo.form.FCKeditor.superclass.getValue.call(this);
45598         
45599
45600     },
45601
45602     /**
45603      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45604      * @return {Mixed} value The field value
45605      */
45606     getRawValue : function()
45607     {
45608         if (this.frame && this.frame.dom.style.display == 'none') {
45609             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45610         }
45611         
45612         if(!this.el || !this.getEditor()) {
45613             //this.getRawValue.defer(100,this); 
45614             return this.value;
45615             return;
45616         }
45617         
45618         
45619         
45620         var value=this.getEditor().GetData();
45621         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45622         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45623          
45624     },
45625     
45626     setSize : function(w,h) {
45627         
45628         
45629         
45630         //if (this.frame && this.frame.dom.style.display == 'none') {
45631         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45632         //    return;
45633         //}
45634         //if(!this.el || !this.getEditor()) {
45635         //    this.setSize.defer(100,this, [w,h]); 
45636         //    return;
45637         //}
45638         
45639         
45640         
45641         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45642         
45643         this.frame.dom.setAttribute('width', w);
45644         this.frame.dom.setAttribute('height', h);
45645         this.frame.setSize(w,h);
45646         
45647     },
45648     
45649     toggleSourceEdit : function(value) {
45650         
45651       
45652          
45653         this.el.dom.style.display = value ? '' : 'none';
45654         this.frame.dom.style.display = value ?  'none' : '';
45655         
45656     },
45657     
45658     
45659     focus: function(tag)
45660     {
45661         if (this.frame.dom.style.display == 'none') {
45662             return Roo.form.FCKeditor.superclass.focus.call(this);
45663         }
45664         if(!this.el || !this.getEditor()) {
45665             this.focus.defer(100,this, [tag]); 
45666             return;
45667         }
45668         
45669         
45670         
45671         
45672         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45673         this.getEditor().Focus();
45674         if (tgs.length) {
45675             if (!this.getEditor().Selection.GetSelection()) {
45676                 this.focus.defer(100,this, [tag]); 
45677                 return;
45678             }
45679             
45680             
45681             var r = this.getEditor().EditorDocument.createRange();
45682             r.setStart(tgs[0],0);
45683             r.setEnd(tgs[0],0);
45684             this.getEditor().Selection.GetSelection().removeAllRanges();
45685             this.getEditor().Selection.GetSelection().addRange(r);
45686             this.getEditor().Focus();
45687         }
45688         
45689     },
45690     
45691     
45692     
45693     replaceTextarea : function()
45694     {
45695         if ( document.getElementById( this.getId() + '___Frame' ) )
45696             return ;
45697         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45698         //{
45699             // We must check the elements firstly using the Id and then the name.
45700         var oTextarea = document.getElementById( this.getId() );
45701         
45702         var colElementsByName = document.getElementsByName( this.getId() ) ;
45703          
45704         oTextarea.style.display = 'none' ;
45705
45706         if ( oTextarea.tabIndex ) {            
45707             this.TabIndex = oTextarea.tabIndex ;
45708         }
45709         
45710         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45711         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45712         this.frame = Roo.get(this.getId() + '___Frame')
45713     },
45714     
45715     _getConfigHtml : function()
45716     {
45717         var sConfig = '' ;
45718
45719         for ( var o in this.fckconfig ) {
45720             sConfig += sConfig.length > 0  ? '&amp;' : '';
45721             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45722         }
45723
45724         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45725     },
45726     
45727     
45728     _getIFrameHtml : function()
45729     {
45730         var sFile = 'fckeditor.html' ;
45731         /* no idea what this is about..
45732         try
45733         {
45734             if ( (/fcksource=true/i).test( window.top.location.search ) )
45735                 sFile = 'fckeditor.original.html' ;
45736         }
45737         catch (e) { 
45738         */
45739
45740         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45741         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45742         
45743         
45744         var html = '<iframe id="' + this.getId() +
45745             '___Frame" src="' + sLink +
45746             '" width="' + this.width +
45747             '" height="' + this.height + '"' +
45748             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45749             ' frameborder="0" scrolling="no"></iframe>' ;
45750
45751         return html ;
45752     },
45753     
45754     _insertHtmlBefore : function( html, element )
45755     {
45756         if ( element.insertAdjacentHTML )       {
45757             // IE
45758             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45759         } else { // Gecko
45760             var oRange = document.createRange() ;
45761             oRange.setStartBefore( element ) ;
45762             var oFragment = oRange.createContextualFragment( html );
45763             element.parentNode.insertBefore( oFragment, element ) ;
45764         }
45765     }
45766     
45767     
45768   
45769     
45770     
45771     
45772     
45773
45774 });
45775
45776 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45777
45778 function FCKeditor_OnComplete(editorInstance){
45779     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45780     f.fckEditor = editorInstance;
45781     //console.log("loaded");
45782     f.fireEvent('editorinit', f, editorInstance);
45783
45784   
45785
45786  
45787
45788
45789
45790
45791
45792
45793
45794
45795
45796
45797
45798
45799
45800
45801
45802 //<script type="text/javascript">
45803 /**
45804  * @class Roo.form.GridField
45805  * @extends Roo.form.Field
45806  * Embed a grid (or editable grid into a form)
45807  * STATUS ALPHA
45808  * 
45809  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45810  * it needs 
45811  * xgrid.store = Roo.data.Store
45812  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45813  * xgrid.store.reader = Roo.data.JsonReader 
45814  * 
45815  * 
45816  * @constructor
45817  * Creates a new GridField
45818  * @param {Object} config Configuration options
45819  */
45820 Roo.form.GridField = function(config){
45821     Roo.form.GridField.superclass.constructor.call(this, config);
45822      
45823 };
45824
45825 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45826     /**
45827      * @cfg {Number} width  - used to restrict width of grid..
45828      */
45829     width : 100,
45830     /**
45831      * @cfg {Number} height - used to restrict height of grid..
45832      */
45833     height : 50,
45834      /**
45835      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45836          * 
45837          *}
45838      */
45839     xgrid : false, 
45840     /**
45841      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45842      * {tag: "input", type: "checkbox", autocomplete: "off"})
45843      */
45844    // defaultAutoCreate : { tag: 'div' },
45845     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45846     /**
45847      * @cfg {String} addTitle Text to include for adding a title.
45848      */
45849     addTitle : false,
45850     //
45851     onResize : function(){
45852         Roo.form.Field.superclass.onResize.apply(this, arguments);
45853     },
45854
45855     initEvents : function(){
45856         // Roo.form.Checkbox.superclass.initEvents.call(this);
45857         // has no events...
45858        
45859     },
45860
45861
45862     getResizeEl : function(){
45863         return this.wrap;
45864     },
45865
45866     getPositionEl : function(){
45867         return this.wrap;
45868     },
45869
45870     // private
45871     onRender : function(ct, position){
45872         
45873         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45874         var style = this.style;
45875         delete this.style;
45876         
45877         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45878         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45879         this.viewEl = this.wrap.createChild({ tag: 'div' });
45880         if (style) {
45881             this.viewEl.applyStyles(style);
45882         }
45883         if (this.width) {
45884             this.viewEl.setWidth(this.width);
45885         }
45886         if (this.height) {
45887             this.viewEl.setHeight(this.height);
45888         }
45889         //if(this.inputValue !== undefined){
45890         //this.setValue(this.value);
45891         
45892         
45893         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45894         
45895         
45896         this.grid.render();
45897         this.grid.getDataSource().on('remove', this.refreshValue, this);
45898         this.grid.getDataSource().on('update', this.refreshValue, this);
45899         this.grid.on('afteredit', this.refreshValue, this);
45900  
45901     },
45902      
45903     
45904     /**
45905      * Sets the value of the item. 
45906      * @param {String} either an object  or a string..
45907      */
45908     setValue : function(v){
45909         //this.value = v;
45910         v = v || []; // empty set..
45911         // this does not seem smart - it really only affects memoryproxy grids..
45912         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45913             var ds = this.grid.getDataSource();
45914             // assumes a json reader..
45915             var data = {}
45916             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45917             ds.loadData( data);
45918         }
45919         // clear selection so it does not get stale.
45920         if (this.grid.sm) { 
45921             this.grid.sm.clearSelections();
45922         }
45923         
45924         Roo.form.GridField.superclass.setValue.call(this, v);
45925         this.refreshValue();
45926         // should load data in the grid really....
45927     },
45928     
45929     // private
45930     refreshValue: function() {
45931          var val = [];
45932         this.grid.getDataSource().each(function(r) {
45933             val.push(r.data);
45934         });
45935         this.el.dom.value = Roo.encode(val);
45936     }
45937     
45938      
45939     
45940     
45941 });/*
45942  * Based on:
45943  * Ext JS Library 1.1.1
45944  * Copyright(c) 2006-2007, Ext JS, LLC.
45945  *
45946  * Originally Released Under LGPL - original licence link has changed is not relivant.
45947  *
45948  * Fork - LGPL
45949  * <script type="text/javascript">
45950  */
45951 /**
45952  * @class Roo.form.DisplayField
45953  * @extends Roo.form.Field
45954  * A generic Field to display non-editable data.
45955  * @constructor
45956  * Creates a new Display Field item.
45957  * @param {Object} config Configuration options
45958  */
45959 Roo.form.DisplayField = function(config){
45960     Roo.form.DisplayField.superclass.constructor.call(this, config);
45961     
45962 };
45963
45964 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45965     inputType:      'hidden',
45966     allowBlank:     true,
45967     readOnly:         true,
45968     
45969  
45970     /**
45971      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45972      */
45973     focusClass : undefined,
45974     /**
45975      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45976      */
45977     fieldClass: 'x-form-field',
45978     
45979      /**
45980      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45981      */
45982     valueRenderer: undefined,
45983     
45984     width: 100,
45985     /**
45986      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45987      * {tag: "input", type: "checkbox", autocomplete: "off"})
45988      */
45989      
45990  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45991
45992     onResize : function(){
45993         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45994         
45995     },
45996
45997     initEvents : function(){
45998         // Roo.form.Checkbox.superclass.initEvents.call(this);
45999         // has no events...
46000        
46001     },
46002
46003
46004     getResizeEl : function(){
46005         return this.wrap;
46006     },
46007
46008     getPositionEl : function(){
46009         return this.wrap;
46010     },
46011
46012     // private
46013     onRender : function(ct, position){
46014         
46015         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46016         //if(this.inputValue !== undefined){
46017         this.wrap = this.el.wrap();
46018         
46019         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46020         
46021         if (this.bodyStyle) {
46022             this.viewEl.applyStyles(this.bodyStyle);
46023         }
46024         //this.viewEl.setStyle('padding', '2px');
46025         
46026         this.setValue(this.value);
46027         
46028     },
46029 /*
46030     // private
46031     initValue : Roo.emptyFn,
46032
46033   */
46034
46035         // private
46036     onClick : function(){
46037         
46038     },
46039
46040     /**
46041      * Sets the checked state of the checkbox.
46042      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46043      */
46044     setValue : function(v){
46045         this.value = v;
46046         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46047         // this might be called before we have a dom element..
46048         if (!this.viewEl) {
46049             return;
46050         }
46051         this.viewEl.dom.innerHTML = html;
46052         Roo.form.DisplayField.superclass.setValue.call(this, v);
46053
46054     }
46055 });/*
46056  * 
46057  * Licence- LGPL
46058  * 
46059  */
46060
46061 /**
46062  * @class Roo.form.DayPicker
46063  * @extends Roo.form.Field
46064  * A Day picker show [M] [T] [W] ....
46065  * @constructor
46066  * Creates a new Day Picker
46067  * @param {Object} config Configuration options
46068  */
46069 Roo.form.DayPicker= function(config){
46070     Roo.form.DayPicker.superclass.constructor.call(this, config);
46071      
46072 };
46073
46074 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46075     /**
46076      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46077      */
46078     focusClass : undefined,
46079     /**
46080      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46081      */
46082     fieldClass: "x-form-field",
46083    
46084     /**
46085      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46086      * {tag: "input", type: "checkbox", autocomplete: "off"})
46087      */
46088     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46089     
46090    
46091     actionMode : 'viewEl', 
46092     //
46093     // private
46094  
46095     inputType : 'hidden',
46096     
46097      
46098     inputElement: false, // real input element?
46099     basedOn: false, // ????
46100     
46101     isFormField: true, // not sure where this is needed!!!!
46102
46103     onResize : function(){
46104         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46105         if(!this.boxLabel){
46106             this.el.alignTo(this.wrap, 'c-c');
46107         }
46108     },
46109
46110     initEvents : function(){
46111         Roo.form.Checkbox.superclass.initEvents.call(this);
46112         this.el.on("click", this.onClick,  this);
46113         this.el.on("change", this.onClick,  this);
46114     },
46115
46116
46117     getResizeEl : function(){
46118         return this.wrap;
46119     },
46120
46121     getPositionEl : function(){
46122         return this.wrap;
46123     },
46124
46125     
46126     // private
46127     onRender : function(ct, position){
46128         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46129        
46130         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46131         
46132         var r1 = '<table><tr>';
46133         var r2 = '<tr class="x-form-daypick-icons">';
46134         for (var i=0; i < 7; i++) {
46135             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46136             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46137         }
46138         
46139         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46140         viewEl.select('img').on('click', this.onClick, this);
46141         this.viewEl = viewEl;   
46142         
46143         
46144         // this will not work on Chrome!!!
46145         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46146         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46147         
46148         
46149           
46150
46151     },
46152
46153     // private
46154     initValue : Roo.emptyFn,
46155
46156     /**
46157      * Returns the checked state of the checkbox.
46158      * @return {Boolean} True if checked, else false
46159      */
46160     getValue : function(){
46161         return this.el.dom.value;
46162         
46163     },
46164
46165         // private
46166     onClick : function(e){ 
46167         //this.setChecked(!this.checked);
46168         Roo.get(e.target).toggleClass('x-menu-item-checked');
46169         this.refreshValue();
46170         //if(this.el.dom.checked != this.checked){
46171         //    this.setValue(this.el.dom.checked);
46172        // }
46173     },
46174     
46175     // private
46176     refreshValue : function()
46177     {
46178         var val = '';
46179         this.viewEl.select('img',true).each(function(e,i,n)  {
46180             val += e.is(".x-menu-item-checked") ? String(n) : '';
46181         });
46182         this.setValue(val, true);
46183     },
46184
46185     /**
46186      * Sets the checked state of the checkbox.
46187      * On is always based on a string comparison between inputValue and the param.
46188      * @param {Boolean/String} value - the value to set 
46189      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46190      */
46191     setValue : function(v,suppressEvent){
46192         if (!this.el.dom) {
46193             return;
46194         }
46195         var old = this.el.dom.value ;
46196         this.el.dom.value = v;
46197         if (suppressEvent) {
46198             return ;
46199         }
46200          
46201         // update display..
46202         this.viewEl.select('img',true).each(function(e,i,n)  {
46203             
46204             var on = e.is(".x-menu-item-checked");
46205             var newv = v.indexOf(String(n)) > -1;
46206             if (on != newv) {
46207                 e.toggleClass('x-menu-item-checked');
46208             }
46209             
46210         });
46211         
46212         
46213         this.fireEvent('change', this, v, old);
46214         
46215         
46216     },
46217    
46218     // handle setting of hidden value by some other method!!?!?
46219     setFromHidden: function()
46220     {
46221         if(!this.el){
46222             return;
46223         }
46224         //console.log("SET FROM HIDDEN");
46225         //alert('setFrom hidden');
46226         this.setValue(this.el.dom.value);
46227     },
46228     
46229     onDestroy : function()
46230     {
46231         if(this.viewEl){
46232             Roo.get(this.viewEl).remove();
46233         }
46234          
46235         Roo.form.DayPicker.superclass.onDestroy.call(this);
46236     }
46237
46238 });/*
46239  * RooJS Library 1.1.1
46240  * Copyright(c) 2008-2011  Alan Knowles
46241  *
46242  * License - LGPL
46243  */
46244  
46245
46246 /**
46247  * @class Roo.form.ComboCheck
46248  * @extends Roo.form.ComboBox
46249  * A combobox for multiple select items.
46250  *
46251  * FIXME - could do with a reset button..
46252  * 
46253  * @constructor
46254  * Create a new ComboCheck
46255  * @param {Object} config Configuration options
46256  */
46257 Roo.form.ComboCheck = function(config){
46258     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46259     // should verify some data...
46260     // like
46261     // hiddenName = required..
46262     // displayField = required
46263     // valudField == required
46264     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46265     var _t = this;
46266     Roo.each(req, function(e) {
46267         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46268             throw "Roo.form.ComboCheck : missing value for: " + e;
46269         }
46270     });
46271     
46272     
46273 };
46274
46275 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46276      
46277      
46278     editable : false,
46279      
46280     selectedClass: 'x-menu-item-checked', 
46281     
46282     // private
46283     onRender : function(ct, position){
46284         var _t = this;
46285         
46286         
46287         
46288         if(!this.tpl){
46289             var cls = 'x-combo-list';
46290
46291             
46292             this.tpl =  new Roo.Template({
46293                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46294                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46295                    '<span>{' + this.displayField + '}</span>' +
46296                     '</div>' 
46297                 
46298             });
46299         }
46300  
46301         
46302         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46303         this.view.singleSelect = false;
46304         this.view.multiSelect = true;
46305         this.view.toggleSelect = true;
46306         this.pageTb.add(new Roo.Toolbar.Fill(), {
46307             
46308             text: 'Done',
46309             handler: function()
46310             {
46311                 _t.collapse();
46312             }
46313         });
46314     },
46315     
46316     onViewOver : function(e, t){
46317         // do nothing...
46318         return;
46319         
46320     },
46321     
46322     onViewClick : function(doFocus,index){
46323         return;
46324         
46325     },
46326     select: function () {
46327         //Roo.log("SELECT CALLED");
46328     },
46329      
46330     selectByValue : function(xv, scrollIntoView){
46331         var ar = this.getValueArray();
46332         var sels = [];
46333         
46334         Roo.each(ar, function(v) {
46335             if(v === undefined || v === null){
46336                 return;
46337             }
46338             var r = this.findRecord(this.valueField, v);
46339             if(r){
46340                 sels.push(this.store.indexOf(r))
46341                 
46342             }
46343         },this);
46344         this.view.select(sels);
46345         return false;
46346     },
46347     
46348     
46349     
46350     onSelect : function(record, index){
46351        // Roo.log("onselect Called");
46352        // this is only called by the clear button now..
46353         this.view.clearSelections();
46354         this.setValue('[]');
46355         if (this.value != this.valueBefore) {
46356             this.fireEvent('change', this, this.value, this.valueBefore);
46357             this.valueBefore = this.value;
46358         }
46359     },
46360     getValueArray : function()
46361     {
46362         var ar = [] ;
46363         
46364         try {
46365             //Roo.log(this.value);
46366             if (typeof(this.value) == 'undefined') {
46367                 return [];
46368             }
46369             var ar = Roo.decode(this.value);
46370             return  ar instanceof Array ? ar : []; //?? valid?
46371             
46372         } catch(e) {
46373             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46374             return [];
46375         }
46376          
46377     },
46378     expand : function ()
46379     {
46380         
46381         Roo.form.ComboCheck.superclass.expand.call(this);
46382         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46383         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46384         
46385
46386     },
46387     
46388     collapse : function(){
46389         Roo.form.ComboCheck.superclass.collapse.call(this);
46390         var sl = this.view.getSelectedIndexes();
46391         var st = this.store;
46392         var nv = [];
46393         var tv = [];
46394         var r;
46395         Roo.each(sl, function(i) {
46396             r = st.getAt(i);
46397             nv.push(r.get(this.valueField));
46398         },this);
46399         this.setValue(Roo.encode(nv));
46400         if (this.value != this.valueBefore) {
46401
46402             this.fireEvent('change', this, this.value, this.valueBefore);
46403             this.valueBefore = this.value;
46404         }
46405         
46406     },
46407     
46408     setValue : function(v){
46409         // Roo.log(v);
46410         this.value = v;
46411         
46412         var vals = this.getValueArray();
46413         var tv = [];
46414         Roo.each(vals, function(k) {
46415             var r = this.findRecord(this.valueField, k);
46416             if(r){
46417                 tv.push(r.data[this.displayField]);
46418             }else if(this.valueNotFoundText !== undefined){
46419                 tv.push( this.valueNotFoundText );
46420             }
46421         },this);
46422        // Roo.log(tv);
46423         
46424         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46425         this.hiddenField.value = v;
46426         this.value = v;
46427     }
46428     
46429 });/*
46430  * Based on:
46431  * Ext JS Library 1.1.1
46432  * Copyright(c) 2006-2007, Ext JS, LLC.
46433  *
46434  * Originally Released Under LGPL - original licence link has changed is not relivant.
46435  *
46436  * Fork - LGPL
46437  * <script type="text/javascript">
46438  */
46439  
46440 /**
46441  * @class Roo.form.Signature
46442  * @extends Roo.form.Field
46443  * Signature field.  
46444  * @constructor
46445  * 
46446  * @param {Object} config Configuration options
46447  */
46448
46449 Roo.form.Signature = function(config){
46450     Roo.form.Signature.superclass.constructor.call(this, config);
46451     
46452     this.addEvents({// not in used??
46453          /**
46454          * @event confirm
46455          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46456              * @param {Roo.form.Signature} combo This combo box
46457              */
46458         'confirm' : true,
46459         /**
46460          * @event reset
46461          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46462              * @param {Roo.form.ComboBox} combo This combo box
46463              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46464              */
46465         'reset' : true
46466     });
46467 };
46468
46469 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46470     /**
46471      * @cfg {Object} labels Label to use when rendering a form.
46472      * defaults to 
46473      * labels : { 
46474      *      clear : "Clear",
46475      *      confirm : "Confirm"
46476      *  }
46477      */
46478     labels : { 
46479         clear : "Clear",
46480         confirm : "Confirm"
46481     },
46482     /**
46483      * @cfg {Number} width The signature panel width (defaults to 300)
46484      */
46485     width: 300,
46486     /**
46487      * @cfg {Number} height The signature panel height (defaults to 100)
46488      */
46489     height : 100,
46490     /**
46491      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46492      */
46493     allowBlank : false,
46494     
46495     //private
46496     // {Object} signPanel The signature SVG panel element (defaults to {})
46497     signPanel : {},
46498     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46499     isMouseDown : false,
46500     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46501     isConfirmed : false,
46502     // {String} signatureTmp SVG mapping string (defaults to empty string)
46503     signatureTmp : '',
46504     
46505     
46506     defaultAutoCreate : { // modified by initCompnoent..
46507         tag: "input",
46508         type:"hidden"
46509     },
46510
46511     // private
46512     onRender : function(ct, position){
46513         
46514         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46515         
46516         this.wrap = this.el.wrap({
46517             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46518         });
46519         
46520         this.createToolbar(this);
46521         this.signPanel = this.wrap.createChild({
46522                 tag: 'div',
46523                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46524             }, this.el
46525         );
46526             
46527         this.svgID = Roo.id();
46528         this.svgEl = this.signPanel.createChild({
46529               xmlns : 'http://www.w3.org/2000/svg',
46530               tag : 'svg',
46531               id : this.svgID + "-svg",
46532               width: this.width,
46533               height: this.height,
46534               viewBox: '0 0 '+this.width+' '+this.height,
46535               cn : [
46536                 {
46537                     tag: "rect",
46538                     id: this.svgID + "-svg-r",
46539                     width: this.width,
46540                     height: this.height,
46541                     fill: "#ffa"
46542                 },
46543                 {
46544                     tag: "line",
46545                     id: this.svgID + "-svg-l",
46546                     x1: "0", // start
46547                     y1: (this.height*0.8), // start set the line in 80% of height
46548                     x2: this.width, // end
46549                     y2: (this.height*0.8), // end set the line in 80% of height
46550                     'stroke': "#666",
46551                     'stroke-width': "1",
46552                     'stroke-dasharray': "3",
46553                     'shape-rendering': "crispEdges",
46554                     'pointer-events': "none"
46555                 },
46556                 {
46557                     tag: "path",
46558                     id: this.svgID + "-svg-p",
46559                     'stroke': "navy",
46560                     'stroke-width': "3",
46561                     'fill': "none",
46562                     'pointer-events': 'none'
46563                 }
46564               ]
46565         });
46566         this.createSVG();
46567         this.svgBox = this.svgEl.dom.getScreenCTM();
46568     },
46569     createSVG : function(){ 
46570         var svg = this.signPanel;
46571         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46572         var t = this;
46573
46574         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46575         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46576         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46577         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46578         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46579         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46580         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46581         
46582     },
46583     isTouchEvent : function(e){
46584         return e.type.match(/^touch/);
46585     },
46586     getCoords : function (e) {
46587         var pt    = this.svgEl.dom.createSVGPoint();
46588         pt.x = e.clientX; 
46589         pt.y = e.clientY;
46590         if (this.isTouchEvent(e)) {
46591             pt.x =  e.targetTouches[0].clientX 
46592             pt.y = e.targetTouches[0].clientY;
46593         }
46594         var a = this.svgEl.dom.getScreenCTM();
46595         var b = a.inverse();
46596         var mx = pt.matrixTransform(b);
46597         return mx.x + ',' + mx.y;
46598     },
46599     //mouse event headler 
46600     down : function (e) {
46601         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46602         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46603         
46604         this.isMouseDown = true;
46605         
46606         e.preventDefault();
46607     },
46608     move : function (e) {
46609         if (this.isMouseDown) {
46610             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46611             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46612         }
46613         
46614         e.preventDefault();
46615     },
46616     up : function (e) {
46617         this.isMouseDown = false;
46618         var sp = this.signatureTmp.split(' ');
46619         
46620         if(sp.length > 1){
46621             if(!sp[sp.length-2].match(/^L/)){
46622                 sp.pop();
46623                 sp.pop();
46624                 sp.push("");
46625                 this.signatureTmp = sp.join(" ");
46626             }
46627         }
46628         if(this.getValue() != this.signatureTmp){
46629             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46630             this.isConfirmed = false;
46631         }
46632         e.preventDefault();
46633     },
46634     
46635     /**
46636      * Protected method that will not generally be called directly. It
46637      * is called when the editor creates its toolbar. Override this method if you need to
46638      * add custom toolbar buttons.
46639      * @param {HtmlEditor} editor
46640      */
46641     createToolbar : function(editor){
46642          function btn(id, toggle, handler){
46643             var xid = fid + '-'+ id ;
46644             return {
46645                 id : xid,
46646                 cmd : id,
46647                 cls : 'x-btn-icon x-edit-'+id,
46648                 enableToggle:toggle !== false,
46649                 scope: editor, // was editor...
46650                 handler:handler||editor.relayBtnCmd,
46651                 clickEvent:'mousedown',
46652                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46653                 tabIndex:-1
46654             };
46655         }
46656         
46657         
46658         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46659         this.tb = tb;
46660         this.tb.add(
46661            {
46662                 cls : ' x-signature-btn x-signature-'+id,
46663                 scope: editor, // was editor...
46664                 handler: this.reset,
46665                 clickEvent:'mousedown',
46666                 text: this.labels.clear
46667             },
46668             {
46669                  xtype : 'Fill',
46670                  xns: Roo.Toolbar
46671             }, 
46672             {
46673                 cls : '  x-signature-btn x-signature-'+id,
46674                 scope: editor, // was editor...
46675                 handler: this.confirmHandler,
46676                 clickEvent:'mousedown',
46677                 text: this.labels.confirm
46678             }
46679         );
46680     
46681     },
46682     //public
46683     /**
46684      * when user is clicked confirm then show this image.....
46685      * 
46686      * @return {String} Image Data URI
46687      */
46688     getImageDataURI : function(){
46689         var svg = this.svgEl.dom.parentNode.innerHTML;
46690         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46691         return src; 
46692     },
46693     /**
46694      * 
46695      * @return {Boolean} this.isConfirmed
46696      */
46697     getConfirmed : function(){
46698         return this.isConfirmed;
46699     },
46700     /**
46701      * 
46702      * @return {Number} this.width
46703      */
46704     getWidth : function(){
46705         return this.width;
46706     },
46707     /**
46708      * 
46709      * @return {Number} this.height
46710      */
46711     getHeight : function(){
46712         return this.height;
46713     },
46714     // private
46715     getSignature : function(){
46716         return this.signatureTmp;
46717     },
46718     // private
46719     reset : function(){
46720         this.signatureTmp = '';
46721         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46722         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46723         this.isConfirmed = false;
46724         Roo.form.Signature.superclass.reset.call(this);
46725     },
46726     setSignature : function(s){
46727         this.signatureTmp = s;
46728         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46729         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46730         this.setValue(s);
46731         this.isConfirmed = false;
46732         Roo.form.Signature.superclass.reset.call(this);
46733     }, 
46734     test : function(){
46735 //        Roo.log(this.signPanel.dom.contentWindow.up())
46736     },
46737     //private
46738     setConfirmed : function(){
46739         
46740         
46741         
46742 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46743     },
46744     // private
46745     confirmHandler : function(){
46746         if(!this.getSignature()){
46747             return;
46748         }
46749         
46750         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46751         this.setValue(this.getSignature());
46752         this.isConfirmed = true;
46753         
46754         this.fireEvent('confirm', this);
46755     },
46756     // private
46757     // Subclasses should provide the validation implementation by overriding this
46758     validateValue : function(value){
46759         if(this.allowBlank){
46760             return true;
46761         }
46762         
46763         if(this.isConfirmed){
46764             return true;
46765         }
46766         return false;
46767     }
46768 });/*
46769  * Based on:
46770  * Ext JS Library 1.1.1
46771  * Copyright(c) 2006-2007, Ext JS, LLC.
46772  *
46773  * Originally Released Under LGPL - original licence link has changed is not relivant.
46774  *
46775  * Fork - LGPL
46776  * <script type="text/javascript">
46777  */
46778  
46779
46780 /**
46781  * @class Roo.form.ComboBox
46782  * @extends Roo.form.TriggerField
46783  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46784  * @constructor
46785  * Create a new ComboBox.
46786  * @param {Object} config Configuration options
46787  */
46788 Roo.form.Select = function(config){
46789     Roo.form.Select.superclass.constructor.call(this, config);
46790      
46791 };
46792
46793 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46794     /**
46795      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46796      */
46797     /**
46798      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46799      * rendering into an Roo.Editor, defaults to false)
46800      */
46801     /**
46802      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46803      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46804      */
46805     /**
46806      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46807      */
46808     /**
46809      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46810      * the dropdown list (defaults to undefined, with no header element)
46811      */
46812
46813      /**
46814      * @cfg {String/Roo.Template} tpl The template to use to render the output
46815      */
46816      
46817     // private
46818     defaultAutoCreate : {tag: "select"  },
46819     /**
46820      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46821      */
46822     listWidth: undefined,
46823     /**
46824      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46825      * mode = 'remote' or 'text' if mode = 'local')
46826      */
46827     displayField: undefined,
46828     /**
46829      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46830      * mode = 'remote' or 'value' if mode = 'local'). 
46831      * Note: use of a valueField requires the user make a selection
46832      * in order for a value to be mapped.
46833      */
46834     valueField: undefined,
46835     
46836     
46837     /**
46838      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46839      * field's data value (defaults to the underlying DOM element's name)
46840      */
46841     hiddenName: undefined,
46842     /**
46843      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46844      */
46845     listClass: '',
46846     /**
46847      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46848      */
46849     selectedClass: 'x-combo-selected',
46850     /**
46851      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46852      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46853      * which displays a downward arrow icon).
46854      */
46855     triggerClass : 'x-form-arrow-trigger',
46856     /**
46857      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46858      */
46859     shadow:'sides',
46860     /**
46861      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46862      * anchor positions (defaults to 'tl-bl')
46863      */
46864     listAlign: 'tl-bl?',
46865     /**
46866      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46867      */
46868     maxHeight: 300,
46869     /**
46870      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46871      * query specified by the allQuery config option (defaults to 'query')
46872      */
46873     triggerAction: 'query',
46874     /**
46875      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46876      * (defaults to 4, does not apply if editable = false)
46877      */
46878     minChars : 4,
46879     /**
46880      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46881      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46882      */
46883     typeAhead: false,
46884     /**
46885      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46886      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46887      */
46888     queryDelay: 500,
46889     /**
46890      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46891      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46892      */
46893     pageSize: 0,
46894     /**
46895      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46896      * when editable = true (defaults to false)
46897      */
46898     selectOnFocus:false,
46899     /**
46900      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46901      */
46902     queryParam: 'query',
46903     /**
46904      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46905      * when mode = 'remote' (defaults to 'Loading...')
46906      */
46907     loadingText: 'Loading...',
46908     /**
46909      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46910      */
46911     resizable: false,
46912     /**
46913      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46914      */
46915     handleHeight : 8,
46916     /**
46917      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46918      * traditional select (defaults to true)
46919      */
46920     editable: true,
46921     /**
46922      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46923      */
46924     allQuery: '',
46925     /**
46926      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46927      */
46928     mode: 'remote',
46929     /**
46930      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46931      * listWidth has a higher value)
46932      */
46933     minListWidth : 70,
46934     /**
46935      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46936      * allow the user to set arbitrary text into the field (defaults to false)
46937      */
46938     forceSelection:false,
46939     /**
46940      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46941      * if typeAhead = true (defaults to 250)
46942      */
46943     typeAheadDelay : 250,
46944     /**
46945      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46946      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46947      */
46948     valueNotFoundText : undefined,
46949     
46950     /**
46951      * @cfg {String} defaultValue The value displayed after loading the store.
46952      */
46953     defaultValue: '',
46954     
46955     /**
46956      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46957      */
46958     blockFocus : false,
46959     
46960     /**
46961      * @cfg {Boolean} disableClear Disable showing of clear button.
46962      */
46963     disableClear : false,
46964     /**
46965      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46966      */
46967     alwaysQuery : false,
46968     
46969     //private
46970     addicon : false,
46971     editicon: false,
46972     
46973     // element that contains real text value.. (when hidden is used..)
46974      
46975     // private
46976     onRender : function(ct, position){
46977         Roo.form.Field.prototype.onRender.call(this, ct, position);
46978         
46979         if(this.store){
46980             this.store.on('beforeload', this.onBeforeLoad, this);
46981             this.store.on('load', this.onLoad, this);
46982             this.store.on('loadexception', this.onLoadException, this);
46983             this.store.load({});
46984         }
46985         
46986         
46987         
46988     },
46989
46990     // private
46991     initEvents : function(){
46992         //Roo.form.ComboBox.superclass.initEvents.call(this);
46993  
46994     },
46995
46996     onDestroy : function(){
46997        
46998         if(this.store){
46999             this.store.un('beforeload', this.onBeforeLoad, this);
47000             this.store.un('load', this.onLoad, this);
47001             this.store.un('loadexception', this.onLoadException, this);
47002         }
47003         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47004     },
47005
47006     // private
47007     fireKey : function(e){
47008         if(e.isNavKeyPress() && !this.list.isVisible()){
47009             this.fireEvent("specialkey", this, e);
47010         }
47011     },
47012
47013     // private
47014     onResize: function(w, h){
47015         
47016         return; 
47017     
47018         
47019     },
47020
47021     /**
47022      * Allow or prevent the user from directly editing the field text.  If false is passed,
47023      * the user will only be able to select from the items defined in the dropdown list.  This method
47024      * is the runtime equivalent of setting the 'editable' config option at config time.
47025      * @param {Boolean} value True to allow the user to directly edit the field text
47026      */
47027     setEditable : function(value){
47028          
47029     },
47030
47031     // private
47032     onBeforeLoad : function(){
47033         
47034         Roo.log("Select before load");
47035         return;
47036     
47037         this.innerList.update(this.loadingText ?
47038                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47039         //this.restrictHeight();
47040         this.selectedIndex = -1;
47041     },
47042
47043     // private
47044     onLoad : function(){
47045
47046     
47047         var dom = this.el.dom;
47048         dom.innerHTML = '';
47049          var od = dom.ownerDocument;
47050          
47051         if (this.emptyText) {
47052             var op = od.createElement('option');
47053             op.setAttribute('value', '');
47054             op.innerHTML = String.format('{0}', this.emptyText);
47055             dom.appendChild(op);
47056         }
47057         if(this.store.getCount() > 0){
47058            
47059             var vf = this.valueField;
47060             var df = this.displayField;
47061             this.store.data.each(function(r) {
47062                 // which colmsn to use... testing - cdoe / title..
47063                 var op = od.createElement('option');
47064                 op.setAttribute('value', r.data[vf]);
47065                 op.innerHTML = String.format('{0}', r.data[df]);
47066                 dom.appendChild(op);
47067             });
47068             if (typeof(this.defaultValue != 'undefined')) {
47069                 this.setValue(this.defaultValue);
47070             }
47071             
47072              
47073         }else{
47074             //this.onEmptyResults();
47075         }
47076         //this.el.focus();
47077     },
47078     // private
47079     onLoadException : function()
47080     {
47081         dom.innerHTML = '';
47082             
47083         Roo.log("Select on load exception");
47084         return;
47085     
47086         this.collapse();
47087         Roo.log(this.store.reader.jsonData);
47088         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47089             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47090         }
47091         
47092         
47093     },
47094     // private
47095     onTypeAhead : function(){
47096          
47097     },
47098
47099     // private
47100     onSelect : function(record, index){
47101         Roo.log('on select?');
47102         return;
47103         if(this.fireEvent('beforeselect', this, record, index) !== false){
47104             this.setFromData(index > -1 ? record.data : false);
47105             this.collapse();
47106             this.fireEvent('select', this, record, index);
47107         }
47108     },
47109
47110     /**
47111      * Returns the currently selected field value or empty string if no value is set.
47112      * @return {String} value The selected value
47113      */
47114     getValue : function(){
47115         var dom = this.el.dom;
47116         this.value = dom.options[dom.selectedIndex].value;
47117         return this.value;
47118         
47119     },
47120
47121     /**
47122      * Clears any text/value currently set in the field
47123      */
47124     clearValue : function(){
47125         this.value = '';
47126         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47127         
47128     },
47129
47130     /**
47131      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47132      * will be displayed in the field.  If the value does not match the data value of an existing item,
47133      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47134      * Otherwise the field will be blank (although the value will still be set).
47135      * @param {String} value The value to match
47136      */
47137     setValue : function(v){
47138         var d = this.el.dom;
47139         for (var i =0; i < d.options.length;i++) {
47140             if (v == d.options[i].value) {
47141                 d.selectedIndex = i;
47142                 this.value = v;
47143                 return;
47144             }
47145         }
47146         this.clearValue();
47147     },
47148     /**
47149      * @property {Object} the last set data for the element
47150      */
47151     
47152     lastData : false,
47153     /**
47154      * Sets the value of the field based on a object which is related to the record format for the store.
47155      * @param {Object} value the value to set as. or false on reset?
47156      */
47157     setFromData : function(o){
47158         Roo.log('setfrom data?');
47159          
47160         
47161         
47162     },
47163     // private
47164     reset : function(){
47165         this.clearValue();
47166     },
47167     // private
47168     findRecord : function(prop, value){
47169         
47170         return false;
47171     
47172         var record;
47173         if(this.store.getCount() > 0){
47174             this.store.each(function(r){
47175                 if(r.data[prop] == value){
47176                     record = r;
47177                     return false;
47178                 }
47179                 return true;
47180             });
47181         }
47182         return record;
47183     },
47184     
47185     getName: function()
47186     {
47187         // returns hidden if it's set..
47188         if (!this.rendered) {return ''};
47189         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47190         
47191     },
47192      
47193
47194     
47195
47196     // private
47197     onEmptyResults : function(){
47198         Roo.log('empty results');
47199         //this.collapse();
47200     },
47201
47202     /**
47203      * Returns true if the dropdown list is expanded, else false.
47204      */
47205     isExpanded : function(){
47206         return false;
47207     },
47208
47209     /**
47210      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47211      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47212      * @param {String} value The data value of the item to select
47213      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47214      * selected item if it is not currently in view (defaults to true)
47215      * @return {Boolean} True if the value matched an item in the list, else false
47216      */
47217     selectByValue : function(v, scrollIntoView){
47218         Roo.log('select By Value');
47219         return false;
47220     
47221         if(v !== undefined && v !== null){
47222             var r = this.findRecord(this.valueField || this.displayField, v);
47223             if(r){
47224                 this.select(this.store.indexOf(r), scrollIntoView);
47225                 return true;
47226             }
47227         }
47228         return false;
47229     },
47230
47231     /**
47232      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47233      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47234      * @param {Number} index The zero-based index of the list item to select
47235      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47236      * selected item if it is not currently in view (defaults to true)
47237      */
47238     select : function(index, scrollIntoView){
47239         Roo.log('select ');
47240         return  ;
47241         
47242         this.selectedIndex = index;
47243         this.view.select(index);
47244         if(scrollIntoView !== false){
47245             var el = this.view.getNode(index);
47246             if(el){
47247                 this.innerList.scrollChildIntoView(el, false);
47248             }
47249         }
47250     },
47251
47252       
47253
47254     // private
47255     validateBlur : function(){
47256         
47257         return;
47258         
47259     },
47260
47261     // private
47262     initQuery : function(){
47263         this.doQuery(this.getRawValue());
47264     },
47265
47266     // private
47267     doForce : function(){
47268         if(this.el.dom.value.length > 0){
47269             this.el.dom.value =
47270                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47271              
47272         }
47273     },
47274
47275     /**
47276      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47277      * query allowing the query action to be canceled if needed.
47278      * @param {String} query The SQL query to execute
47279      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47280      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47281      * saved in the current store (defaults to false)
47282      */
47283     doQuery : function(q, forceAll){
47284         
47285         Roo.log('doQuery?');
47286         if(q === undefined || q === null){
47287             q = '';
47288         }
47289         var qe = {
47290             query: q,
47291             forceAll: forceAll,
47292             combo: this,
47293             cancel:false
47294         };
47295         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47296             return false;
47297         }
47298         q = qe.query;
47299         forceAll = qe.forceAll;
47300         if(forceAll === true || (q.length >= this.minChars)){
47301             if(this.lastQuery != q || this.alwaysQuery){
47302                 this.lastQuery = q;
47303                 if(this.mode == 'local'){
47304                     this.selectedIndex = -1;
47305                     if(forceAll){
47306                         this.store.clearFilter();
47307                     }else{
47308                         this.store.filter(this.displayField, q);
47309                     }
47310                     this.onLoad();
47311                 }else{
47312                     this.store.baseParams[this.queryParam] = q;
47313                     this.store.load({
47314                         params: this.getParams(q)
47315                     });
47316                     this.expand();
47317                 }
47318             }else{
47319                 this.selectedIndex = -1;
47320                 this.onLoad();   
47321             }
47322         }
47323     },
47324
47325     // private
47326     getParams : function(q){
47327         var p = {};
47328         //p[this.queryParam] = q;
47329         if(this.pageSize){
47330             p.start = 0;
47331             p.limit = this.pageSize;
47332         }
47333         return p;
47334     },
47335
47336     /**
47337      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47338      */
47339     collapse : function(){
47340         
47341     },
47342
47343     // private
47344     collapseIf : function(e){
47345         
47346     },
47347
47348     /**
47349      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47350      */
47351     expand : function(){
47352         
47353     } ,
47354
47355     // private
47356      
47357
47358     /** 
47359     * @cfg {Boolean} grow 
47360     * @hide 
47361     */
47362     /** 
47363     * @cfg {Number} growMin 
47364     * @hide 
47365     */
47366     /** 
47367     * @cfg {Number} growMax 
47368     * @hide 
47369     */
47370     /**
47371      * @hide
47372      * @method autoSize
47373      */
47374     
47375     setWidth : function()
47376     {
47377         
47378     },
47379     getResizeEl : function(){
47380         return this.el;
47381     }
47382 });//<script type="text/javasscript">
47383  
47384
47385 /**
47386  * @class Roo.DDView
47387  * A DnD enabled version of Roo.View.
47388  * @param {Element/String} container The Element in which to create the View.
47389  * @param {String} tpl The template string used to create the markup for each element of the View
47390  * @param {Object} config The configuration properties. These include all the config options of
47391  * {@link Roo.View} plus some specific to this class.<br>
47392  * <p>
47393  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47394  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47395  * <p>
47396  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47397 .x-view-drag-insert-above {
47398         border-top:1px dotted #3366cc;
47399 }
47400 .x-view-drag-insert-below {
47401         border-bottom:1px dotted #3366cc;
47402 }
47403 </code></pre>
47404  * 
47405  */
47406  
47407 Roo.DDView = function(container, tpl, config) {
47408     Roo.DDView.superclass.constructor.apply(this, arguments);
47409     this.getEl().setStyle("outline", "0px none");
47410     this.getEl().unselectable();
47411     if (this.dragGroup) {
47412                 this.setDraggable(this.dragGroup.split(","));
47413     }
47414     if (this.dropGroup) {
47415                 this.setDroppable(this.dropGroup.split(","));
47416     }
47417     if (this.deletable) {
47418         this.setDeletable();
47419     }
47420     this.isDirtyFlag = false;
47421         this.addEvents({
47422                 "drop" : true
47423         });
47424 };
47425
47426 Roo.extend(Roo.DDView, Roo.View, {
47427 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47428 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47429 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47430 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47431
47432         isFormField: true,
47433
47434         reset: Roo.emptyFn,
47435         
47436         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47437
47438         validate: function() {
47439                 return true;
47440         },
47441         
47442         destroy: function() {
47443                 this.purgeListeners();
47444                 this.getEl.removeAllListeners();
47445                 this.getEl().remove();
47446                 if (this.dragZone) {
47447                         if (this.dragZone.destroy) {
47448                                 this.dragZone.destroy();
47449                         }
47450                 }
47451                 if (this.dropZone) {
47452                         if (this.dropZone.destroy) {
47453                                 this.dropZone.destroy();
47454                         }
47455                 }
47456         },
47457
47458 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47459         getName: function() {
47460                 return this.name;
47461         },
47462
47463 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47464         setValue: function(v) {
47465                 if (!this.store) {
47466                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47467                 }
47468                 var data = {};
47469                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47470                 this.store.proxy = new Roo.data.MemoryProxy(data);
47471                 this.store.load();
47472         },
47473
47474 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47475         getValue: function() {
47476                 var result = '(';
47477                 this.store.each(function(rec) {
47478                         result += rec.id + ',';
47479                 });
47480                 return result.substr(0, result.length - 1) + ')';
47481         },
47482         
47483         getIds: function() {
47484                 var i = 0, result = new Array(this.store.getCount());
47485                 this.store.each(function(rec) {
47486                         result[i++] = rec.id;
47487                 });
47488                 return result;
47489         },
47490         
47491         isDirty: function() {
47492                 return this.isDirtyFlag;
47493         },
47494
47495 /**
47496  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47497  *      whole Element becomes the target, and this causes the drop gesture to append.
47498  */
47499     getTargetFromEvent : function(e) {
47500                 var target = e.getTarget();
47501                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47502                 target = target.parentNode;
47503                 }
47504                 if (!target) {
47505                         target = this.el.dom.lastChild || this.el.dom;
47506                 }
47507                 return target;
47508     },
47509
47510 /**
47511  *      Create the drag data which consists of an object which has the property "ddel" as
47512  *      the drag proxy element. 
47513  */
47514     getDragData : function(e) {
47515         var target = this.findItemFromChild(e.getTarget());
47516                 if(target) {
47517                         this.handleSelection(e);
47518                         var selNodes = this.getSelectedNodes();
47519             var dragData = {
47520                 source: this,
47521                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47522                 nodes: selNodes,
47523                 records: []
47524                         };
47525                         var selectedIndices = this.getSelectedIndexes();
47526                         for (var i = 0; i < selectedIndices.length; i++) {
47527                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47528                         }
47529                         if (selNodes.length == 1) {
47530                                 dragData.ddel = target.cloneNode(true); // the div element
47531                         } else {
47532                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47533                                 div.className = 'multi-proxy';
47534                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47535                                         div.appendChild(selNodes[i].cloneNode(true));
47536                                 }
47537                                 dragData.ddel = div;
47538                         }
47539             //console.log(dragData)
47540             //console.log(dragData.ddel.innerHTML)
47541                         return dragData;
47542                 }
47543         //console.log('nodragData')
47544                 return false;
47545     },
47546     
47547 /**     Specify to which ddGroup items in this DDView may be dragged. */
47548     setDraggable: function(ddGroup) {
47549         if (ddGroup instanceof Array) {
47550                 Roo.each(ddGroup, this.setDraggable, this);
47551                 return;
47552         }
47553         if (this.dragZone) {
47554                 this.dragZone.addToGroup(ddGroup);
47555         } else {
47556                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47557                                 containerScroll: true,
47558                                 ddGroup: ddGroup 
47559
47560                         });
47561 //                      Draggability implies selection. DragZone's mousedown selects the element.
47562                         if (!this.multiSelect) { this.singleSelect = true; }
47563
47564 //                      Wire the DragZone's handlers up to methods in *this*
47565                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47566                 }
47567     },
47568
47569 /**     Specify from which ddGroup this DDView accepts drops. */
47570     setDroppable: function(ddGroup) {
47571         if (ddGroup instanceof Array) {
47572                 Roo.each(ddGroup, this.setDroppable, this);
47573                 return;
47574         }
47575         if (this.dropZone) {
47576                 this.dropZone.addToGroup(ddGroup);
47577         } else {
47578                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47579                                 containerScroll: true,
47580                                 ddGroup: ddGroup
47581                         });
47582
47583 //                      Wire the DropZone's handlers up to methods in *this*
47584                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47585                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47586                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47587                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47588                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47589                 }
47590     },
47591
47592 /**     Decide whether to drop above or below a View node. */
47593     getDropPoint : function(e, n, dd){
47594         if (n == this.el.dom) { return "above"; }
47595                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47596                 var c = t + (b - t) / 2;
47597                 var y = Roo.lib.Event.getPageY(e);
47598                 if(y <= c) {
47599                         return "above";
47600                 }else{
47601                         return "below";
47602                 }
47603     },
47604
47605     onNodeEnter : function(n, dd, e, data){
47606                 return false;
47607     },
47608     
47609     onNodeOver : function(n, dd, e, data){
47610                 var pt = this.getDropPoint(e, n, dd);
47611                 // set the insert point style on the target node
47612                 var dragElClass = this.dropNotAllowed;
47613                 if (pt) {
47614                         var targetElClass;
47615                         if (pt == "above"){
47616                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47617                                 targetElClass = "x-view-drag-insert-above";
47618                         } else {
47619                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47620                                 targetElClass = "x-view-drag-insert-below";
47621                         }
47622                         if (this.lastInsertClass != targetElClass){
47623                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47624                                 this.lastInsertClass = targetElClass;
47625                         }
47626                 }
47627                 return dragElClass;
47628         },
47629
47630     onNodeOut : function(n, dd, e, data){
47631                 this.removeDropIndicators(n);
47632     },
47633
47634     onNodeDrop : function(n, dd, e, data){
47635         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47636                 return false;
47637         }
47638         var pt = this.getDropPoint(e, n, dd);
47639                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47640                 if (pt == "below") { insertAt++; }
47641                 for (var i = 0; i < data.records.length; i++) {
47642                         var r = data.records[i];
47643                         var dup = this.store.getById(r.id);
47644                         if (dup && (dd != this.dragZone)) {
47645                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
47646                         } else {
47647                                 if (data.copy) {
47648                                         this.store.insert(insertAt++, r.copy());
47649                                 } else {
47650                                         data.source.isDirtyFlag = true;
47651                                         r.store.remove(r);
47652                                         this.store.insert(insertAt++, r);
47653                                 }
47654                                 this.isDirtyFlag = true;
47655                         }
47656                 }
47657                 this.dragZone.cachedTarget = null;
47658                 return true;
47659     },
47660
47661     removeDropIndicators : function(n){
47662                 if(n){
47663                         Roo.fly(n).removeClass([
47664                                 "x-view-drag-insert-above",
47665                                 "x-view-drag-insert-below"]);
47666                         this.lastInsertClass = "_noclass";
47667                 }
47668     },
47669
47670 /**
47671  *      Utility method. Add a delete option to the DDView's context menu.
47672  *      @param {String} imageUrl The URL of the "delete" icon image.
47673  */
47674         setDeletable: function(imageUrl) {
47675                 if (!this.singleSelect && !this.multiSelect) {
47676                         this.singleSelect = true;
47677                 }
47678                 var c = this.getContextMenu();
47679                 this.contextMenu.on("itemclick", function(item) {
47680                         switch (item.id) {
47681                                 case "delete":
47682                                         this.remove(this.getSelectedIndexes());
47683                                         break;
47684                         }
47685                 }, this);
47686                 this.contextMenu.add({
47687                         icon: imageUrl,
47688                         id: "delete",
47689                         text: 'Delete'
47690                 });
47691         },
47692         
47693 /**     Return the context menu for this DDView. */
47694         getContextMenu: function() {
47695                 if (!this.contextMenu) {
47696 //                      Create the View's context menu
47697                         this.contextMenu = new Roo.menu.Menu({
47698                                 id: this.id + "-contextmenu"
47699                         });
47700                         this.el.on("contextmenu", this.showContextMenu, this);
47701                 }
47702                 return this.contextMenu;
47703         },
47704         
47705         disableContextMenu: function() {
47706                 if (this.contextMenu) {
47707                         this.el.un("contextmenu", this.showContextMenu, this);
47708                 }
47709         },
47710
47711         showContextMenu: function(e, item) {
47712         item = this.findItemFromChild(e.getTarget());
47713                 if (item) {
47714                         e.stopEvent();
47715                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47716                         this.contextMenu.showAt(e.getXY());
47717             }
47718     },
47719
47720 /**
47721  *      Remove {@link Roo.data.Record}s at the specified indices.
47722  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47723  */
47724     remove: function(selectedIndices) {
47725                 selectedIndices = [].concat(selectedIndices);
47726                 for (var i = 0; i < selectedIndices.length; i++) {
47727                         var rec = this.store.getAt(selectedIndices[i]);
47728                         this.store.remove(rec);
47729                 }
47730     },
47731
47732 /**
47733  *      Double click fires the event, but also, if this is draggable, and there is only one other
47734  *      related DropZone, it transfers the selected node.
47735  */
47736     onDblClick : function(e){
47737         var item = this.findItemFromChild(e.getTarget());
47738         if(item){
47739             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47740                 return false;
47741             }
47742             if (this.dragGroup) {
47743                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47744                     while (targets.indexOf(this.dropZone) > -1) {
47745                             targets.remove(this.dropZone);
47746                                 }
47747                     if (targets.length == 1) {
47748                                         this.dragZone.cachedTarget = null;
47749                         var el = Roo.get(targets[0].getEl());
47750                         var box = el.getBox(true);
47751                         targets[0].onNodeDrop(el.dom, {
47752                                 target: el.dom,
47753                                 xy: [box.x, box.y + box.height - 1]
47754                         }, null, this.getDragData(e));
47755                     }
47756                 }
47757         }
47758     },
47759     
47760     handleSelection: function(e) {
47761                 this.dragZone.cachedTarget = null;
47762         var item = this.findItemFromChild(e.getTarget());
47763         if (!item) {
47764                 this.clearSelections(true);
47765                 return;
47766         }
47767                 if (item && (this.multiSelect || this.singleSelect)){
47768                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47769                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47770                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47771                                 this.unselect(item);
47772                         } else {
47773                                 this.select(item, this.multiSelect && e.ctrlKey);
47774                                 this.lastSelection = item;
47775                         }
47776                 }
47777     },
47778
47779     onItemClick : function(item, index, e){
47780                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47781                         return false;
47782                 }
47783                 return true;
47784     },
47785
47786     unselect : function(nodeInfo, suppressEvent){
47787                 var node = this.getNode(nodeInfo);
47788                 if(node && this.isSelected(node)){
47789                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47790                                 Roo.fly(node).removeClass(this.selectedClass);
47791                                 this.selections.remove(node);
47792                                 if(!suppressEvent){
47793                                         this.fireEvent("selectionchange", this, this.selections);
47794                                 }
47795                         }
47796                 }
47797     }
47798 });
47799 /*
47800  * Based on:
47801  * Ext JS Library 1.1.1
47802  * Copyright(c) 2006-2007, Ext JS, LLC.
47803  *
47804  * Originally Released Under LGPL - original licence link has changed is not relivant.
47805  *
47806  * Fork - LGPL
47807  * <script type="text/javascript">
47808  */
47809  
47810 /**
47811  * @class Roo.LayoutManager
47812  * @extends Roo.util.Observable
47813  * Base class for layout managers.
47814  */
47815 Roo.LayoutManager = function(container, config){
47816     Roo.LayoutManager.superclass.constructor.call(this);
47817     this.el = Roo.get(container);
47818     // ie scrollbar fix
47819     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47820         document.body.scroll = "no";
47821     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47822         this.el.position('relative');
47823     }
47824     this.id = this.el.id;
47825     this.el.addClass("x-layout-container");
47826     /** false to disable window resize monitoring @type Boolean */
47827     this.monitorWindowResize = true;
47828     this.regions = {};
47829     this.addEvents({
47830         /**
47831          * @event layout
47832          * Fires when a layout is performed. 
47833          * @param {Roo.LayoutManager} this
47834          */
47835         "layout" : true,
47836         /**
47837          * @event regionresized
47838          * Fires when the user resizes a region. 
47839          * @param {Roo.LayoutRegion} region The resized region
47840          * @param {Number} newSize The new size (width for east/west, height for north/south)
47841          */
47842         "regionresized" : true,
47843         /**
47844          * @event regioncollapsed
47845          * Fires when a region is collapsed. 
47846          * @param {Roo.LayoutRegion} region The collapsed region
47847          */
47848         "regioncollapsed" : true,
47849         /**
47850          * @event regionexpanded
47851          * Fires when a region is expanded.  
47852          * @param {Roo.LayoutRegion} region The expanded region
47853          */
47854         "regionexpanded" : true
47855     });
47856     this.updating = false;
47857     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47858 };
47859
47860 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47861     /**
47862      * Returns true if this layout is currently being updated
47863      * @return {Boolean}
47864      */
47865     isUpdating : function(){
47866         return this.updating; 
47867     },
47868     
47869     /**
47870      * Suspend the LayoutManager from doing auto-layouts while
47871      * making multiple add or remove calls
47872      */
47873     beginUpdate : function(){
47874         this.updating = true;    
47875     },
47876     
47877     /**
47878      * Restore auto-layouts and optionally disable the manager from performing a layout
47879      * @param {Boolean} noLayout true to disable a layout update 
47880      */
47881     endUpdate : function(noLayout){
47882         this.updating = false;
47883         if(!noLayout){
47884             this.layout();
47885         }    
47886     },
47887     
47888     layout: function(){
47889         
47890     },
47891     
47892     onRegionResized : function(region, newSize){
47893         this.fireEvent("regionresized", region, newSize);
47894         this.layout();
47895     },
47896     
47897     onRegionCollapsed : function(region){
47898         this.fireEvent("regioncollapsed", region);
47899     },
47900     
47901     onRegionExpanded : function(region){
47902         this.fireEvent("regionexpanded", region);
47903     },
47904         
47905     /**
47906      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47907      * performs box-model adjustments.
47908      * @return {Object} The size as an object {width: (the width), height: (the height)}
47909      */
47910     getViewSize : function(){
47911         var size;
47912         if(this.el.dom != document.body){
47913             size = this.el.getSize();
47914         }else{
47915             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47916         }
47917         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47918         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47919         return size;
47920     },
47921     
47922     /**
47923      * Returns the Element this layout is bound to.
47924      * @return {Roo.Element}
47925      */
47926     getEl : function(){
47927         return this.el;
47928     },
47929     
47930     /**
47931      * Returns the specified region.
47932      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47933      * @return {Roo.LayoutRegion}
47934      */
47935     getRegion : function(target){
47936         return this.regions[target.toLowerCase()];
47937     },
47938     
47939     onWindowResize : function(){
47940         if(this.monitorWindowResize){
47941             this.layout();
47942         }
47943     }
47944 });/*
47945  * Based on:
47946  * Ext JS Library 1.1.1
47947  * Copyright(c) 2006-2007, Ext JS, LLC.
47948  *
47949  * Originally Released Under LGPL - original licence link has changed is not relivant.
47950  *
47951  * Fork - LGPL
47952  * <script type="text/javascript">
47953  */
47954 /**
47955  * @class Roo.BorderLayout
47956  * @extends Roo.LayoutManager
47957  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47958  * please see: <br><br>
47959  * <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>
47960  * <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>
47961  * Example:
47962  <pre><code>
47963  var layout = new Roo.BorderLayout(document.body, {
47964     north: {
47965         initialSize: 25,
47966         titlebar: false
47967     },
47968     west: {
47969         split:true,
47970         initialSize: 200,
47971         minSize: 175,
47972         maxSize: 400,
47973         titlebar: true,
47974         collapsible: true
47975     },
47976     east: {
47977         split:true,
47978         initialSize: 202,
47979         minSize: 175,
47980         maxSize: 400,
47981         titlebar: true,
47982         collapsible: true
47983     },
47984     south: {
47985         split:true,
47986         initialSize: 100,
47987         minSize: 100,
47988         maxSize: 200,
47989         titlebar: true,
47990         collapsible: true
47991     },
47992     center: {
47993         titlebar: true,
47994         autoScroll:true,
47995         resizeTabs: true,
47996         minTabWidth: 50,
47997         preferredTabWidth: 150
47998     }
47999 });
48000
48001 // shorthand
48002 var CP = Roo.ContentPanel;
48003
48004 layout.beginUpdate();
48005 layout.add("north", new CP("north", "North"));
48006 layout.add("south", new CP("south", {title: "South", closable: true}));
48007 layout.add("west", new CP("west", {title: "West"}));
48008 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48009 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48010 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48011 layout.getRegion("center").showPanel("center1");
48012 layout.endUpdate();
48013 </code></pre>
48014
48015 <b>The container the layout is rendered into can be either the body element or any other element.
48016 If it is not the body element, the container needs to either be an absolute positioned element,
48017 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48018 the container size if it is not the body element.</b>
48019
48020 * @constructor
48021 * Create a new BorderLayout
48022 * @param {String/HTMLElement/Element} container The container this layout is bound to
48023 * @param {Object} config Configuration options
48024  */
48025 Roo.BorderLayout = function(container, config){
48026     config = config || {};
48027     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48028     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48029     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48030         var target = this.factory.validRegions[i];
48031         if(config[target]){
48032             this.addRegion(target, config[target]);
48033         }
48034     }
48035 };
48036
48037 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48038     /**
48039      * Creates and adds a new region if it doesn't already exist.
48040      * @param {String} target The target region key (north, south, east, west or center).
48041      * @param {Object} config The regions config object
48042      * @return {BorderLayoutRegion} The new region
48043      */
48044     addRegion : function(target, config){
48045         if(!this.regions[target]){
48046             var r = this.factory.create(target, this, config);
48047             this.bindRegion(target, r);
48048         }
48049         return this.regions[target];
48050     },
48051
48052     // private (kinda)
48053     bindRegion : function(name, r){
48054         this.regions[name] = r;
48055         r.on("visibilitychange", this.layout, this);
48056         r.on("paneladded", this.layout, this);
48057         r.on("panelremoved", this.layout, this);
48058         r.on("invalidated", this.layout, this);
48059         r.on("resized", this.onRegionResized, this);
48060         r.on("collapsed", this.onRegionCollapsed, this);
48061         r.on("expanded", this.onRegionExpanded, this);
48062     },
48063
48064     /**
48065      * Performs a layout update.
48066      */
48067     layout : function(){
48068         if(this.updating) return;
48069         var size = this.getViewSize();
48070         var w = size.width;
48071         var h = size.height;
48072         var centerW = w;
48073         var centerH = h;
48074         var centerY = 0;
48075         var centerX = 0;
48076         //var x = 0, y = 0;
48077
48078         var rs = this.regions;
48079         var north = rs["north"];
48080         var south = rs["south"]; 
48081         var west = rs["west"];
48082         var east = rs["east"];
48083         var center = rs["center"];
48084         //if(this.hideOnLayout){ // not supported anymore
48085             //c.el.setStyle("display", "none");
48086         //}
48087         if(north && north.isVisible()){
48088             var b = north.getBox();
48089             var m = north.getMargins();
48090             b.width = w - (m.left+m.right);
48091             b.x = m.left;
48092             b.y = m.top;
48093             centerY = b.height + b.y + m.bottom;
48094             centerH -= centerY;
48095             north.updateBox(this.safeBox(b));
48096         }
48097         if(south && south.isVisible()){
48098             var b = south.getBox();
48099             var m = south.getMargins();
48100             b.width = w - (m.left+m.right);
48101             b.x = m.left;
48102             var totalHeight = (b.height + m.top + m.bottom);
48103             b.y = h - totalHeight + m.top;
48104             centerH -= totalHeight;
48105             south.updateBox(this.safeBox(b));
48106         }
48107         if(west && west.isVisible()){
48108             var b = west.getBox();
48109             var m = west.getMargins();
48110             b.height = centerH - (m.top+m.bottom);
48111             b.x = m.left;
48112             b.y = centerY + m.top;
48113             var totalWidth = (b.width + m.left + m.right);
48114             centerX += totalWidth;
48115             centerW -= totalWidth;
48116             west.updateBox(this.safeBox(b));
48117         }
48118         if(east && east.isVisible()){
48119             var b = east.getBox();
48120             var m = east.getMargins();
48121             b.height = centerH - (m.top+m.bottom);
48122             var totalWidth = (b.width + m.left + m.right);
48123             b.x = w - totalWidth + m.left;
48124             b.y = centerY + m.top;
48125             centerW -= totalWidth;
48126             east.updateBox(this.safeBox(b));
48127         }
48128         if(center){
48129             var m = center.getMargins();
48130             var centerBox = {
48131                 x: centerX + m.left,
48132                 y: centerY + m.top,
48133                 width: centerW - (m.left+m.right),
48134                 height: centerH - (m.top+m.bottom)
48135             };
48136             //if(this.hideOnLayout){
48137                 //center.el.setStyle("display", "block");
48138             //}
48139             center.updateBox(this.safeBox(centerBox));
48140         }
48141         this.el.repaint();
48142         this.fireEvent("layout", this);
48143     },
48144
48145     // private
48146     safeBox : function(box){
48147         box.width = Math.max(0, box.width);
48148         box.height = Math.max(0, box.height);
48149         return box;
48150     },
48151
48152     /**
48153      * Adds a ContentPanel (or subclass) to this layout.
48154      * @param {String} target The target region key (north, south, east, west or center).
48155      * @param {Roo.ContentPanel} panel The panel to add
48156      * @return {Roo.ContentPanel} The added panel
48157      */
48158     add : function(target, panel){
48159          
48160         target = target.toLowerCase();
48161         return this.regions[target].add(panel);
48162     },
48163
48164     /**
48165      * Remove a ContentPanel (or subclass) to this layout.
48166      * @param {String} target The target region key (north, south, east, west or center).
48167      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48168      * @return {Roo.ContentPanel} The removed panel
48169      */
48170     remove : function(target, panel){
48171         target = target.toLowerCase();
48172         return this.regions[target].remove(panel);
48173     },
48174
48175     /**
48176      * Searches all regions for a panel with the specified id
48177      * @param {String} panelId
48178      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48179      */
48180     findPanel : function(panelId){
48181         var rs = this.regions;
48182         for(var target in rs){
48183             if(typeof rs[target] != "function"){
48184                 var p = rs[target].getPanel(panelId);
48185                 if(p){
48186                     return p;
48187                 }
48188             }
48189         }
48190         return null;
48191     },
48192
48193     /**
48194      * Searches all regions for a panel with the specified id and activates (shows) it.
48195      * @param {String/ContentPanel} panelId The panels id or the panel itself
48196      * @return {Roo.ContentPanel} The shown panel or null
48197      */
48198     showPanel : function(panelId) {
48199       var rs = this.regions;
48200       for(var target in rs){
48201          var r = rs[target];
48202          if(typeof r != "function"){
48203             if(r.hasPanel(panelId)){
48204                return r.showPanel(panelId);
48205             }
48206          }
48207       }
48208       return null;
48209    },
48210
48211    /**
48212      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48213      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48214      */
48215     restoreState : function(provider){
48216         if(!provider){
48217             provider = Roo.state.Manager;
48218         }
48219         var sm = new Roo.LayoutStateManager();
48220         sm.init(this, provider);
48221     },
48222
48223     /**
48224      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48225      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48226      * a valid ContentPanel config object.  Example:
48227      * <pre><code>
48228 // Create the main layout
48229 var layout = new Roo.BorderLayout('main-ct', {
48230     west: {
48231         split:true,
48232         minSize: 175,
48233         titlebar: true
48234     },
48235     center: {
48236         title:'Components'
48237     }
48238 }, 'main-ct');
48239
48240 // Create and add multiple ContentPanels at once via configs
48241 layout.batchAdd({
48242    west: {
48243        id: 'source-files',
48244        autoCreate:true,
48245        title:'Ext Source Files',
48246        autoScroll:true,
48247        fitToFrame:true
48248    },
48249    center : {
48250        el: cview,
48251        autoScroll:true,
48252        fitToFrame:true,
48253        toolbar: tb,
48254        resizeEl:'cbody'
48255    }
48256 });
48257 </code></pre>
48258      * @param {Object} regions An object containing ContentPanel configs by region name
48259      */
48260     batchAdd : function(regions){
48261         this.beginUpdate();
48262         for(var rname in regions){
48263             var lr = this.regions[rname];
48264             if(lr){
48265                 this.addTypedPanels(lr, regions[rname]);
48266             }
48267         }
48268         this.endUpdate();
48269     },
48270
48271     // private
48272     addTypedPanels : function(lr, ps){
48273         if(typeof ps == 'string'){
48274             lr.add(new Roo.ContentPanel(ps));
48275         }
48276         else if(ps instanceof Array){
48277             for(var i =0, len = ps.length; i < len; i++){
48278                 this.addTypedPanels(lr, ps[i]);
48279             }
48280         }
48281         else if(!ps.events){ // raw config?
48282             var el = ps.el;
48283             delete ps.el; // prevent conflict
48284             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48285         }
48286         else {  // panel object assumed!
48287             lr.add(ps);
48288         }
48289     },
48290     /**
48291      * Adds a xtype elements to the layout.
48292      * <pre><code>
48293
48294 layout.addxtype({
48295        xtype : 'ContentPanel',
48296        region: 'west',
48297        items: [ .... ]
48298    }
48299 );
48300
48301 layout.addxtype({
48302         xtype : 'NestedLayoutPanel',
48303         region: 'west',
48304         layout: {
48305            center: { },
48306            west: { }   
48307         },
48308         items : [ ... list of content panels or nested layout panels.. ]
48309    }
48310 );
48311 </code></pre>
48312      * @param {Object} cfg Xtype definition of item to add.
48313      */
48314     addxtype : function(cfg)
48315     {
48316         // basically accepts a pannel...
48317         // can accept a layout region..!?!?
48318         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48319         
48320         if (!cfg.xtype.match(/Panel$/)) {
48321             return false;
48322         }
48323         var ret = false;
48324         
48325         if (typeof(cfg.region) == 'undefined') {
48326             Roo.log("Failed to add Panel, region was not set");
48327             Roo.log(cfg);
48328             return false;
48329         }
48330         var region = cfg.region;
48331         delete cfg.region;
48332         
48333           
48334         var xitems = [];
48335         if (cfg.items) {
48336             xitems = cfg.items;
48337             delete cfg.items;
48338         }
48339         var nb = false;
48340         
48341         switch(cfg.xtype) 
48342         {
48343             case 'ContentPanel':  // ContentPanel (el, cfg)
48344             case 'ScrollPanel':  // ContentPanel (el, cfg)
48345             case 'ViewPanel': 
48346                 if(cfg.autoCreate) {
48347                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48348                 } else {
48349                     var el = this.el.createChild();
48350                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48351                 }
48352                 
48353                 this.add(region, ret);
48354                 break;
48355             
48356             
48357             case 'TreePanel': // our new panel!
48358                 cfg.el = this.el.createChild();
48359                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48360                 this.add(region, ret);
48361                 break;
48362             
48363             case 'NestedLayoutPanel': 
48364                 // create a new Layout (which is  a Border Layout...
48365                 var el = this.el.createChild();
48366                 var clayout = cfg.layout;
48367                 delete cfg.layout;
48368                 clayout.items   = clayout.items  || [];
48369                 // replace this exitems with the clayout ones..
48370                 xitems = clayout.items;
48371                  
48372                 
48373                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48374                     cfg.background = false;
48375                 }
48376                 var layout = new Roo.BorderLayout(el, clayout);
48377                 
48378                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48379                 //console.log('adding nested layout panel '  + cfg.toSource());
48380                 this.add(region, ret);
48381                 nb = {}; /// find first...
48382                 break;
48383                 
48384             case 'GridPanel': 
48385             
48386                 // needs grid and region
48387                 
48388                 //var el = this.getRegion(region).el.createChild();
48389                 var el = this.el.createChild();
48390                 // create the grid first...
48391                 
48392                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48393                 delete cfg.grid;
48394                 if (region == 'center' && this.active ) {
48395                     cfg.background = false;
48396                 }
48397                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48398                 
48399                 this.add(region, ret);
48400                 if (cfg.background) {
48401                     ret.on('activate', function(gp) {
48402                         if (!gp.grid.rendered) {
48403                             gp.grid.render();
48404                         }
48405                     });
48406                 } else {
48407                     grid.render();
48408                 }
48409                 break;
48410            
48411            
48412            
48413                 
48414                 
48415                 
48416             default: 
48417                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48418                 return null;
48419              // GridPanel (grid, cfg)
48420             
48421         }
48422         this.beginUpdate();
48423         // add children..
48424         var region = '';
48425         var abn = {};
48426         Roo.each(xitems, function(i)  {
48427             region = nb && i.region ? i.region : false;
48428             
48429             var add = ret.addxtype(i);
48430            
48431             if (region) {
48432                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48433                 if (!i.background) {
48434                     abn[region] = nb[region] ;
48435                 }
48436             }
48437             
48438         });
48439         this.endUpdate();
48440
48441         // make the last non-background panel active..
48442         //if (nb) { Roo.log(abn); }
48443         if (nb) {
48444             
48445             for(var r in abn) {
48446                 region = this.getRegion(r);
48447                 if (region) {
48448                     // tried using nb[r], but it does not work..
48449                      
48450                     region.showPanel(abn[r]);
48451                    
48452                 }
48453             }
48454         }
48455         return ret;
48456         
48457     }
48458 });
48459
48460 /**
48461  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48462  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48463  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48464  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48465  * <pre><code>
48466 // shorthand
48467 var CP = Roo.ContentPanel;
48468
48469 var layout = Roo.BorderLayout.create({
48470     north: {
48471         initialSize: 25,
48472         titlebar: false,
48473         panels: [new CP("north", "North")]
48474     },
48475     west: {
48476         split:true,
48477         initialSize: 200,
48478         minSize: 175,
48479         maxSize: 400,
48480         titlebar: true,
48481         collapsible: true,
48482         panels: [new CP("west", {title: "West"})]
48483     },
48484     east: {
48485         split:true,
48486         initialSize: 202,
48487         minSize: 175,
48488         maxSize: 400,
48489         titlebar: true,
48490         collapsible: true,
48491         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48492     },
48493     south: {
48494         split:true,
48495         initialSize: 100,
48496         minSize: 100,
48497         maxSize: 200,
48498         titlebar: true,
48499         collapsible: true,
48500         panels: [new CP("south", {title: "South", closable: true})]
48501     },
48502     center: {
48503         titlebar: true,
48504         autoScroll:true,
48505         resizeTabs: true,
48506         minTabWidth: 50,
48507         preferredTabWidth: 150,
48508         panels: [
48509             new CP("center1", {title: "Close Me", closable: true}),
48510             new CP("center2", {title: "Center Panel", closable: false})
48511         ]
48512     }
48513 }, document.body);
48514
48515 layout.getRegion("center").showPanel("center1");
48516 </code></pre>
48517  * @param config
48518  * @param targetEl
48519  */
48520 Roo.BorderLayout.create = function(config, targetEl){
48521     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48522     layout.beginUpdate();
48523     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48524     for(var j = 0, jlen = regions.length; j < jlen; j++){
48525         var lr = regions[j];
48526         if(layout.regions[lr] && config[lr].panels){
48527             var r = layout.regions[lr];
48528             var ps = config[lr].panels;
48529             layout.addTypedPanels(r, ps);
48530         }
48531     }
48532     layout.endUpdate();
48533     return layout;
48534 };
48535
48536 // private
48537 Roo.BorderLayout.RegionFactory = {
48538     // private
48539     validRegions : ["north","south","east","west","center"],
48540
48541     // private
48542     create : function(target, mgr, config){
48543         target = target.toLowerCase();
48544         if(config.lightweight || config.basic){
48545             return new Roo.BasicLayoutRegion(mgr, config, target);
48546         }
48547         switch(target){
48548             case "north":
48549                 return new Roo.NorthLayoutRegion(mgr, config);
48550             case "south":
48551                 return new Roo.SouthLayoutRegion(mgr, config);
48552             case "east":
48553                 return new Roo.EastLayoutRegion(mgr, config);
48554             case "west":
48555                 return new Roo.WestLayoutRegion(mgr, config);
48556             case "center":
48557                 return new Roo.CenterLayoutRegion(mgr, config);
48558         }
48559         throw 'Layout region "'+target+'" not supported.';
48560     }
48561 };/*
48562  * Based on:
48563  * Ext JS Library 1.1.1
48564  * Copyright(c) 2006-2007, Ext JS, LLC.
48565  *
48566  * Originally Released Under LGPL - original licence link has changed is not relivant.
48567  *
48568  * Fork - LGPL
48569  * <script type="text/javascript">
48570  */
48571  
48572 /**
48573  * @class Roo.BasicLayoutRegion
48574  * @extends Roo.util.Observable
48575  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48576  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48577  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48578  */
48579 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48580     this.mgr = mgr;
48581     this.position  = pos;
48582     this.events = {
48583         /**
48584          * @scope Roo.BasicLayoutRegion
48585          */
48586         
48587         /**
48588          * @event beforeremove
48589          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48590          * @param {Roo.LayoutRegion} this
48591          * @param {Roo.ContentPanel} panel The panel
48592          * @param {Object} e The cancel event object
48593          */
48594         "beforeremove" : true,
48595         /**
48596          * @event invalidated
48597          * Fires when the layout for this region is changed.
48598          * @param {Roo.LayoutRegion} this
48599          */
48600         "invalidated" : true,
48601         /**
48602          * @event visibilitychange
48603          * Fires when this region is shown or hidden 
48604          * @param {Roo.LayoutRegion} this
48605          * @param {Boolean} visibility true or false
48606          */
48607         "visibilitychange" : true,
48608         /**
48609          * @event paneladded
48610          * Fires when a panel is added. 
48611          * @param {Roo.LayoutRegion} this
48612          * @param {Roo.ContentPanel} panel The panel
48613          */
48614         "paneladded" : true,
48615         /**
48616          * @event panelremoved
48617          * Fires when a panel is removed. 
48618          * @param {Roo.LayoutRegion} this
48619          * @param {Roo.ContentPanel} panel The panel
48620          */
48621         "panelremoved" : true,
48622         /**
48623          * @event collapsed
48624          * Fires when this region is collapsed.
48625          * @param {Roo.LayoutRegion} this
48626          */
48627         "collapsed" : true,
48628         /**
48629          * @event expanded
48630          * Fires when this region is expanded.
48631          * @param {Roo.LayoutRegion} this
48632          */
48633         "expanded" : true,
48634         /**
48635          * @event slideshow
48636          * Fires when this region is slid into view.
48637          * @param {Roo.LayoutRegion} this
48638          */
48639         "slideshow" : true,
48640         /**
48641          * @event slidehide
48642          * Fires when this region slides out of view. 
48643          * @param {Roo.LayoutRegion} this
48644          */
48645         "slidehide" : true,
48646         /**
48647          * @event panelactivated
48648          * Fires when a panel is activated. 
48649          * @param {Roo.LayoutRegion} this
48650          * @param {Roo.ContentPanel} panel The activated panel
48651          */
48652         "panelactivated" : true,
48653         /**
48654          * @event resized
48655          * Fires when the user resizes this region. 
48656          * @param {Roo.LayoutRegion} this
48657          * @param {Number} newSize The new size (width for east/west, height for north/south)
48658          */
48659         "resized" : true
48660     };
48661     /** A collection of panels in this region. @type Roo.util.MixedCollection */
48662     this.panels = new Roo.util.MixedCollection();
48663     this.panels.getKey = this.getPanelId.createDelegate(this);
48664     this.box = null;
48665     this.activePanel = null;
48666     // ensure listeners are added...
48667     
48668     if (config.listeners || config.events) {
48669         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
48670             listeners : config.listeners || {},
48671             events : config.events || {}
48672         });
48673     }
48674     
48675     if(skipConfig !== true){
48676         this.applyConfig(config);
48677     }
48678 };
48679
48680 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
48681     getPanelId : function(p){
48682         return p.getId();
48683     },
48684     
48685     applyConfig : function(config){
48686         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48687         this.config = config;
48688         
48689     },
48690     
48691     /**
48692      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
48693      * the width, for horizontal (north, south) the height.
48694      * @param {Number} newSize The new width or height
48695      */
48696     resizeTo : function(newSize){
48697         var el = this.el ? this.el :
48698                  (this.activePanel ? this.activePanel.getEl() : null);
48699         if(el){
48700             switch(this.position){
48701                 case "east":
48702                 case "west":
48703                     el.setWidth(newSize);
48704                     this.fireEvent("resized", this, newSize);
48705                 break;
48706                 case "north":
48707                 case "south":
48708                     el.setHeight(newSize);
48709                     this.fireEvent("resized", this, newSize);
48710                 break;                
48711             }
48712         }
48713     },
48714     
48715     getBox : function(){
48716         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48717     },
48718     
48719     getMargins : function(){
48720         return this.margins;
48721     },
48722     
48723     updateBox : function(box){
48724         this.box = box;
48725         var el = this.activePanel.getEl();
48726         el.dom.style.left = box.x + "px";
48727         el.dom.style.top = box.y + "px";
48728         this.activePanel.setSize(box.width, box.height);
48729     },
48730     
48731     /**
48732      * Returns the container element for this region.
48733      * @return {Roo.Element}
48734      */
48735     getEl : function(){
48736         return this.activePanel;
48737     },
48738     
48739     /**
48740      * Returns true if this region is currently visible.
48741      * @return {Boolean}
48742      */
48743     isVisible : function(){
48744         return this.activePanel ? true : false;
48745     },
48746     
48747     setActivePanel : function(panel){
48748         panel = this.getPanel(panel);
48749         if(this.activePanel && this.activePanel != panel){
48750             this.activePanel.setActiveState(false);
48751             this.activePanel.getEl().setLeftTop(-10000,-10000);
48752         }
48753         this.activePanel = panel;
48754         panel.setActiveState(true);
48755         if(this.box){
48756             panel.setSize(this.box.width, this.box.height);
48757         }
48758         this.fireEvent("panelactivated", this, panel);
48759         this.fireEvent("invalidated");
48760     },
48761     
48762     /**
48763      * Show the specified panel.
48764      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48765      * @return {Roo.ContentPanel} The shown panel or null
48766      */
48767     showPanel : function(panel){
48768         if(panel = this.getPanel(panel)){
48769             this.setActivePanel(panel);
48770         }
48771         return panel;
48772     },
48773     
48774     /**
48775      * Get the active panel for this region.
48776      * @return {Roo.ContentPanel} The active panel or null
48777      */
48778     getActivePanel : function(){
48779         return this.activePanel;
48780     },
48781     
48782     /**
48783      * Add the passed ContentPanel(s)
48784      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48785      * @return {Roo.ContentPanel} The panel added (if only one was added)
48786      */
48787     add : function(panel){
48788         if(arguments.length > 1){
48789             for(var i = 0, len = arguments.length; i < len; i++) {
48790                 this.add(arguments[i]);
48791             }
48792             return null;
48793         }
48794         if(this.hasPanel(panel)){
48795             this.showPanel(panel);
48796             return panel;
48797         }
48798         var el = panel.getEl();
48799         if(el.dom.parentNode != this.mgr.el.dom){
48800             this.mgr.el.dom.appendChild(el.dom);
48801         }
48802         if(panel.setRegion){
48803             panel.setRegion(this);
48804         }
48805         this.panels.add(panel);
48806         el.setStyle("position", "absolute");
48807         if(!panel.background){
48808             this.setActivePanel(panel);
48809             if(this.config.initialSize && this.panels.getCount()==1){
48810                 this.resizeTo(this.config.initialSize);
48811             }
48812         }
48813         this.fireEvent("paneladded", this, panel);
48814         return panel;
48815     },
48816     
48817     /**
48818      * Returns true if the panel is in this region.
48819      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48820      * @return {Boolean}
48821      */
48822     hasPanel : function(panel){
48823         if(typeof panel == "object"){ // must be panel obj
48824             panel = panel.getId();
48825         }
48826         return this.getPanel(panel) ? true : false;
48827     },
48828     
48829     /**
48830      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48831      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48832      * @param {Boolean} preservePanel Overrides the config preservePanel option
48833      * @return {Roo.ContentPanel} The panel that was removed
48834      */
48835     remove : function(panel, preservePanel){
48836         panel = this.getPanel(panel);
48837         if(!panel){
48838             return null;
48839         }
48840         var e = {};
48841         this.fireEvent("beforeremove", this, panel, e);
48842         if(e.cancel === true){
48843             return null;
48844         }
48845         var panelId = panel.getId();
48846         this.panels.removeKey(panelId);
48847         return panel;
48848     },
48849     
48850     /**
48851      * Returns the panel specified or null if it's not in this region.
48852      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48853      * @return {Roo.ContentPanel}
48854      */
48855     getPanel : function(id){
48856         if(typeof id == "object"){ // must be panel obj
48857             return id;
48858         }
48859         return this.panels.get(id);
48860     },
48861     
48862     /**
48863      * Returns this regions position (north/south/east/west/center).
48864      * @return {String} 
48865      */
48866     getPosition: function(){
48867         return this.position;    
48868     }
48869 });/*
48870  * Based on:
48871  * Ext JS Library 1.1.1
48872  * Copyright(c) 2006-2007, Ext JS, LLC.
48873  *
48874  * Originally Released Under LGPL - original licence link has changed is not relivant.
48875  *
48876  * Fork - LGPL
48877  * <script type="text/javascript">
48878  */
48879  
48880 /**
48881  * @class Roo.LayoutRegion
48882  * @extends Roo.BasicLayoutRegion
48883  * This class represents a region in a layout manager.
48884  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48885  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48886  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48887  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48888  * @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})
48889  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48890  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48891  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48892  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48893  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48894  * @cfg {String}    title           The title for the region (overrides panel titles)
48895  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48896  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48897  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48898  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48899  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48900  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48901  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48902  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48903  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48904  * @cfg {Boolean}   showPin         True to show a pin button
48905  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48906  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48907  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48908  * @cfg {Number}    width           For East/West panels
48909  * @cfg {Number}    height          For North/South panels
48910  * @cfg {Boolean}   split           To show the splitter
48911  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48912  */
48913 Roo.LayoutRegion = function(mgr, config, pos){
48914     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48915     var dh = Roo.DomHelper;
48916     /** This region's container element 
48917     * @type Roo.Element */
48918     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48919     /** This region's title element 
48920     * @type Roo.Element */
48921
48922     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48923         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48924         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48925     ]}, true);
48926     this.titleEl.enableDisplayMode();
48927     /** This region's title text element 
48928     * @type HTMLElement */
48929     this.titleTextEl = this.titleEl.dom.firstChild;
48930     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48931     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48932     this.closeBtn.enableDisplayMode();
48933     this.closeBtn.on("click", this.closeClicked, this);
48934     this.closeBtn.hide();
48935
48936     this.createBody(config);
48937     this.visible = true;
48938     this.collapsed = false;
48939
48940     if(config.hideWhenEmpty){
48941         this.hide();
48942         this.on("paneladded", this.validateVisibility, this);
48943         this.on("panelremoved", this.validateVisibility, this);
48944     }
48945     this.applyConfig(config);
48946 };
48947
48948 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48949
48950     createBody : function(){
48951         /** This region's body element 
48952         * @type Roo.Element */
48953         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48954     },
48955
48956     applyConfig : function(c){
48957         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48958             var dh = Roo.DomHelper;
48959             if(c.titlebar !== false){
48960                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48961                 this.collapseBtn.on("click", this.collapse, this);
48962                 this.collapseBtn.enableDisplayMode();
48963
48964                 if(c.showPin === true || this.showPin){
48965                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48966                     this.stickBtn.enableDisplayMode();
48967                     this.stickBtn.on("click", this.expand, this);
48968                     this.stickBtn.hide();
48969                 }
48970             }
48971             /** This region's collapsed element
48972             * @type Roo.Element */
48973             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48974                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48975             ]}, true);
48976             if(c.floatable !== false){
48977                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48978                this.collapsedEl.on("click", this.collapseClick, this);
48979             }
48980
48981             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48982                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48983                    id: "message", unselectable: "on", style:{"float":"left"}});
48984                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
48985              }
48986             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
48987             this.expandBtn.on("click", this.expand, this);
48988         }
48989         if(this.collapseBtn){
48990             this.collapseBtn.setVisible(c.collapsible == true);
48991         }
48992         this.cmargins = c.cmargins || this.cmargins ||
48993                          (this.position == "west" || this.position == "east" ?
48994                              {top: 0, left: 2, right:2, bottom: 0} :
48995                              {top: 2, left: 0, right:0, bottom: 2});
48996         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48997         this.bottomTabs = c.tabPosition != "top";
48998         this.autoScroll = c.autoScroll || false;
48999         if(this.autoScroll){
49000             this.bodyEl.setStyle("overflow", "auto");
49001         }else{
49002             this.bodyEl.setStyle("overflow", "hidden");
49003         }
49004         //if(c.titlebar !== false){
49005             if((!c.titlebar && !c.title) || c.titlebar === false){
49006                 this.titleEl.hide();
49007             }else{
49008                 this.titleEl.show();
49009                 if(c.title){
49010                     this.titleTextEl.innerHTML = c.title;
49011                 }
49012             }
49013         //}
49014         this.duration = c.duration || .30;
49015         this.slideDuration = c.slideDuration || .45;
49016         this.config = c;
49017         if(c.collapsed){
49018             this.collapse(true);
49019         }
49020         if(c.hidden){
49021             this.hide();
49022         }
49023     },
49024     /**
49025      * Returns true if this region is currently visible.
49026      * @return {Boolean}
49027      */
49028     isVisible : function(){
49029         return this.visible;
49030     },
49031
49032     /**
49033      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49034      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49035      */
49036     setCollapsedTitle : function(title){
49037         title = title || "&#160;";
49038         if(this.collapsedTitleTextEl){
49039             this.collapsedTitleTextEl.innerHTML = title;
49040         }
49041     },
49042
49043     getBox : function(){
49044         var b;
49045         if(!this.collapsed){
49046             b = this.el.getBox(false, true);
49047         }else{
49048             b = this.collapsedEl.getBox(false, true);
49049         }
49050         return b;
49051     },
49052
49053     getMargins : function(){
49054         return this.collapsed ? this.cmargins : this.margins;
49055     },
49056
49057     highlight : function(){
49058         this.el.addClass("x-layout-panel-dragover");
49059     },
49060
49061     unhighlight : function(){
49062         this.el.removeClass("x-layout-panel-dragover");
49063     },
49064
49065     updateBox : function(box){
49066         this.box = box;
49067         if(!this.collapsed){
49068             this.el.dom.style.left = box.x + "px";
49069             this.el.dom.style.top = box.y + "px";
49070             this.updateBody(box.width, box.height);
49071         }else{
49072             this.collapsedEl.dom.style.left = box.x + "px";
49073             this.collapsedEl.dom.style.top = box.y + "px";
49074             this.collapsedEl.setSize(box.width, box.height);
49075         }
49076         if(this.tabs){
49077             this.tabs.autoSizeTabs();
49078         }
49079     },
49080
49081     updateBody : function(w, h){
49082         if(w !== null){
49083             this.el.setWidth(w);
49084             w -= this.el.getBorderWidth("rl");
49085             if(this.config.adjustments){
49086                 w += this.config.adjustments[0];
49087             }
49088         }
49089         if(h !== null){
49090             this.el.setHeight(h);
49091             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49092             h -= this.el.getBorderWidth("tb");
49093             if(this.config.adjustments){
49094                 h += this.config.adjustments[1];
49095             }
49096             this.bodyEl.setHeight(h);
49097             if(this.tabs){
49098                 h = this.tabs.syncHeight(h);
49099             }
49100         }
49101         if(this.panelSize){
49102             w = w !== null ? w : this.panelSize.width;
49103             h = h !== null ? h : this.panelSize.height;
49104         }
49105         if(this.activePanel){
49106             var el = this.activePanel.getEl();
49107             w = w !== null ? w : el.getWidth();
49108             h = h !== null ? h : el.getHeight();
49109             this.panelSize = {width: w, height: h};
49110             this.activePanel.setSize(w, h);
49111         }
49112         if(Roo.isIE && this.tabs){
49113             this.tabs.el.repaint();
49114         }
49115     },
49116
49117     /**
49118      * Returns the container element for this region.
49119      * @return {Roo.Element}
49120      */
49121     getEl : function(){
49122         return this.el;
49123     },
49124
49125     /**
49126      * Hides this region.
49127      */
49128     hide : function(){
49129         if(!this.collapsed){
49130             this.el.dom.style.left = "-2000px";
49131             this.el.hide();
49132         }else{
49133             this.collapsedEl.dom.style.left = "-2000px";
49134             this.collapsedEl.hide();
49135         }
49136         this.visible = false;
49137         this.fireEvent("visibilitychange", this, false);
49138     },
49139
49140     /**
49141      * Shows this region if it was previously hidden.
49142      */
49143     show : function(){
49144         if(!this.collapsed){
49145             this.el.show();
49146         }else{
49147             this.collapsedEl.show();
49148         }
49149         this.visible = true;
49150         this.fireEvent("visibilitychange", this, true);
49151     },
49152
49153     closeClicked : function(){
49154         if(this.activePanel){
49155             this.remove(this.activePanel);
49156         }
49157     },
49158
49159     collapseClick : function(e){
49160         if(this.isSlid){
49161            e.stopPropagation();
49162            this.slideIn();
49163         }else{
49164            e.stopPropagation();
49165            this.slideOut();
49166         }
49167     },
49168
49169     /**
49170      * Collapses this region.
49171      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49172      */
49173     collapse : function(skipAnim){
49174         if(this.collapsed) return;
49175         this.collapsed = true;
49176         if(this.split){
49177             this.split.el.hide();
49178         }
49179         if(this.config.animate && skipAnim !== true){
49180             this.fireEvent("invalidated", this);
49181             this.animateCollapse();
49182         }else{
49183             this.el.setLocation(-20000,-20000);
49184             this.el.hide();
49185             this.collapsedEl.show();
49186             this.fireEvent("collapsed", this);
49187             this.fireEvent("invalidated", this);
49188         }
49189     },
49190
49191     animateCollapse : function(){
49192         // overridden
49193     },
49194
49195     /**
49196      * Expands this region if it was previously collapsed.
49197      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49198      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49199      */
49200     expand : function(e, skipAnim){
49201         if(e) e.stopPropagation();
49202         if(!this.collapsed || this.el.hasActiveFx()) return;
49203         if(this.isSlid){
49204             this.afterSlideIn();
49205             skipAnim = true;
49206         }
49207         this.collapsed = false;
49208         if(this.config.animate && skipAnim !== true){
49209             this.animateExpand();
49210         }else{
49211             this.el.show();
49212             if(this.split){
49213                 this.split.el.show();
49214             }
49215             this.collapsedEl.setLocation(-2000,-2000);
49216             this.collapsedEl.hide();
49217             this.fireEvent("invalidated", this);
49218             this.fireEvent("expanded", this);
49219         }
49220     },
49221
49222     animateExpand : function(){
49223         // overridden
49224     },
49225
49226     initTabs : function()
49227     {
49228         this.bodyEl.setStyle("overflow", "hidden");
49229         var ts = new Roo.TabPanel(
49230                 this.bodyEl.dom,
49231                 {
49232                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49233                     disableTooltips: this.config.disableTabTips,
49234                     toolbar : this.config.toolbar
49235                 }
49236         );
49237         if(this.config.hideTabs){
49238             ts.stripWrap.setDisplayed(false);
49239         }
49240         this.tabs = ts;
49241         ts.resizeTabs = this.config.resizeTabs === true;
49242         ts.minTabWidth = this.config.minTabWidth || 40;
49243         ts.maxTabWidth = this.config.maxTabWidth || 250;
49244         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49245         ts.monitorResize = false;
49246         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49247         ts.bodyEl.addClass('x-layout-tabs-body');
49248         this.panels.each(this.initPanelAsTab, this);
49249     },
49250
49251     initPanelAsTab : function(panel){
49252         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49253                     this.config.closeOnTab && panel.isClosable());
49254         if(panel.tabTip !== undefined){
49255             ti.setTooltip(panel.tabTip);
49256         }
49257         ti.on("activate", function(){
49258               this.setActivePanel(panel);
49259         }, this);
49260         if(this.config.closeOnTab){
49261             ti.on("beforeclose", function(t, e){
49262                 e.cancel = true;
49263                 this.remove(panel);
49264             }, this);
49265         }
49266         return ti;
49267     },
49268
49269     updatePanelTitle : function(panel, title){
49270         if(this.activePanel == panel){
49271             this.updateTitle(title);
49272         }
49273         if(this.tabs){
49274             var ti = this.tabs.getTab(panel.getEl().id);
49275             ti.setText(title);
49276             if(panel.tabTip !== undefined){
49277                 ti.setTooltip(panel.tabTip);
49278             }
49279         }
49280     },
49281
49282     updateTitle : function(title){
49283         if(this.titleTextEl && !this.config.title){
49284             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49285         }
49286     },
49287
49288     setActivePanel : function(panel){
49289         panel = this.getPanel(panel);
49290         if(this.activePanel && this.activePanel != panel){
49291             this.activePanel.setActiveState(false);
49292         }
49293         this.activePanel = panel;
49294         panel.setActiveState(true);
49295         if(this.panelSize){
49296             panel.setSize(this.panelSize.width, this.panelSize.height);
49297         }
49298         if(this.closeBtn){
49299             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49300         }
49301         this.updateTitle(panel.getTitle());
49302         if(this.tabs){
49303             this.fireEvent("invalidated", this);
49304         }
49305         this.fireEvent("panelactivated", this, panel);
49306     },
49307
49308     /**
49309      * Shows the specified panel.
49310      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49311      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49312      */
49313     showPanel : function(panel){
49314         if(panel = this.getPanel(panel)){
49315             if(this.tabs){
49316                 var tab = this.tabs.getTab(panel.getEl().id);
49317                 if(tab.isHidden()){
49318                     this.tabs.unhideTab(tab.id);
49319                 }
49320                 tab.activate();
49321             }else{
49322                 this.setActivePanel(panel);
49323             }
49324         }
49325         return panel;
49326     },
49327
49328     /**
49329      * Get the active panel for this region.
49330      * @return {Roo.ContentPanel} The active panel or null
49331      */
49332     getActivePanel : function(){
49333         return this.activePanel;
49334     },
49335
49336     validateVisibility : function(){
49337         if(this.panels.getCount() < 1){
49338             this.updateTitle("&#160;");
49339             this.closeBtn.hide();
49340             this.hide();
49341         }else{
49342             if(!this.isVisible()){
49343                 this.show();
49344             }
49345         }
49346     },
49347
49348     /**
49349      * Adds the passed ContentPanel(s) to this region.
49350      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49351      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49352      */
49353     add : function(panel){
49354         if(arguments.length > 1){
49355             for(var i = 0, len = arguments.length; i < len; i++) {
49356                 this.add(arguments[i]);
49357             }
49358             return null;
49359         }
49360         if(this.hasPanel(panel)){
49361             this.showPanel(panel);
49362             return panel;
49363         }
49364         panel.setRegion(this);
49365         this.panels.add(panel);
49366         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49367             this.bodyEl.dom.appendChild(panel.getEl().dom);
49368             if(panel.background !== true){
49369                 this.setActivePanel(panel);
49370             }
49371             this.fireEvent("paneladded", this, panel);
49372             return panel;
49373         }
49374         if(!this.tabs){
49375             this.initTabs();
49376         }else{
49377             this.initPanelAsTab(panel);
49378         }
49379         if(panel.background !== true){
49380             this.tabs.activate(panel.getEl().id);
49381         }
49382         this.fireEvent("paneladded", this, panel);
49383         return panel;
49384     },
49385
49386     /**
49387      * Hides the tab for the specified panel.
49388      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49389      */
49390     hidePanel : function(panel){
49391         if(this.tabs && (panel = this.getPanel(panel))){
49392             this.tabs.hideTab(panel.getEl().id);
49393         }
49394     },
49395
49396     /**
49397      * Unhides the tab for a previously hidden panel.
49398      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49399      */
49400     unhidePanel : function(panel){
49401         if(this.tabs && (panel = this.getPanel(panel))){
49402             this.tabs.unhideTab(panel.getEl().id);
49403         }
49404     },
49405
49406     clearPanels : function(){
49407         while(this.panels.getCount() > 0){
49408              this.remove(this.panels.first());
49409         }
49410     },
49411
49412     /**
49413      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49414      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49415      * @param {Boolean} preservePanel Overrides the config preservePanel option
49416      * @return {Roo.ContentPanel} The panel that was removed
49417      */
49418     remove : function(panel, preservePanel){
49419         panel = this.getPanel(panel);
49420         if(!panel){
49421             return null;
49422         }
49423         var e = {};
49424         this.fireEvent("beforeremove", this, panel, e);
49425         if(e.cancel === true){
49426             return null;
49427         }
49428         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49429         var panelId = panel.getId();
49430         this.panels.removeKey(panelId);
49431         if(preservePanel){
49432             document.body.appendChild(panel.getEl().dom);
49433         }
49434         if(this.tabs){
49435             this.tabs.removeTab(panel.getEl().id);
49436         }else if (!preservePanel){
49437             this.bodyEl.dom.removeChild(panel.getEl().dom);
49438         }
49439         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49440             var p = this.panels.first();
49441             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49442             tempEl.appendChild(p.getEl().dom);
49443             this.bodyEl.update("");
49444             this.bodyEl.dom.appendChild(p.getEl().dom);
49445             tempEl = null;
49446             this.updateTitle(p.getTitle());
49447             this.tabs = null;
49448             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49449             this.setActivePanel(p);
49450         }
49451         panel.setRegion(null);
49452         if(this.activePanel == panel){
49453             this.activePanel = null;
49454         }
49455         if(this.config.autoDestroy !== false && preservePanel !== true){
49456             try{panel.destroy();}catch(e){}
49457         }
49458         this.fireEvent("panelremoved", this, panel);
49459         return panel;
49460     },
49461
49462     /**
49463      * Returns the TabPanel component used by this region
49464      * @return {Roo.TabPanel}
49465      */
49466     getTabs : function(){
49467         return this.tabs;
49468     },
49469
49470     createTool : function(parentEl, className){
49471         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49472             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49473         btn.addClassOnOver("x-layout-tools-button-over");
49474         return btn;
49475     }
49476 });/*
49477  * Based on:
49478  * Ext JS Library 1.1.1
49479  * Copyright(c) 2006-2007, Ext JS, LLC.
49480  *
49481  * Originally Released Under LGPL - original licence link has changed is not relivant.
49482  *
49483  * Fork - LGPL
49484  * <script type="text/javascript">
49485  */
49486  
49487
49488
49489 /**
49490  * @class Roo.SplitLayoutRegion
49491  * @extends Roo.LayoutRegion
49492  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49493  */
49494 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49495     this.cursor = cursor;
49496     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49497 };
49498
49499 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49500     splitTip : "Drag to resize.",
49501     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49502     useSplitTips : false,
49503
49504     applyConfig : function(config){
49505         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49506         if(config.split){
49507             if(!this.split){
49508                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49509                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49510                 /** The SplitBar for this region 
49511                 * @type Roo.SplitBar */
49512                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49513                 this.split.on("moved", this.onSplitMove, this);
49514                 this.split.useShim = config.useShim === true;
49515                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49516                 if(this.useSplitTips){
49517                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49518                 }
49519                 if(config.collapsible){
49520                     this.split.el.on("dblclick", this.collapse,  this);
49521                 }
49522             }
49523             if(typeof config.minSize != "undefined"){
49524                 this.split.minSize = config.minSize;
49525             }
49526             if(typeof config.maxSize != "undefined"){
49527                 this.split.maxSize = config.maxSize;
49528             }
49529             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49530                 this.hideSplitter();
49531             }
49532         }
49533     },
49534
49535     getHMaxSize : function(){
49536          var cmax = this.config.maxSize || 10000;
49537          var center = this.mgr.getRegion("center");
49538          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49539     },
49540
49541     getVMaxSize : function(){
49542          var cmax = this.config.maxSize || 10000;
49543          var center = this.mgr.getRegion("center");
49544          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49545     },
49546
49547     onSplitMove : function(split, newSize){
49548         this.fireEvent("resized", this, newSize);
49549     },
49550     
49551     /** 
49552      * Returns the {@link Roo.SplitBar} for this region.
49553      * @return {Roo.SplitBar}
49554      */
49555     getSplitBar : function(){
49556         return this.split;
49557     },
49558     
49559     hide : function(){
49560         this.hideSplitter();
49561         Roo.SplitLayoutRegion.superclass.hide.call(this);
49562     },
49563
49564     hideSplitter : function(){
49565         if(this.split){
49566             this.split.el.setLocation(-2000,-2000);
49567             this.split.el.hide();
49568         }
49569     },
49570
49571     show : function(){
49572         if(this.split){
49573             this.split.el.show();
49574         }
49575         Roo.SplitLayoutRegion.superclass.show.call(this);
49576     },
49577     
49578     beforeSlide: function(){
49579         if(Roo.isGecko){// firefox overflow auto bug workaround
49580             this.bodyEl.clip();
49581             if(this.tabs) this.tabs.bodyEl.clip();
49582             if(this.activePanel){
49583                 this.activePanel.getEl().clip();
49584                 
49585                 if(this.activePanel.beforeSlide){
49586                     this.activePanel.beforeSlide();
49587                 }
49588             }
49589         }
49590     },
49591     
49592     afterSlide : function(){
49593         if(Roo.isGecko){// firefox overflow auto bug workaround
49594             this.bodyEl.unclip();
49595             if(this.tabs) this.tabs.bodyEl.unclip();
49596             if(this.activePanel){
49597                 this.activePanel.getEl().unclip();
49598                 if(this.activePanel.afterSlide){
49599                     this.activePanel.afterSlide();
49600                 }
49601             }
49602         }
49603     },
49604
49605     initAutoHide : function(){
49606         if(this.autoHide !== false){
49607             if(!this.autoHideHd){
49608                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49609                 this.autoHideHd = {
49610                     "mouseout": function(e){
49611                         if(!e.within(this.el, true)){
49612                             st.delay(500);
49613                         }
49614                     },
49615                     "mouseover" : function(e){
49616                         st.cancel();
49617                     },
49618                     scope : this
49619                 };
49620             }
49621             this.el.on(this.autoHideHd);
49622         }
49623     },
49624
49625     clearAutoHide : function(){
49626         if(this.autoHide !== false){
49627             this.el.un("mouseout", this.autoHideHd.mouseout);
49628             this.el.un("mouseover", this.autoHideHd.mouseover);
49629         }
49630     },
49631
49632     clearMonitor : function(){
49633         Roo.get(document).un("click", this.slideInIf, this);
49634     },
49635
49636     // these names are backwards but not changed for compat
49637     slideOut : function(){
49638         if(this.isSlid || this.el.hasActiveFx()){
49639             return;
49640         }
49641         this.isSlid = true;
49642         if(this.collapseBtn){
49643             this.collapseBtn.hide();
49644         }
49645         this.closeBtnState = this.closeBtn.getStyle('display');
49646         this.closeBtn.hide();
49647         if(this.stickBtn){
49648             this.stickBtn.show();
49649         }
49650         this.el.show();
49651         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49652         this.beforeSlide();
49653         this.el.setStyle("z-index", 10001);
49654         this.el.slideIn(this.getSlideAnchor(), {
49655             callback: function(){
49656                 this.afterSlide();
49657                 this.initAutoHide();
49658                 Roo.get(document).on("click", this.slideInIf, this);
49659                 this.fireEvent("slideshow", this);
49660             },
49661             scope: this,
49662             block: true
49663         });
49664     },
49665
49666     afterSlideIn : function(){
49667         this.clearAutoHide();
49668         this.isSlid = false;
49669         this.clearMonitor();
49670         this.el.setStyle("z-index", "");
49671         if(this.collapseBtn){
49672             this.collapseBtn.show();
49673         }
49674         this.closeBtn.setStyle('display', this.closeBtnState);
49675         if(this.stickBtn){
49676             this.stickBtn.hide();
49677         }
49678         this.fireEvent("slidehide", this);
49679     },
49680
49681     slideIn : function(cb){
49682         if(!this.isSlid || this.el.hasActiveFx()){
49683             Roo.callback(cb);
49684             return;
49685         }
49686         this.isSlid = false;
49687         this.beforeSlide();
49688         this.el.slideOut(this.getSlideAnchor(), {
49689             callback: function(){
49690                 this.el.setLeftTop(-10000, -10000);
49691                 this.afterSlide();
49692                 this.afterSlideIn();
49693                 Roo.callback(cb);
49694             },
49695             scope: this,
49696             block: true
49697         });
49698     },
49699     
49700     slideInIf : function(e){
49701         if(!e.within(this.el)){
49702             this.slideIn();
49703         }
49704     },
49705
49706     animateCollapse : function(){
49707         this.beforeSlide();
49708         this.el.setStyle("z-index", 20000);
49709         var anchor = this.getSlideAnchor();
49710         this.el.slideOut(anchor, {
49711             callback : function(){
49712                 this.el.setStyle("z-index", "");
49713                 this.collapsedEl.slideIn(anchor, {duration:.3});
49714                 this.afterSlide();
49715                 this.el.setLocation(-10000,-10000);
49716                 this.el.hide();
49717                 this.fireEvent("collapsed", this);
49718             },
49719             scope: this,
49720             block: true
49721         });
49722     },
49723
49724     animateExpand : function(){
49725         this.beforeSlide();
49726         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49727         this.el.setStyle("z-index", 20000);
49728         this.collapsedEl.hide({
49729             duration:.1
49730         });
49731         this.el.slideIn(this.getSlideAnchor(), {
49732             callback : function(){
49733                 this.el.setStyle("z-index", "");
49734                 this.afterSlide();
49735                 if(this.split){
49736                     this.split.el.show();
49737                 }
49738                 this.fireEvent("invalidated", this);
49739                 this.fireEvent("expanded", this);
49740             },
49741             scope: this,
49742             block: true
49743         });
49744     },
49745
49746     anchors : {
49747         "west" : "left",
49748         "east" : "right",
49749         "north" : "top",
49750         "south" : "bottom"
49751     },
49752
49753     sanchors : {
49754         "west" : "l",
49755         "east" : "r",
49756         "north" : "t",
49757         "south" : "b"
49758     },
49759
49760     canchors : {
49761         "west" : "tl-tr",
49762         "east" : "tr-tl",
49763         "north" : "tl-bl",
49764         "south" : "bl-tl"
49765     },
49766
49767     getAnchor : function(){
49768         return this.anchors[this.position];
49769     },
49770
49771     getCollapseAnchor : function(){
49772         return this.canchors[this.position];
49773     },
49774
49775     getSlideAnchor : function(){
49776         return this.sanchors[this.position];
49777     },
49778
49779     getAlignAdj : function(){
49780         var cm = this.cmargins;
49781         switch(this.position){
49782             case "west":
49783                 return [0, 0];
49784             break;
49785             case "east":
49786                 return [0, 0];
49787             break;
49788             case "north":
49789                 return [0, 0];
49790             break;
49791             case "south":
49792                 return [0, 0];
49793             break;
49794         }
49795     },
49796
49797     getExpandAdj : function(){
49798         var c = this.collapsedEl, cm = this.cmargins;
49799         switch(this.position){
49800             case "west":
49801                 return [-(cm.right+c.getWidth()+cm.left), 0];
49802             break;
49803             case "east":
49804                 return [cm.right+c.getWidth()+cm.left, 0];
49805             break;
49806             case "north":
49807                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49808             break;
49809             case "south":
49810                 return [0, cm.top+cm.bottom+c.getHeight()];
49811             break;
49812         }
49813     }
49814 });/*
49815  * Based on:
49816  * Ext JS Library 1.1.1
49817  * Copyright(c) 2006-2007, Ext JS, LLC.
49818  *
49819  * Originally Released Under LGPL - original licence link has changed is not relivant.
49820  *
49821  * Fork - LGPL
49822  * <script type="text/javascript">
49823  */
49824 /*
49825  * These classes are private internal classes
49826  */
49827 Roo.CenterLayoutRegion = function(mgr, config){
49828     Roo.LayoutRegion.call(this, mgr, config, "center");
49829     this.visible = true;
49830     this.minWidth = config.minWidth || 20;
49831     this.minHeight = config.minHeight || 20;
49832 };
49833
49834 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49835     hide : function(){
49836         // center panel can't be hidden
49837     },
49838     
49839     show : function(){
49840         // center panel can't be hidden
49841     },
49842     
49843     getMinWidth: function(){
49844         return this.minWidth;
49845     },
49846     
49847     getMinHeight: function(){
49848         return this.minHeight;
49849     }
49850 });
49851
49852
49853 Roo.NorthLayoutRegion = function(mgr, config){
49854     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49855     if(this.split){
49856         this.split.placement = Roo.SplitBar.TOP;
49857         this.split.orientation = Roo.SplitBar.VERTICAL;
49858         this.split.el.addClass("x-layout-split-v");
49859     }
49860     var size = config.initialSize || config.height;
49861     if(typeof size != "undefined"){
49862         this.el.setHeight(size);
49863     }
49864 };
49865 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49866     orientation: Roo.SplitBar.VERTICAL,
49867     getBox : function(){
49868         if(this.collapsed){
49869             return this.collapsedEl.getBox();
49870         }
49871         var box = this.el.getBox();
49872         if(this.split){
49873             box.height += this.split.el.getHeight();
49874         }
49875         return box;
49876     },
49877     
49878     updateBox : function(box){
49879         if(this.split && !this.collapsed){
49880             box.height -= this.split.el.getHeight();
49881             this.split.el.setLeft(box.x);
49882             this.split.el.setTop(box.y+box.height);
49883             this.split.el.setWidth(box.width);
49884         }
49885         if(this.collapsed){
49886             this.updateBody(box.width, null);
49887         }
49888         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49889     }
49890 });
49891
49892 Roo.SouthLayoutRegion = function(mgr, config){
49893     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49894     if(this.split){
49895         this.split.placement = Roo.SplitBar.BOTTOM;
49896         this.split.orientation = Roo.SplitBar.VERTICAL;
49897         this.split.el.addClass("x-layout-split-v");
49898     }
49899     var size = config.initialSize || config.height;
49900     if(typeof size != "undefined"){
49901         this.el.setHeight(size);
49902     }
49903 };
49904 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49905     orientation: Roo.SplitBar.VERTICAL,
49906     getBox : function(){
49907         if(this.collapsed){
49908             return this.collapsedEl.getBox();
49909         }
49910         var box = this.el.getBox();
49911         if(this.split){
49912             var sh = this.split.el.getHeight();
49913             box.height += sh;
49914             box.y -= sh;
49915         }
49916         return box;
49917     },
49918     
49919     updateBox : function(box){
49920         if(this.split && !this.collapsed){
49921             var sh = this.split.el.getHeight();
49922             box.height -= sh;
49923             box.y += sh;
49924             this.split.el.setLeft(box.x);
49925             this.split.el.setTop(box.y-sh);
49926             this.split.el.setWidth(box.width);
49927         }
49928         if(this.collapsed){
49929             this.updateBody(box.width, null);
49930         }
49931         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49932     }
49933 });
49934
49935 Roo.EastLayoutRegion = function(mgr, config){
49936     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49937     if(this.split){
49938         this.split.placement = Roo.SplitBar.RIGHT;
49939         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49940         this.split.el.addClass("x-layout-split-h");
49941     }
49942     var size = config.initialSize || config.width;
49943     if(typeof size != "undefined"){
49944         this.el.setWidth(size);
49945     }
49946 };
49947 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49948     orientation: Roo.SplitBar.HORIZONTAL,
49949     getBox : function(){
49950         if(this.collapsed){
49951             return this.collapsedEl.getBox();
49952         }
49953         var box = this.el.getBox();
49954         if(this.split){
49955             var sw = this.split.el.getWidth();
49956             box.width += sw;
49957             box.x -= sw;
49958         }
49959         return box;
49960     },
49961
49962     updateBox : function(box){
49963         if(this.split && !this.collapsed){
49964             var sw = this.split.el.getWidth();
49965             box.width -= sw;
49966             this.split.el.setLeft(box.x);
49967             this.split.el.setTop(box.y);
49968             this.split.el.setHeight(box.height);
49969             box.x += sw;
49970         }
49971         if(this.collapsed){
49972             this.updateBody(null, box.height);
49973         }
49974         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49975     }
49976 });
49977
49978 Roo.WestLayoutRegion = function(mgr, config){
49979     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49980     if(this.split){
49981         this.split.placement = Roo.SplitBar.LEFT;
49982         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49983         this.split.el.addClass("x-layout-split-h");
49984     }
49985     var size = config.initialSize || config.width;
49986     if(typeof size != "undefined"){
49987         this.el.setWidth(size);
49988     }
49989 };
49990 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
49991     orientation: Roo.SplitBar.HORIZONTAL,
49992     getBox : function(){
49993         if(this.collapsed){
49994             return this.collapsedEl.getBox();
49995         }
49996         var box = this.el.getBox();
49997         if(this.split){
49998             box.width += this.split.el.getWidth();
49999         }
50000         return box;
50001     },
50002     
50003     updateBox : function(box){
50004         if(this.split && !this.collapsed){
50005             var sw = this.split.el.getWidth();
50006             box.width -= sw;
50007             this.split.el.setLeft(box.x+box.width);
50008             this.split.el.setTop(box.y);
50009             this.split.el.setHeight(box.height);
50010         }
50011         if(this.collapsed){
50012             this.updateBody(null, box.height);
50013         }
50014         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50015     }
50016 });
50017 /*
50018  * Based on:
50019  * Ext JS Library 1.1.1
50020  * Copyright(c) 2006-2007, Ext JS, LLC.
50021  *
50022  * Originally Released Under LGPL - original licence link has changed is not relivant.
50023  *
50024  * Fork - LGPL
50025  * <script type="text/javascript">
50026  */
50027  
50028  
50029 /*
50030  * Private internal class for reading and applying state
50031  */
50032 Roo.LayoutStateManager = function(layout){
50033      // default empty state
50034      this.state = {
50035         north: {},
50036         south: {},
50037         east: {},
50038         west: {}       
50039     };
50040 };
50041
50042 Roo.LayoutStateManager.prototype = {
50043     init : function(layout, provider){
50044         this.provider = provider;
50045         var state = provider.get(layout.id+"-layout-state");
50046         if(state){
50047             var wasUpdating = layout.isUpdating();
50048             if(!wasUpdating){
50049                 layout.beginUpdate();
50050             }
50051             for(var key in state){
50052                 if(typeof state[key] != "function"){
50053                     var rstate = state[key];
50054                     var r = layout.getRegion(key);
50055                     if(r && rstate){
50056                         if(rstate.size){
50057                             r.resizeTo(rstate.size);
50058                         }
50059                         if(rstate.collapsed == true){
50060                             r.collapse(true);
50061                         }else{
50062                             r.expand(null, true);
50063                         }
50064                     }
50065                 }
50066             }
50067             if(!wasUpdating){
50068                 layout.endUpdate();
50069             }
50070             this.state = state; 
50071         }
50072         this.layout = layout;
50073         layout.on("regionresized", this.onRegionResized, this);
50074         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50075         layout.on("regionexpanded", this.onRegionExpanded, this);
50076     },
50077     
50078     storeState : function(){
50079         this.provider.set(this.layout.id+"-layout-state", this.state);
50080     },
50081     
50082     onRegionResized : function(region, newSize){
50083         this.state[region.getPosition()].size = newSize;
50084         this.storeState();
50085     },
50086     
50087     onRegionCollapsed : function(region){
50088         this.state[region.getPosition()].collapsed = true;
50089         this.storeState();
50090     },
50091     
50092     onRegionExpanded : function(region){
50093         this.state[region.getPosition()].collapsed = false;
50094         this.storeState();
50095     }
50096 };/*
50097  * Based on:
50098  * Ext JS Library 1.1.1
50099  * Copyright(c) 2006-2007, Ext JS, LLC.
50100  *
50101  * Originally Released Under LGPL - original licence link has changed is not relivant.
50102  *
50103  * Fork - LGPL
50104  * <script type="text/javascript">
50105  */
50106 /**
50107  * @class Roo.ContentPanel
50108  * @extends Roo.util.Observable
50109  * A basic ContentPanel element.
50110  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50111  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50112  * @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
50113  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50114  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50115  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50116  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50117  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50118  * @cfg {String} title          The title for this panel
50119  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50120  * @cfg {String} url            Calls {@link #setUrl} with this value
50121  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50122  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50123  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50124  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50125
50126  * @constructor
50127  * Create a new ContentPanel.
50128  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50129  * @param {String/Object} config A string to set only the title or a config object
50130  * @param {String} content (optional) Set the HTML content for this panel
50131  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50132  */
50133 Roo.ContentPanel = function(el, config, content){
50134     
50135      
50136     /*
50137     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50138         config = el;
50139         el = Roo.id();
50140     }
50141     if (config && config.parentLayout) { 
50142         el = config.parentLayout.el.createChild(); 
50143     }
50144     */
50145     if(el.autoCreate){ // xtype is available if this is called from factory
50146         config = el;
50147         el = Roo.id();
50148     }
50149     this.el = Roo.get(el);
50150     if(!this.el && config && config.autoCreate){
50151         if(typeof config.autoCreate == "object"){
50152             if(!config.autoCreate.id){
50153                 config.autoCreate.id = config.id||el;
50154             }
50155             this.el = Roo.DomHelper.append(document.body,
50156                         config.autoCreate, true);
50157         }else{
50158             this.el = Roo.DomHelper.append(document.body,
50159                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50160         }
50161     }
50162     this.closable = false;
50163     this.loaded = false;
50164     this.active = false;
50165     if(typeof config == "string"){
50166         this.title = config;
50167     }else{
50168         Roo.apply(this, config);
50169     }
50170     
50171     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50172         this.wrapEl = this.el.wrap();
50173         this.toolbar.container = this.el.insertSibling(false, 'before');
50174         this.toolbar = new Roo.Toolbar(this.toolbar);
50175     }
50176     
50177     // xtype created footer. - not sure if will work as we normally have to render first..
50178     if (this.footer && !this.footer.el && this.footer.xtype) {
50179         if (!this.wrapEl) {
50180             this.wrapEl = this.el.wrap();
50181         }
50182     
50183         this.footer.container = this.wrapEl.createChild();
50184          
50185         this.footer = Roo.factory(this.footer, Roo);
50186         
50187     }
50188     
50189     if(this.resizeEl){
50190         this.resizeEl = Roo.get(this.resizeEl, true);
50191     }else{
50192         this.resizeEl = this.el;
50193     }
50194     // handle view.xtype
50195     
50196  
50197     
50198     
50199     this.addEvents({
50200         /**
50201          * @event activate
50202          * Fires when this panel is activated. 
50203          * @param {Roo.ContentPanel} this
50204          */
50205         "activate" : true,
50206         /**
50207          * @event deactivate
50208          * Fires when this panel is activated. 
50209          * @param {Roo.ContentPanel} this
50210          */
50211         "deactivate" : true,
50212
50213         /**
50214          * @event resize
50215          * Fires when this panel is resized if fitToFrame is true.
50216          * @param {Roo.ContentPanel} this
50217          * @param {Number} width The width after any component adjustments
50218          * @param {Number} height The height after any component adjustments
50219          */
50220         "resize" : true,
50221         
50222          /**
50223          * @event render
50224          * Fires when this tab is created
50225          * @param {Roo.ContentPanel} this
50226          */
50227         "render" : true
50228         
50229         
50230         
50231     });
50232     
50233
50234     
50235     
50236     if(this.autoScroll){
50237         this.resizeEl.setStyle("overflow", "auto");
50238     } else {
50239         // fix randome scrolling
50240         this.el.on('scroll', function() {
50241             Roo.log('fix random scolling');
50242             this.scrollTo('top',0); 
50243         });
50244     }
50245     content = content || this.content;
50246     if(content){
50247         this.setContent(content);
50248     }
50249     if(config && config.url){
50250         this.setUrl(this.url, this.params, this.loadOnce);
50251     }
50252     
50253     
50254     
50255     Roo.ContentPanel.superclass.constructor.call(this);
50256     
50257     if (this.view && typeof(this.view.xtype) != 'undefined') {
50258         this.view.el = this.el.appendChild(document.createElement("div"));
50259         this.view = Roo.factory(this.view); 
50260         this.view.render  &&  this.view.render(false, '');  
50261     }
50262     
50263     
50264     this.fireEvent('render', this);
50265 };
50266
50267 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50268     tabTip:'',
50269     setRegion : function(region){
50270         this.region = region;
50271         if(region){
50272            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50273         }else{
50274            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50275         } 
50276     },
50277     
50278     /**
50279      * Returns the toolbar for this Panel if one was configured. 
50280      * @return {Roo.Toolbar} 
50281      */
50282     getToolbar : function(){
50283         return this.toolbar;
50284     },
50285     
50286     setActiveState : function(active){
50287         this.active = active;
50288         if(!active){
50289             this.fireEvent("deactivate", this);
50290         }else{
50291             this.fireEvent("activate", this);
50292         }
50293     },
50294     /**
50295      * Updates this panel's element
50296      * @param {String} content The new content
50297      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50298     */
50299     setContent : function(content, loadScripts){
50300         this.el.update(content, loadScripts);
50301     },
50302
50303     ignoreResize : function(w, h){
50304         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50305             return true;
50306         }else{
50307             this.lastSize = {width: w, height: h};
50308             return false;
50309         }
50310     },
50311     /**
50312      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50313      * @return {Roo.UpdateManager} The UpdateManager
50314      */
50315     getUpdateManager : function(){
50316         return this.el.getUpdateManager();
50317     },
50318      /**
50319      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50320      * @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:
50321 <pre><code>
50322 panel.load({
50323     url: "your-url.php",
50324     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50325     callback: yourFunction,
50326     scope: yourObject, //(optional scope)
50327     discardUrl: false,
50328     nocache: false,
50329     text: "Loading...",
50330     timeout: 30,
50331     scripts: false
50332 });
50333 </code></pre>
50334      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50335      * 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.
50336      * @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}
50337      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50338      * @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.
50339      * @return {Roo.ContentPanel} this
50340      */
50341     load : function(){
50342         var um = this.el.getUpdateManager();
50343         um.update.apply(um, arguments);
50344         return this;
50345     },
50346
50347
50348     /**
50349      * 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.
50350      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50351      * @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)
50352      * @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)
50353      * @return {Roo.UpdateManager} The UpdateManager
50354      */
50355     setUrl : function(url, params, loadOnce){
50356         if(this.refreshDelegate){
50357             this.removeListener("activate", this.refreshDelegate);
50358         }
50359         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50360         this.on("activate", this.refreshDelegate);
50361         return this.el.getUpdateManager();
50362     },
50363     
50364     _handleRefresh : function(url, params, loadOnce){
50365         if(!loadOnce || !this.loaded){
50366             var updater = this.el.getUpdateManager();
50367             updater.update(url, params, this._setLoaded.createDelegate(this));
50368         }
50369     },
50370     
50371     _setLoaded : function(){
50372         this.loaded = true;
50373     }, 
50374     
50375     /**
50376      * Returns this panel's id
50377      * @return {String} 
50378      */
50379     getId : function(){
50380         return this.el.id;
50381     },
50382     
50383     /** 
50384      * Returns this panel's element - used by regiosn to add.
50385      * @return {Roo.Element} 
50386      */
50387     getEl : function(){
50388         return this.wrapEl || this.el;
50389     },
50390     
50391     adjustForComponents : function(width, height)
50392     {
50393         //Roo.log('adjustForComponents ');
50394         if(this.resizeEl != this.el){
50395             width -= this.el.getFrameWidth('lr');
50396             height -= this.el.getFrameWidth('tb');
50397         }
50398         if(this.toolbar){
50399             var te = this.toolbar.getEl();
50400             height -= te.getHeight();
50401             te.setWidth(width);
50402         }
50403         if(this.footer){
50404             var te = this.footer.getEl();
50405             Roo.log("footer:" + te.getHeight());
50406             
50407             height -= te.getHeight();
50408             te.setWidth(width);
50409         }
50410         
50411         
50412         if(this.adjustments){
50413             width += this.adjustments[0];
50414             height += this.adjustments[1];
50415         }
50416         return {"width": width, "height": height};
50417     },
50418     
50419     setSize : function(width, height){
50420         if(this.fitToFrame && !this.ignoreResize(width, height)){
50421             if(this.fitContainer && this.resizeEl != this.el){
50422                 this.el.setSize(width, height);
50423             }
50424             var size = this.adjustForComponents(width, height);
50425             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50426             this.fireEvent('resize', this, size.width, size.height);
50427         }
50428     },
50429     
50430     /**
50431      * Returns this panel's title
50432      * @return {String} 
50433      */
50434     getTitle : function(){
50435         return this.title;
50436     },
50437     
50438     /**
50439      * Set this panel's title
50440      * @param {String} title
50441      */
50442     setTitle : function(title){
50443         this.title = title;
50444         if(this.region){
50445             this.region.updatePanelTitle(this, title);
50446         }
50447     },
50448     
50449     /**
50450      * Returns true is this panel was configured to be closable
50451      * @return {Boolean} 
50452      */
50453     isClosable : function(){
50454         return this.closable;
50455     },
50456     
50457     beforeSlide : function(){
50458         this.el.clip();
50459         this.resizeEl.clip();
50460     },
50461     
50462     afterSlide : function(){
50463         this.el.unclip();
50464         this.resizeEl.unclip();
50465     },
50466     
50467     /**
50468      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50469      *   Will fail silently if the {@link #setUrl} method has not been called.
50470      *   This does not activate the panel, just updates its content.
50471      */
50472     refresh : function(){
50473         if(this.refreshDelegate){
50474            this.loaded = false;
50475            this.refreshDelegate();
50476         }
50477     },
50478     
50479     /**
50480      * Destroys this panel
50481      */
50482     destroy : function(){
50483         this.el.removeAllListeners();
50484         var tempEl = document.createElement("span");
50485         tempEl.appendChild(this.el.dom);
50486         tempEl.innerHTML = "";
50487         this.el.remove();
50488         this.el = null;
50489     },
50490     
50491     /**
50492      * form - if the content panel contains a form - this is a reference to it.
50493      * @type {Roo.form.Form}
50494      */
50495     form : false,
50496     /**
50497      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50498      *    This contains a reference to it.
50499      * @type {Roo.View}
50500      */
50501     view : false,
50502     
50503       /**
50504      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50505      * <pre><code>
50506
50507 layout.addxtype({
50508        xtype : 'Form',
50509        items: [ .... ]
50510    }
50511 );
50512
50513 </code></pre>
50514      * @param {Object} cfg Xtype definition of item to add.
50515      */
50516     
50517     addxtype : function(cfg) {
50518         // add form..
50519         if (cfg.xtype.match(/^Form$/)) {
50520             
50521             var el;
50522             //if (this.footer) {
50523             //    el = this.footer.container.insertSibling(false, 'before');
50524             //} else {
50525                 el = this.el.createChild();
50526             //}
50527
50528             this.form = new  Roo.form.Form(cfg);
50529             
50530             
50531             if ( this.form.allItems.length) this.form.render(el.dom);
50532             return this.form;
50533         }
50534         // should only have one of theses..
50535         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50536             // views.. should not be just added - used named prop 'view''
50537             
50538             cfg.el = this.el.appendChild(document.createElement("div"));
50539             // factory?
50540             
50541             var ret = new Roo.factory(cfg);
50542              
50543              ret.render && ret.render(false, ''); // render blank..
50544             this.view = ret;
50545             return ret;
50546         }
50547         return false;
50548     }
50549 });
50550
50551 /**
50552  * @class Roo.GridPanel
50553  * @extends Roo.ContentPanel
50554  * @constructor
50555  * Create a new GridPanel.
50556  * @param {Roo.grid.Grid} grid The grid for this panel
50557  * @param {String/Object} config A string to set only the panel's title, or a config object
50558  */
50559 Roo.GridPanel = function(grid, config){
50560     
50561   
50562     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50563         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50564         
50565     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50566     
50567     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50568     
50569     if(this.toolbar){
50570         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50571     }
50572     // xtype created footer. - not sure if will work as we normally have to render first..
50573     if (this.footer && !this.footer.el && this.footer.xtype) {
50574         
50575         this.footer.container = this.grid.getView().getFooterPanel(true);
50576         this.footer.dataSource = this.grid.dataSource;
50577         this.footer = Roo.factory(this.footer, Roo);
50578         
50579     }
50580     
50581     grid.monitorWindowResize = false; // turn off autosizing
50582     grid.autoHeight = false;
50583     grid.autoWidth = false;
50584     this.grid = grid;
50585     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50586 };
50587
50588 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50589     getId : function(){
50590         return this.grid.id;
50591     },
50592     
50593     /**
50594      * Returns the grid for this panel
50595      * @return {Roo.grid.Grid} 
50596      */
50597     getGrid : function(){
50598         return this.grid;    
50599     },
50600     
50601     setSize : function(width, height){
50602         if(!this.ignoreResize(width, height)){
50603             var grid = this.grid;
50604             var size = this.adjustForComponents(width, height);
50605             grid.getGridEl().setSize(size.width, size.height);
50606             grid.autoSize();
50607         }
50608     },
50609     
50610     beforeSlide : function(){
50611         this.grid.getView().scroller.clip();
50612     },
50613     
50614     afterSlide : function(){
50615         this.grid.getView().scroller.unclip();
50616     },
50617     
50618     destroy : function(){
50619         this.grid.destroy();
50620         delete this.grid;
50621         Roo.GridPanel.superclass.destroy.call(this); 
50622     }
50623 });
50624
50625
50626 /**
50627  * @class Roo.NestedLayoutPanel
50628  * @extends Roo.ContentPanel
50629  * @constructor
50630  * Create a new NestedLayoutPanel.
50631  * 
50632  * 
50633  * @param {Roo.BorderLayout} layout The layout for this panel
50634  * @param {String/Object} config A string to set only the title or a config object
50635  */
50636 Roo.NestedLayoutPanel = function(layout, config)
50637 {
50638     // construct with only one argument..
50639     /* FIXME - implement nicer consturctors
50640     if (layout.layout) {
50641         config = layout;
50642         layout = config.layout;
50643         delete config.layout;
50644     }
50645     if (layout.xtype && !layout.getEl) {
50646         // then layout needs constructing..
50647         layout = Roo.factory(layout, Roo);
50648     }
50649     */
50650     
50651     
50652     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50653     
50654     layout.monitorWindowResize = false; // turn off autosizing
50655     this.layout = layout;
50656     this.layout.getEl().addClass("x-layout-nested-layout");
50657     
50658     
50659     
50660     
50661 };
50662
50663 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
50664
50665     setSize : function(width, height){
50666         if(!this.ignoreResize(width, height)){
50667             var size = this.adjustForComponents(width, height);
50668             var el = this.layout.getEl();
50669             el.setSize(size.width, size.height);
50670             var touch = el.dom.offsetWidth;
50671             this.layout.layout();
50672             // ie requires a double layout on the first pass
50673             if(Roo.isIE && !this.initialized){
50674                 this.initialized = true;
50675                 this.layout.layout();
50676             }
50677         }
50678     },
50679     
50680     // activate all subpanels if not currently active..
50681     
50682     setActiveState : function(active){
50683         this.active = active;
50684         if(!active){
50685             this.fireEvent("deactivate", this);
50686             return;
50687         }
50688         
50689         this.fireEvent("activate", this);
50690         // not sure if this should happen before or after..
50691         if (!this.layout) {
50692             return; // should not happen..
50693         }
50694         var reg = false;
50695         for (var r in this.layout.regions) {
50696             reg = this.layout.getRegion(r);
50697             if (reg.getActivePanel()) {
50698                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
50699                 reg.setActivePanel(reg.getActivePanel());
50700                 continue;
50701             }
50702             if (!reg.panels.length) {
50703                 continue;
50704             }
50705             reg.showPanel(reg.getPanel(0));
50706         }
50707         
50708         
50709         
50710         
50711     },
50712     
50713     /**
50714      * Returns the nested BorderLayout for this panel
50715      * @return {Roo.BorderLayout} 
50716      */
50717     getLayout : function(){
50718         return this.layout;
50719     },
50720     
50721      /**
50722      * Adds a xtype elements to the layout of the nested panel
50723      * <pre><code>
50724
50725 panel.addxtype({
50726        xtype : 'ContentPanel',
50727        region: 'west',
50728        items: [ .... ]
50729    }
50730 );
50731
50732 panel.addxtype({
50733         xtype : 'NestedLayoutPanel',
50734         region: 'west',
50735         layout: {
50736            center: { },
50737            west: { }   
50738         },
50739         items : [ ... list of content panels or nested layout panels.. ]
50740    }
50741 );
50742 </code></pre>
50743      * @param {Object} cfg Xtype definition of item to add.
50744      */
50745     addxtype : function(cfg) {
50746         return this.layout.addxtype(cfg);
50747     
50748     }
50749 });
50750
50751 Roo.ScrollPanel = function(el, config, content){
50752     config = config || {};
50753     config.fitToFrame = true;
50754     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50755     
50756     this.el.dom.style.overflow = "hidden";
50757     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50758     this.el.removeClass("x-layout-inactive-content");
50759     this.el.on("mousewheel", this.onWheel, this);
50760
50761     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50762     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50763     up.unselectable(); down.unselectable();
50764     up.on("click", this.scrollUp, this);
50765     down.on("click", this.scrollDown, this);
50766     up.addClassOnOver("x-scroller-btn-over");
50767     down.addClassOnOver("x-scroller-btn-over");
50768     up.addClassOnClick("x-scroller-btn-click");
50769     down.addClassOnClick("x-scroller-btn-click");
50770     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50771
50772     this.resizeEl = this.el;
50773     this.el = wrap; this.up = up; this.down = down;
50774 };
50775
50776 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50777     increment : 100,
50778     wheelIncrement : 5,
50779     scrollUp : function(){
50780         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50781     },
50782
50783     scrollDown : function(){
50784         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50785     },
50786
50787     afterScroll : function(){
50788         var el = this.resizeEl;
50789         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50790         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50791         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50792     },
50793
50794     setSize : function(){
50795         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50796         this.afterScroll();
50797     },
50798
50799     onWheel : function(e){
50800         var d = e.getWheelDelta();
50801         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50802         this.afterScroll();
50803         e.stopEvent();
50804     },
50805
50806     setContent : function(content, loadScripts){
50807         this.resizeEl.update(content, loadScripts);
50808     }
50809
50810 });
50811
50812
50813
50814
50815
50816
50817
50818
50819
50820 /**
50821  * @class Roo.TreePanel
50822  * @extends Roo.ContentPanel
50823  * @constructor
50824  * Create a new TreePanel. - defaults to fit/scoll contents.
50825  * @param {String/Object} config A string to set only the panel's title, or a config object
50826  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50827  */
50828 Roo.TreePanel = function(config){
50829     var el = config.el;
50830     var tree = config.tree;
50831     delete config.tree; 
50832     delete config.el; // hopefull!
50833     
50834     // wrapper for IE7 strict & safari scroll issue
50835     
50836     var treeEl = el.createChild();
50837     config.resizeEl = treeEl;
50838     
50839     
50840     
50841     Roo.TreePanel.superclass.constructor.call(this, el, config);
50842  
50843  
50844     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50845     //console.log(tree);
50846     this.on('activate', function()
50847     {
50848         if (this.tree.rendered) {
50849             return;
50850         }
50851         //console.log('render tree');
50852         this.tree.render();
50853     });
50854     // this should not be needed.. - it's actually the 'el' that resizes?
50855     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50856     
50857     //this.on('resize',  function (cp, w, h) {
50858     //        this.tree.innerCt.setWidth(w);
50859     //        this.tree.innerCt.setHeight(h);
50860     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50861     //});
50862
50863         
50864     
50865 };
50866
50867 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50868     fitToFrame : true,
50869     autoScroll : true
50870 });
50871
50872
50873
50874
50875
50876
50877
50878
50879
50880
50881
50882 /*
50883  * Based on:
50884  * Ext JS Library 1.1.1
50885  * Copyright(c) 2006-2007, Ext JS, LLC.
50886  *
50887  * Originally Released Under LGPL - original licence link has changed is not relivant.
50888  *
50889  * Fork - LGPL
50890  * <script type="text/javascript">
50891  */
50892  
50893
50894 /**
50895  * @class Roo.ReaderLayout
50896  * @extends Roo.BorderLayout
50897  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50898  * center region containing two nested regions (a top one for a list view and one for item preview below),
50899  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50900  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50901  * expedites the setup of the overall layout and regions for this common application style.
50902  * Example:
50903  <pre><code>
50904 var reader = new Roo.ReaderLayout();
50905 var CP = Roo.ContentPanel;  // shortcut for adding
50906
50907 reader.beginUpdate();
50908 reader.add("north", new CP("north", "North"));
50909 reader.add("west", new CP("west", {title: "West"}));
50910 reader.add("east", new CP("east", {title: "East"}));
50911
50912 reader.regions.listView.add(new CP("listView", "List"));
50913 reader.regions.preview.add(new CP("preview", "Preview"));
50914 reader.endUpdate();
50915 </code></pre>
50916 * @constructor
50917 * Create a new ReaderLayout
50918 * @param {Object} config Configuration options
50919 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50920 * document.body if omitted)
50921 */
50922 Roo.ReaderLayout = function(config, renderTo){
50923     var c = config || {size:{}};
50924     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50925         north: c.north !== false ? Roo.apply({
50926             split:false,
50927             initialSize: 32,
50928             titlebar: false
50929         }, c.north) : false,
50930         west: c.west !== false ? Roo.apply({
50931             split:true,
50932             initialSize: 200,
50933             minSize: 175,
50934             maxSize: 400,
50935             titlebar: true,
50936             collapsible: true,
50937             animate: true,
50938             margins:{left:5,right:0,bottom:5,top:5},
50939             cmargins:{left:5,right:5,bottom:5,top:5}
50940         }, c.west) : false,
50941         east: c.east !== false ? Roo.apply({
50942             split:true,
50943             initialSize: 200,
50944             minSize: 175,
50945             maxSize: 400,
50946             titlebar: true,
50947             collapsible: true,
50948             animate: true,
50949             margins:{left:0,right:5,bottom:5,top:5},
50950             cmargins:{left:5,right:5,bottom:5,top:5}
50951         }, c.east) : false,
50952         center: Roo.apply({
50953             tabPosition: 'top',
50954             autoScroll:false,
50955             closeOnTab: true,
50956             titlebar:false,
50957             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50958         }, c.center)
50959     });
50960
50961     this.el.addClass('x-reader');
50962
50963     this.beginUpdate();
50964
50965     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50966         south: c.preview !== false ? Roo.apply({
50967             split:true,
50968             initialSize: 200,
50969             minSize: 100,
50970             autoScroll:true,
50971             collapsible:true,
50972             titlebar: true,
50973             cmargins:{top:5,left:0, right:0, bottom:0}
50974         }, c.preview) : false,
50975         center: Roo.apply({
50976             autoScroll:false,
50977             titlebar:false,
50978             minHeight:200
50979         }, c.listView)
50980     });
50981     this.add('center', new Roo.NestedLayoutPanel(inner,
50982             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50983
50984     this.endUpdate();
50985
50986     this.regions.preview = inner.getRegion('south');
50987     this.regions.listView = inner.getRegion('center');
50988 };
50989
50990 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
50991  * Based on:
50992  * Ext JS Library 1.1.1
50993  * Copyright(c) 2006-2007, Ext JS, LLC.
50994  *
50995  * Originally Released Under LGPL - original licence link has changed is not relivant.
50996  *
50997  * Fork - LGPL
50998  * <script type="text/javascript">
50999  */
51000  
51001 /**
51002  * @class Roo.grid.Grid
51003  * @extends Roo.util.Observable
51004  * This class represents the primary interface of a component based grid control.
51005  * <br><br>Usage:<pre><code>
51006  var grid = new Roo.grid.Grid("my-container-id", {
51007      ds: myDataStore,
51008      cm: myColModel,
51009      selModel: mySelectionModel,
51010      autoSizeColumns: true,
51011      monitorWindowResize: false,
51012      trackMouseOver: true
51013  });
51014  // set any options
51015  grid.render();
51016  * </code></pre>
51017  * <b>Common Problems:</b><br/>
51018  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51019  * element will correct this<br/>
51020  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51021  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51022  * are unpredictable.<br/>
51023  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51024  * grid to calculate dimensions/offsets.<br/>
51025   * @constructor
51026  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51027  * The container MUST have some type of size defined for the grid to fill. The container will be
51028  * automatically set to position relative if it isn't already.
51029  * @param {Object} config A config object that sets properties on this grid.
51030  */
51031 Roo.grid.Grid = function(container, config){
51032         // initialize the container
51033         this.container = Roo.get(container);
51034         this.container.update("");
51035         this.container.setStyle("overflow", "hidden");
51036     this.container.addClass('x-grid-container');
51037
51038     this.id = this.container.id;
51039
51040     Roo.apply(this, config);
51041     // check and correct shorthanded configs
51042     if(this.ds){
51043         this.dataSource = this.ds;
51044         delete this.ds;
51045     }
51046     if(this.cm){
51047         this.colModel = this.cm;
51048         delete this.cm;
51049     }
51050     if(this.sm){
51051         this.selModel = this.sm;
51052         delete this.sm;
51053     }
51054
51055     if (this.selModel) {
51056         this.selModel = Roo.factory(this.selModel, Roo.grid);
51057         this.sm = this.selModel;
51058         this.sm.xmodule = this.xmodule || false;
51059     }
51060     if (typeof(this.colModel.config) == 'undefined') {
51061         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51062         this.cm = this.colModel;
51063         this.cm.xmodule = this.xmodule || false;
51064     }
51065     if (this.dataSource) {
51066         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51067         this.ds = this.dataSource;
51068         this.ds.xmodule = this.xmodule || false;
51069          
51070     }
51071     
51072     
51073     
51074     if(this.width){
51075         this.container.setWidth(this.width);
51076     }
51077
51078     if(this.height){
51079         this.container.setHeight(this.height);
51080     }
51081     /** @private */
51082         this.addEvents({
51083         // raw events
51084         /**
51085          * @event click
51086          * The raw click event for the entire grid.
51087          * @param {Roo.EventObject} e
51088          */
51089         "click" : true,
51090         /**
51091          * @event dblclick
51092          * The raw dblclick event for the entire grid.
51093          * @param {Roo.EventObject} e
51094          */
51095         "dblclick" : true,
51096         /**
51097          * @event contextmenu
51098          * The raw contextmenu event for the entire grid.
51099          * @param {Roo.EventObject} e
51100          */
51101         "contextmenu" : true,
51102         /**
51103          * @event mousedown
51104          * The raw mousedown event for the entire grid.
51105          * @param {Roo.EventObject} e
51106          */
51107         "mousedown" : true,
51108         /**
51109          * @event mouseup
51110          * The raw mouseup event for the entire grid.
51111          * @param {Roo.EventObject} e
51112          */
51113         "mouseup" : true,
51114         /**
51115          * @event mouseover
51116          * The raw mouseover event for the entire grid.
51117          * @param {Roo.EventObject} e
51118          */
51119         "mouseover" : true,
51120         /**
51121          * @event mouseout
51122          * The raw mouseout event for the entire grid.
51123          * @param {Roo.EventObject} e
51124          */
51125         "mouseout" : true,
51126         /**
51127          * @event keypress
51128          * The raw keypress event for the entire grid.
51129          * @param {Roo.EventObject} e
51130          */
51131         "keypress" : true,
51132         /**
51133          * @event keydown
51134          * The raw keydown event for the entire grid.
51135          * @param {Roo.EventObject} e
51136          */
51137         "keydown" : true,
51138
51139         // custom events
51140
51141         /**
51142          * @event cellclick
51143          * Fires when a cell is clicked
51144          * @param {Grid} this
51145          * @param {Number} rowIndex
51146          * @param {Number} columnIndex
51147          * @param {Roo.EventObject} e
51148          */
51149         "cellclick" : true,
51150         /**
51151          * @event celldblclick
51152          * Fires when a cell is double clicked
51153          * @param {Grid} this
51154          * @param {Number} rowIndex
51155          * @param {Number} columnIndex
51156          * @param {Roo.EventObject} e
51157          */
51158         "celldblclick" : true,
51159         /**
51160          * @event rowclick
51161          * Fires when a row is clicked
51162          * @param {Grid} this
51163          * @param {Number} rowIndex
51164          * @param {Roo.EventObject} e
51165          */
51166         "rowclick" : true,
51167         /**
51168          * @event rowdblclick
51169          * Fires when a row is double clicked
51170          * @param {Grid} this
51171          * @param {Number} rowIndex
51172          * @param {Roo.EventObject} e
51173          */
51174         "rowdblclick" : true,
51175         /**
51176          * @event headerclick
51177          * Fires when a header is clicked
51178          * @param {Grid} this
51179          * @param {Number} columnIndex
51180          * @param {Roo.EventObject} e
51181          */
51182         "headerclick" : true,
51183         /**
51184          * @event headerdblclick
51185          * Fires when a header cell is double clicked
51186          * @param {Grid} this
51187          * @param {Number} columnIndex
51188          * @param {Roo.EventObject} e
51189          */
51190         "headerdblclick" : true,
51191         /**
51192          * @event rowcontextmenu
51193          * Fires when a row is right clicked
51194          * @param {Grid} this
51195          * @param {Number} rowIndex
51196          * @param {Roo.EventObject} e
51197          */
51198         "rowcontextmenu" : true,
51199         /**
51200          * @event cellcontextmenu
51201          * Fires when a cell is right clicked
51202          * @param {Grid} this
51203          * @param {Number} rowIndex
51204          * @param {Number} cellIndex
51205          * @param {Roo.EventObject} e
51206          */
51207          "cellcontextmenu" : true,
51208         /**
51209          * @event headercontextmenu
51210          * Fires when a header is right clicked
51211          * @param {Grid} this
51212          * @param {Number} columnIndex
51213          * @param {Roo.EventObject} e
51214          */
51215         "headercontextmenu" : true,
51216         /**
51217          * @event bodyscroll
51218          * Fires when the body element is scrolled
51219          * @param {Number} scrollLeft
51220          * @param {Number} scrollTop
51221          */
51222         "bodyscroll" : true,
51223         /**
51224          * @event columnresize
51225          * Fires when the user resizes a column
51226          * @param {Number} columnIndex
51227          * @param {Number} newSize
51228          */
51229         "columnresize" : true,
51230         /**
51231          * @event columnmove
51232          * Fires when the user moves a column
51233          * @param {Number} oldIndex
51234          * @param {Number} newIndex
51235          */
51236         "columnmove" : true,
51237         /**
51238          * @event startdrag
51239          * Fires when row(s) start being dragged
51240          * @param {Grid} this
51241          * @param {Roo.GridDD} dd The drag drop object
51242          * @param {event} e The raw browser event
51243          */
51244         "startdrag" : true,
51245         /**
51246          * @event enddrag
51247          * Fires when a drag operation is complete
51248          * @param {Grid} this
51249          * @param {Roo.GridDD} dd The drag drop object
51250          * @param {event} e The raw browser event
51251          */
51252         "enddrag" : true,
51253         /**
51254          * @event dragdrop
51255          * Fires when dragged row(s) are dropped on a valid DD target
51256          * @param {Grid} this
51257          * @param {Roo.GridDD} dd The drag drop object
51258          * @param {String} targetId The target drag drop object
51259          * @param {event} e The raw browser event
51260          */
51261         "dragdrop" : true,
51262         /**
51263          * @event dragover
51264          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51265          * @param {Grid} this
51266          * @param {Roo.GridDD} dd The drag drop object
51267          * @param {String} targetId The target drag drop object
51268          * @param {event} e The raw browser event
51269          */
51270         "dragover" : true,
51271         /**
51272          * @event dragenter
51273          *  Fires when the dragged row(s) first cross another DD target while being dragged
51274          * @param {Grid} this
51275          * @param {Roo.GridDD} dd The drag drop object
51276          * @param {String} targetId The target drag drop object
51277          * @param {event} e The raw browser event
51278          */
51279         "dragenter" : true,
51280         /**
51281          * @event dragout
51282          * Fires when the dragged row(s) leave another DD target while being dragged
51283          * @param {Grid} this
51284          * @param {Roo.GridDD} dd The drag drop object
51285          * @param {String} targetId The target drag drop object
51286          * @param {event} e The raw browser event
51287          */
51288         "dragout" : true,
51289         /**
51290          * @event rowclass
51291          * Fires when a row is rendered, so you can change add a style to it.
51292          * @param {GridView} gridview   The grid view
51293          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51294          */
51295         'rowclass' : true,
51296
51297         /**
51298          * @event render
51299          * Fires when the grid is rendered
51300          * @param {Grid} grid
51301          */
51302         'render' : true
51303     });
51304
51305     Roo.grid.Grid.superclass.constructor.call(this);
51306 };
51307 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51308     
51309     /**
51310      * @cfg {String} ddGroup - drag drop group.
51311      */
51312
51313     /**
51314      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51315      */
51316     minColumnWidth : 25,
51317
51318     /**
51319      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51320      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51321      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51322      */
51323     autoSizeColumns : false,
51324
51325     /**
51326      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51327      */
51328     autoSizeHeaders : true,
51329
51330     /**
51331      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51332      */
51333     monitorWindowResize : true,
51334
51335     /**
51336      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51337      * rows measured to get a columns size. Default is 0 (all rows).
51338      */
51339     maxRowsToMeasure : 0,
51340
51341     /**
51342      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51343      */
51344     trackMouseOver : true,
51345
51346     /**
51347     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51348     */
51349     
51350     /**
51351     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51352     */
51353     enableDragDrop : false,
51354     
51355     /**
51356     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51357     */
51358     enableColumnMove : true,
51359     
51360     /**
51361     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51362     */
51363     enableColumnHide : true,
51364     
51365     /**
51366     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51367     */
51368     enableRowHeightSync : false,
51369     
51370     /**
51371     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51372     */
51373     stripeRows : true,
51374     
51375     /**
51376     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51377     */
51378     autoHeight : false,
51379
51380     /**
51381      * @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.
51382      */
51383     autoExpandColumn : false,
51384
51385     /**
51386     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51387     * Default is 50.
51388     */
51389     autoExpandMin : 50,
51390
51391     /**
51392     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51393     */
51394     autoExpandMax : 1000,
51395
51396     /**
51397     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51398     */
51399     view : null,
51400
51401     /**
51402     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51403     */
51404     loadMask : false,
51405     /**
51406     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51407     */
51408     dropTarget: false,
51409     
51410    
51411     
51412     // private
51413     rendered : false,
51414
51415     /**
51416     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51417     * of a fixed width. Default is false.
51418     */
51419     /**
51420     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51421     */
51422     /**
51423      * Called once after all setup has been completed and the grid is ready to be rendered.
51424      * @return {Roo.grid.Grid} this
51425      */
51426     render : function()
51427     {
51428         var c = this.container;
51429         // try to detect autoHeight/width mode
51430         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51431             this.autoHeight = true;
51432         }
51433         var view = this.getView();
51434         view.init(this);
51435
51436         c.on("click", this.onClick, this);
51437         c.on("dblclick", this.onDblClick, this);
51438         c.on("contextmenu", this.onContextMenu, this);
51439         c.on("keydown", this.onKeyDown, this);
51440         if (Roo.isTouch) {
51441             c.on("touchstart", this.onTouchStart, this);
51442         }
51443
51444         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51445
51446         this.getSelectionModel().init(this);
51447
51448         view.render();
51449
51450         if(this.loadMask){
51451             this.loadMask = new Roo.LoadMask(this.container,
51452                     Roo.apply({store:this.dataSource}, this.loadMask));
51453         }
51454         
51455         
51456         if (this.toolbar && this.toolbar.xtype) {
51457             this.toolbar.container = this.getView().getHeaderPanel(true);
51458             this.toolbar = new Roo.Toolbar(this.toolbar);
51459         }
51460         if (this.footer && this.footer.xtype) {
51461             this.footer.dataSource = this.getDataSource();
51462             this.footer.container = this.getView().getFooterPanel(true);
51463             this.footer = Roo.factory(this.footer, Roo);
51464         }
51465         if (this.dropTarget && this.dropTarget.xtype) {
51466             delete this.dropTarget.xtype;
51467             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51468         }
51469         
51470         
51471         this.rendered = true;
51472         this.fireEvent('render', this);
51473         return this;
51474     },
51475
51476         /**
51477          * Reconfigures the grid to use a different Store and Column Model.
51478          * The View will be bound to the new objects and refreshed.
51479          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51480          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51481          */
51482     reconfigure : function(dataSource, colModel){
51483         if(this.loadMask){
51484             this.loadMask.destroy();
51485             this.loadMask = new Roo.LoadMask(this.container,
51486                     Roo.apply({store:dataSource}, this.loadMask));
51487         }
51488         this.view.bind(dataSource, colModel);
51489         this.dataSource = dataSource;
51490         this.colModel = colModel;
51491         this.view.refresh(true);
51492     },
51493
51494     // private
51495     onKeyDown : function(e){
51496         this.fireEvent("keydown", e);
51497     },
51498
51499     /**
51500      * Destroy this grid.
51501      * @param {Boolean} removeEl True to remove the element
51502      */
51503     destroy : function(removeEl, keepListeners){
51504         if(this.loadMask){
51505             this.loadMask.destroy();
51506         }
51507         var c = this.container;
51508         c.removeAllListeners();
51509         this.view.destroy();
51510         this.colModel.purgeListeners();
51511         if(!keepListeners){
51512             this.purgeListeners();
51513         }
51514         c.update("");
51515         if(removeEl === true){
51516             c.remove();
51517         }
51518     },
51519
51520     // private
51521     processEvent : function(name, e){
51522         // does this fire select???
51523         Roo.log('grid:processEvent '  + name);
51524         
51525         if (name != 'touchstart' ) {
51526             this.fireEvent(name, e);    
51527         }
51528         
51529         var t = e.getTarget();
51530         var v = this.view;
51531         var header = v.findHeaderIndex(t);
51532         if(header !== false){
51533             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
51534         }else{
51535             var row = v.findRowIndex(t);
51536             var cell = v.findCellIndex(t);
51537             if (name == 'touchstart') {
51538                 // first touch is always a click.
51539                 // hopefull this happens after selection is updated.?
51540                 name = false;
51541                 
51542                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51543                     var cs = this.selModel.getSelectedCell();
51544                     if (row == cs[0] && cell == cs[1]){
51545                         name = 'dblclick';
51546                     }
51547                 }
51548                 if (typeof(this.selModel.getSelections) != 'undefined') {
51549                     var cs = this.selModel.getSelections();
51550                     var ds = this.dataSource;
51551                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51552                         name = 'dblclick';
51553                     }
51554                 }
51555                 if (!name) {
51556                     return;
51557                 }
51558             }
51559             
51560             
51561             if(row !== false){
51562                 this.fireEvent("row" + name, this, row, e);
51563                 if(cell !== false){
51564                     this.fireEvent("cell" + name, this, row, cell, e);
51565                 }
51566             }
51567         }
51568     },
51569
51570     // private
51571     onClick : function(e){
51572         this.processEvent("click", e);
51573     },
51574    // private
51575     onTouchStart : function(e){
51576         this.processEvent("touchstart", e);
51577     },
51578
51579     // private
51580     onContextMenu : function(e, t){
51581         this.processEvent("contextmenu", e);
51582     },
51583
51584     // private
51585     onDblClick : function(e){
51586         this.processEvent("dblclick", e);
51587     },
51588
51589     // private
51590     walkCells : function(row, col, step, fn, scope){
51591         var cm = this.colModel, clen = cm.getColumnCount();
51592         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51593         if(step < 0){
51594             if(col < 0){
51595                 row--;
51596                 first = false;
51597             }
51598             while(row >= 0){
51599                 if(!first){
51600                     col = clen-1;
51601                 }
51602                 first = false;
51603                 while(col >= 0){
51604                     if(fn.call(scope || this, row, col, cm) === true){
51605                         return [row, col];
51606                     }
51607                     col--;
51608                 }
51609                 row--;
51610             }
51611         } else {
51612             if(col >= clen){
51613                 row++;
51614                 first = false;
51615             }
51616             while(row < rlen){
51617                 if(!first){
51618                     col = 0;
51619                 }
51620                 first = false;
51621                 while(col < clen){
51622                     if(fn.call(scope || this, row, col, cm) === true){
51623                         return [row, col];
51624                     }
51625                     col++;
51626                 }
51627                 row++;
51628             }
51629         }
51630         return null;
51631     },
51632
51633     // private
51634     getSelections : function(){
51635         return this.selModel.getSelections();
51636     },
51637
51638     /**
51639      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
51640      * but if manual update is required this method will initiate it.
51641      */
51642     autoSize : function(){
51643         if(this.rendered){
51644             this.view.layout();
51645             if(this.view.adjustForScroll){
51646                 this.view.adjustForScroll();
51647             }
51648         }
51649     },
51650
51651     /**
51652      * Returns the grid's underlying element.
51653      * @return {Element} The element
51654      */
51655     getGridEl : function(){
51656         return this.container;
51657     },
51658
51659     // private for compatibility, overridden by editor grid
51660     stopEditing : function(){},
51661
51662     /**
51663      * Returns the grid's SelectionModel.
51664      * @return {SelectionModel}
51665      */
51666     getSelectionModel : function(){
51667         if(!this.selModel){
51668             this.selModel = new Roo.grid.RowSelectionModel();
51669         }
51670         return this.selModel;
51671     },
51672
51673     /**
51674      * Returns the grid's DataSource.
51675      * @return {DataSource}
51676      */
51677     getDataSource : function(){
51678         return this.dataSource;
51679     },
51680
51681     /**
51682      * Returns the grid's ColumnModel.
51683      * @return {ColumnModel}
51684      */
51685     getColumnModel : function(){
51686         return this.colModel;
51687     },
51688
51689     /**
51690      * Returns the grid's GridView object.
51691      * @return {GridView}
51692      */
51693     getView : function(){
51694         if(!this.view){
51695             this.view = new Roo.grid.GridView(this.viewConfig);
51696         }
51697         return this.view;
51698     },
51699     /**
51700      * Called to get grid's drag proxy text, by default returns this.ddText.
51701      * @return {String}
51702      */
51703     getDragDropText : function(){
51704         var count = this.selModel.getCount();
51705         return String.format(this.ddText, count, count == 1 ? '' : 's');
51706     }
51707 });
51708 /**
51709  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
51710  * %0 is replaced with the number of selected rows.
51711  * @type String
51712  */
51713 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51714  * Based on:
51715  * Ext JS Library 1.1.1
51716  * Copyright(c) 2006-2007, Ext JS, LLC.
51717  *
51718  * Originally Released Under LGPL - original licence link has changed is not relivant.
51719  *
51720  * Fork - LGPL
51721  * <script type="text/javascript">
51722  */
51723  
51724 Roo.grid.AbstractGridView = function(){
51725         this.grid = null;
51726         
51727         this.events = {
51728             "beforerowremoved" : true,
51729             "beforerowsinserted" : true,
51730             "beforerefresh" : true,
51731             "rowremoved" : true,
51732             "rowsinserted" : true,
51733             "rowupdated" : true,
51734             "refresh" : true
51735         };
51736     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51737 };
51738
51739 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51740     rowClass : "x-grid-row",
51741     cellClass : "x-grid-cell",
51742     tdClass : "x-grid-td",
51743     hdClass : "x-grid-hd",
51744     splitClass : "x-grid-hd-split",
51745     
51746         init: function(grid){
51747         this.grid = grid;
51748                 var cid = this.grid.getGridEl().id;
51749         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51750         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51751         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51752         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51753         },
51754         
51755         getColumnRenderers : function(){
51756         var renderers = [];
51757         var cm = this.grid.colModel;
51758         var colCount = cm.getColumnCount();
51759         for(var i = 0; i < colCount; i++){
51760             renderers[i] = cm.getRenderer(i);
51761         }
51762         return renderers;
51763     },
51764     
51765     getColumnIds : function(){
51766         var ids = [];
51767         var cm = this.grid.colModel;
51768         var colCount = cm.getColumnCount();
51769         for(var i = 0; i < colCount; i++){
51770             ids[i] = cm.getColumnId(i);
51771         }
51772         return ids;
51773     },
51774     
51775     getDataIndexes : function(){
51776         if(!this.indexMap){
51777             this.indexMap = this.buildIndexMap();
51778         }
51779         return this.indexMap.colToData;
51780     },
51781     
51782     getColumnIndexByDataIndex : function(dataIndex){
51783         if(!this.indexMap){
51784             this.indexMap = this.buildIndexMap();
51785         }
51786         return this.indexMap.dataToCol[dataIndex];
51787     },
51788     
51789     /**
51790      * Set a css style for a column dynamically. 
51791      * @param {Number} colIndex The index of the column
51792      * @param {String} name The css property name
51793      * @param {String} value The css value
51794      */
51795     setCSSStyle : function(colIndex, name, value){
51796         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51797         Roo.util.CSS.updateRule(selector, name, value);
51798     },
51799     
51800     generateRules : function(cm){
51801         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51802         Roo.util.CSS.removeStyleSheet(rulesId);
51803         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51804             var cid = cm.getColumnId(i);
51805             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51806                          this.tdSelector, cid, " {\n}\n",
51807                          this.hdSelector, cid, " {\n}\n",
51808                          this.splitSelector, cid, " {\n}\n");
51809         }
51810         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51811     }
51812 });/*
51813  * Based on:
51814  * Ext JS Library 1.1.1
51815  * Copyright(c) 2006-2007, Ext JS, LLC.
51816  *
51817  * Originally Released Under LGPL - original licence link has changed is not relivant.
51818  *
51819  * Fork - LGPL
51820  * <script type="text/javascript">
51821  */
51822
51823 // private
51824 // This is a support class used internally by the Grid components
51825 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51826     this.grid = grid;
51827     this.view = grid.getView();
51828     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51829     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51830     if(hd2){
51831         this.setHandleElId(Roo.id(hd));
51832         this.setOuterHandleElId(Roo.id(hd2));
51833     }
51834     this.scroll = false;
51835 };
51836 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51837     maxDragWidth: 120,
51838     getDragData : function(e){
51839         var t = Roo.lib.Event.getTarget(e);
51840         var h = this.view.findHeaderCell(t);
51841         if(h){
51842             return {ddel: h.firstChild, header:h};
51843         }
51844         return false;
51845     },
51846
51847     onInitDrag : function(e){
51848         this.view.headersDisabled = true;
51849         var clone = this.dragData.ddel.cloneNode(true);
51850         clone.id = Roo.id();
51851         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51852         this.proxy.update(clone);
51853         return true;
51854     },
51855
51856     afterValidDrop : function(){
51857         var v = this.view;
51858         setTimeout(function(){
51859             v.headersDisabled = false;
51860         }, 50);
51861     },
51862
51863     afterInvalidDrop : function(){
51864         var v = this.view;
51865         setTimeout(function(){
51866             v.headersDisabled = false;
51867         }, 50);
51868     }
51869 });
51870 /*
51871  * Based on:
51872  * Ext JS Library 1.1.1
51873  * Copyright(c) 2006-2007, Ext JS, LLC.
51874  *
51875  * Originally Released Under LGPL - original licence link has changed is not relivant.
51876  *
51877  * Fork - LGPL
51878  * <script type="text/javascript">
51879  */
51880 // private
51881 // This is a support class used internally by the Grid components
51882 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51883     this.grid = grid;
51884     this.view = grid.getView();
51885     // split the proxies so they don't interfere with mouse events
51886     this.proxyTop = Roo.DomHelper.append(document.body, {
51887         cls:"col-move-top", html:"&#160;"
51888     }, true);
51889     this.proxyBottom = Roo.DomHelper.append(document.body, {
51890         cls:"col-move-bottom", html:"&#160;"
51891     }, true);
51892     this.proxyTop.hide = this.proxyBottom.hide = function(){
51893         this.setLeftTop(-100,-100);
51894         this.setStyle("visibility", "hidden");
51895     };
51896     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51897     // temporarily disabled
51898     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51899     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51900 };
51901 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51902     proxyOffsets : [-4, -9],
51903     fly: Roo.Element.fly,
51904
51905     getTargetFromEvent : function(e){
51906         var t = Roo.lib.Event.getTarget(e);
51907         var cindex = this.view.findCellIndex(t);
51908         if(cindex !== false){
51909             return this.view.getHeaderCell(cindex);
51910         }
51911         return null;
51912     },
51913
51914     nextVisible : function(h){
51915         var v = this.view, cm = this.grid.colModel;
51916         h = h.nextSibling;
51917         while(h){
51918             if(!cm.isHidden(v.getCellIndex(h))){
51919                 return h;
51920             }
51921             h = h.nextSibling;
51922         }
51923         return null;
51924     },
51925
51926     prevVisible : function(h){
51927         var v = this.view, cm = this.grid.colModel;
51928         h = h.prevSibling;
51929         while(h){
51930             if(!cm.isHidden(v.getCellIndex(h))){
51931                 return h;
51932             }
51933             h = h.prevSibling;
51934         }
51935         return null;
51936     },
51937
51938     positionIndicator : function(h, n, e){
51939         var x = Roo.lib.Event.getPageX(e);
51940         var r = Roo.lib.Dom.getRegion(n.firstChild);
51941         var px, pt, py = r.top + this.proxyOffsets[1];
51942         if((r.right - x) <= (r.right-r.left)/2){
51943             px = r.right+this.view.borderWidth;
51944             pt = "after";
51945         }else{
51946             px = r.left;
51947             pt = "before";
51948         }
51949         var oldIndex = this.view.getCellIndex(h);
51950         var newIndex = this.view.getCellIndex(n);
51951
51952         if(this.grid.colModel.isFixed(newIndex)){
51953             return false;
51954         }
51955
51956         var locked = this.grid.colModel.isLocked(newIndex);
51957
51958         if(pt == "after"){
51959             newIndex++;
51960         }
51961         if(oldIndex < newIndex){
51962             newIndex--;
51963         }
51964         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51965             return false;
51966         }
51967         px +=  this.proxyOffsets[0];
51968         this.proxyTop.setLeftTop(px, py);
51969         this.proxyTop.show();
51970         if(!this.bottomOffset){
51971             this.bottomOffset = this.view.mainHd.getHeight();
51972         }
51973         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51974         this.proxyBottom.show();
51975         return pt;
51976     },
51977
51978     onNodeEnter : function(n, dd, e, data){
51979         if(data.header != n){
51980             this.positionIndicator(data.header, n, e);
51981         }
51982     },
51983
51984     onNodeOver : function(n, dd, e, data){
51985         var result = false;
51986         if(data.header != n){
51987             result = this.positionIndicator(data.header, n, e);
51988         }
51989         if(!result){
51990             this.proxyTop.hide();
51991             this.proxyBottom.hide();
51992         }
51993         return result ? this.dropAllowed : this.dropNotAllowed;
51994     },
51995
51996     onNodeOut : function(n, dd, e, data){
51997         this.proxyTop.hide();
51998         this.proxyBottom.hide();
51999     },
52000
52001     onNodeDrop : function(n, dd, e, data){
52002         var h = data.header;
52003         if(h != n){
52004             var cm = this.grid.colModel;
52005             var x = Roo.lib.Event.getPageX(e);
52006             var r = Roo.lib.Dom.getRegion(n.firstChild);
52007             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52008             var oldIndex = this.view.getCellIndex(h);
52009             var newIndex = this.view.getCellIndex(n);
52010             var locked = cm.isLocked(newIndex);
52011             if(pt == "after"){
52012                 newIndex++;
52013             }
52014             if(oldIndex < newIndex){
52015                 newIndex--;
52016             }
52017             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52018                 return false;
52019             }
52020             cm.setLocked(oldIndex, locked, true);
52021             cm.moveColumn(oldIndex, newIndex);
52022             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52023             return true;
52024         }
52025         return false;
52026     }
52027 });
52028 /*
52029  * Based on:
52030  * Ext JS Library 1.1.1
52031  * Copyright(c) 2006-2007, Ext JS, LLC.
52032  *
52033  * Originally Released Under LGPL - original licence link has changed is not relivant.
52034  *
52035  * Fork - LGPL
52036  * <script type="text/javascript">
52037  */
52038   
52039 /**
52040  * @class Roo.grid.GridView
52041  * @extends Roo.util.Observable
52042  *
52043  * @constructor
52044  * @param {Object} config
52045  */
52046 Roo.grid.GridView = function(config){
52047     Roo.grid.GridView.superclass.constructor.call(this);
52048     this.el = null;
52049
52050     Roo.apply(this, config);
52051 };
52052
52053 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52054
52055     unselectable :  'unselectable="on"',
52056     unselectableCls :  'x-unselectable',
52057     
52058     
52059     rowClass : "x-grid-row",
52060
52061     cellClass : "x-grid-col",
52062
52063     tdClass : "x-grid-td",
52064
52065     hdClass : "x-grid-hd",
52066
52067     splitClass : "x-grid-split",
52068
52069     sortClasses : ["sort-asc", "sort-desc"],
52070
52071     enableMoveAnim : false,
52072
52073     hlColor: "C3DAF9",
52074
52075     dh : Roo.DomHelper,
52076
52077     fly : Roo.Element.fly,
52078
52079     css : Roo.util.CSS,
52080
52081     borderWidth: 1,
52082
52083     splitOffset: 3,
52084
52085     scrollIncrement : 22,
52086
52087     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52088
52089     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52090
52091     bind : function(ds, cm){
52092         if(this.ds){
52093             this.ds.un("load", this.onLoad, this);
52094             this.ds.un("datachanged", this.onDataChange, this);
52095             this.ds.un("add", this.onAdd, this);
52096             this.ds.un("remove", this.onRemove, this);
52097             this.ds.un("update", this.onUpdate, this);
52098             this.ds.un("clear", this.onClear, this);
52099         }
52100         if(ds){
52101             ds.on("load", this.onLoad, this);
52102             ds.on("datachanged", this.onDataChange, this);
52103             ds.on("add", this.onAdd, this);
52104             ds.on("remove", this.onRemove, this);
52105             ds.on("update", this.onUpdate, this);
52106             ds.on("clear", this.onClear, this);
52107         }
52108         this.ds = ds;
52109
52110         if(this.cm){
52111             this.cm.un("widthchange", this.onColWidthChange, this);
52112             this.cm.un("headerchange", this.onHeaderChange, this);
52113             this.cm.un("hiddenchange", this.onHiddenChange, this);
52114             this.cm.un("columnmoved", this.onColumnMove, this);
52115             this.cm.un("columnlockchange", this.onColumnLock, this);
52116         }
52117         if(cm){
52118             this.generateRules(cm);
52119             cm.on("widthchange", this.onColWidthChange, this);
52120             cm.on("headerchange", this.onHeaderChange, this);
52121             cm.on("hiddenchange", this.onHiddenChange, this);
52122             cm.on("columnmoved", this.onColumnMove, this);
52123             cm.on("columnlockchange", this.onColumnLock, this);
52124         }
52125         this.cm = cm;
52126     },
52127
52128     init: function(grid){
52129         Roo.grid.GridView.superclass.init.call(this, grid);
52130
52131         this.bind(grid.dataSource, grid.colModel);
52132
52133         grid.on("headerclick", this.handleHeaderClick, this);
52134
52135         if(grid.trackMouseOver){
52136             grid.on("mouseover", this.onRowOver, this);
52137             grid.on("mouseout", this.onRowOut, this);
52138         }
52139         grid.cancelTextSelection = function(){};
52140         this.gridId = grid.id;
52141
52142         var tpls = this.templates || {};
52143
52144         if(!tpls.master){
52145             tpls.master = new Roo.Template(
52146                '<div class="x-grid" hidefocus="true">',
52147                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52148                   '<div class="x-grid-topbar"></div>',
52149                   '<div class="x-grid-scroller"><div></div></div>',
52150                   '<div class="x-grid-locked">',
52151                       '<div class="x-grid-header">{lockedHeader}</div>',
52152                       '<div class="x-grid-body">{lockedBody}</div>',
52153                   "</div>",
52154                   '<div class="x-grid-viewport">',
52155                       '<div class="x-grid-header">{header}</div>',
52156                       '<div class="x-grid-body">{body}</div>',
52157                   "</div>",
52158                   '<div class="x-grid-bottombar"></div>',
52159                  
52160                   '<div class="x-grid-resize-proxy">&#160;</div>',
52161                "</div>"
52162             );
52163             tpls.master.disableformats = true;
52164         }
52165
52166         if(!tpls.header){
52167             tpls.header = new Roo.Template(
52168                '<table border="0" cellspacing="0" cellpadding="0">',
52169                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52170                "</table>{splits}"
52171             );
52172             tpls.header.disableformats = true;
52173         }
52174         tpls.header.compile();
52175
52176         if(!tpls.hcell){
52177             tpls.hcell = new Roo.Template(
52178                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52179                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52180                 "</div></td>"
52181              );
52182              tpls.hcell.disableFormats = true;
52183         }
52184         tpls.hcell.compile();
52185
52186         if(!tpls.hsplit){
52187             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52188                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52189             tpls.hsplit.disableFormats = true;
52190         }
52191         tpls.hsplit.compile();
52192
52193         if(!tpls.body){
52194             tpls.body = new Roo.Template(
52195                '<table border="0" cellspacing="0" cellpadding="0">',
52196                "<tbody>{rows}</tbody>",
52197                "</table>"
52198             );
52199             tpls.body.disableFormats = true;
52200         }
52201         tpls.body.compile();
52202
52203         if(!tpls.row){
52204             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52205             tpls.row.disableFormats = true;
52206         }
52207         tpls.row.compile();
52208
52209         if(!tpls.cell){
52210             tpls.cell = new Roo.Template(
52211                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52212                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52213                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52214                 "</td>"
52215             );
52216             tpls.cell.disableFormats = true;
52217         }
52218         tpls.cell.compile();
52219
52220         this.templates = tpls;
52221     },
52222
52223     // remap these for backwards compat
52224     onColWidthChange : function(){
52225         this.updateColumns.apply(this, arguments);
52226     },
52227     onHeaderChange : function(){
52228         this.updateHeaders.apply(this, arguments);
52229     }, 
52230     onHiddenChange : function(){
52231         this.handleHiddenChange.apply(this, arguments);
52232     },
52233     onColumnMove : function(){
52234         this.handleColumnMove.apply(this, arguments);
52235     },
52236     onColumnLock : function(){
52237         this.handleLockChange.apply(this, arguments);
52238     },
52239
52240     onDataChange : function(){
52241         this.refresh();
52242         this.updateHeaderSortState();
52243     },
52244
52245     onClear : function(){
52246         this.refresh();
52247     },
52248
52249     onUpdate : function(ds, record){
52250         this.refreshRow(record);
52251     },
52252
52253     refreshRow : function(record){
52254         var ds = this.ds, index;
52255         if(typeof record == 'number'){
52256             index = record;
52257             record = ds.getAt(index);
52258         }else{
52259             index = ds.indexOf(record);
52260         }
52261         this.insertRows(ds, index, index, true);
52262         this.onRemove(ds, record, index+1, true);
52263         this.syncRowHeights(index, index);
52264         this.layout();
52265         this.fireEvent("rowupdated", this, index, record);
52266     },
52267
52268     onAdd : function(ds, records, index){
52269         this.insertRows(ds, index, index + (records.length-1));
52270     },
52271
52272     onRemove : function(ds, record, index, isUpdate){
52273         if(isUpdate !== true){
52274             this.fireEvent("beforerowremoved", this, index, record);
52275         }
52276         var bt = this.getBodyTable(), lt = this.getLockedTable();
52277         if(bt.rows[index]){
52278             bt.firstChild.removeChild(bt.rows[index]);
52279         }
52280         if(lt.rows[index]){
52281             lt.firstChild.removeChild(lt.rows[index]);
52282         }
52283         if(isUpdate !== true){
52284             this.stripeRows(index);
52285             this.syncRowHeights(index, index);
52286             this.layout();
52287             this.fireEvent("rowremoved", this, index, record);
52288         }
52289     },
52290
52291     onLoad : function(){
52292         this.scrollToTop();
52293     },
52294
52295     /**
52296      * Scrolls the grid to the top
52297      */
52298     scrollToTop : function(){
52299         if(this.scroller){
52300             this.scroller.dom.scrollTop = 0;
52301             this.syncScroll();
52302         }
52303     },
52304
52305     /**
52306      * Gets a panel in the header of the grid that can be used for toolbars etc.
52307      * After modifying the contents of this panel a call to grid.autoSize() may be
52308      * required to register any changes in size.
52309      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52310      * @return Roo.Element
52311      */
52312     getHeaderPanel : function(doShow){
52313         if(doShow){
52314             this.headerPanel.show();
52315         }
52316         return this.headerPanel;
52317     },
52318
52319     /**
52320      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52321      * After modifying the contents of this panel a call to grid.autoSize() may be
52322      * required to register any changes in size.
52323      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52324      * @return Roo.Element
52325      */
52326     getFooterPanel : function(doShow){
52327         if(doShow){
52328             this.footerPanel.show();
52329         }
52330         return this.footerPanel;
52331     },
52332
52333     initElements : function(){
52334         var E = Roo.Element;
52335         var el = this.grid.getGridEl().dom.firstChild;
52336         var cs = el.childNodes;
52337
52338         this.el = new E(el);
52339         
52340          this.focusEl = new E(el.firstChild);
52341         this.focusEl.swallowEvent("click", true);
52342         
52343         this.headerPanel = new E(cs[1]);
52344         this.headerPanel.enableDisplayMode("block");
52345
52346         this.scroller = new E(cs[2]);
52347         this.scrollSizer = new E(this.scroller.dom.firstChild);
52348
52349         this.lockedWrap = new E(cs[3]);
52350         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52351         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52352
52353         this.mainWrap = new E(cs[4]);
52354         this.mainHd = new E(this.mainWrap.dom.firstChild);
52355         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52356
52357         this.footerPanel = new E(cs[5]);
52358         this.footerPanel.enableDisplayMode("block");
52359
52360         this.resizeProxy = new E(cs[6]);
52361
52362         this.headerSelector = String.format(
52363            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52364            this.lockedHd.id, this.mainHd.id
52365         );
52366
52367         this.splitterSelector = String.format(
52368            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52369            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52370         );
52371     },
52372     idToCssName : function(s)
52373     {
52374         return s.replace(/[^a-z0-9]+/ig, '-');
52375     },
52376
52377     getHeaderCell : function(index){
52378         return Roo.DomQuery.select(this.headerSelector)[index];
52379     },
52380
52381     getHeaderCellMeasure : function(index){
52382         return this.getHeaderCell(index).firstChild;
52383     },
52384
52385     getHeaderCellText : function(index){
52386         return this.getHeaderCell(index).firstChild.firstChild;
52387     },
52388
52389     getLockedTable : function(){
52390         return this.lockedBody.dom.firstChild;
52391     },
52392
52393     getBodyTable : function(){
52394         return this.mainBody.dom.firstChild;
52395     },
52396
52397     getLockedRow : function(index){
52398         return this.getLockedTable().rows[index];
52399     },
52400
52401     getRow : function(index){
52402         return this.getBodyTable().rows[index];
52403     },
52404
52405     getRowComposite : function(index){
52406         if(!this.rowEl){
52407             this.rowEl = new Roo.CompositeElementLite();
52408         }
52409         var els = [], lrow, mrow;
52410         if(lrow = this.getLockedRow(index)){
52411             els.push(lrow);
52412         }
52413         if(mrow = this.getRow(index)){
52414             els.push(mrow);
52415         }
52416         this.rowEl.elements = els;
52417         return this.rowEl;
52418     },
52419     /**
52420      * Gets the 'td' of the cell
52421      * 
52422      * @param {Integer} rowIndex row to select
52423      * @param {Integer} colIndex column to select
52424      * 
52425      * @return {Object} 
52426      */
52427     getCell : function(rowIndex, colIndex){
52428         var locked = this.cm.getLockedCount();
52429         var source;
52430         if(colIndex < locked){
52431             source = this.lockedBody.dom.firstChild;
52432         }else{
52433             source = this.mainBody.dom.firstChild;
52434             colIndex -= locked;
52435         }
52436         return source.rows[rowIndex].childNodes[colIndex];
52437     },
52438
52439     getCellText : function(rowIndex, colIndex){
52440         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52441     },
52442
52443     getCellBox : function(cell){
52444         var b = this.fly(cell).getBox();
52445         if(Roo.isOpera){ // opera fails to report the Y
52446             b.y = cell.offsetTop + this.mainBody.getY();
52447         }
52448         return b;
52449     },
52450
52451     getCellIndex : function(cell){
52452         var id = String(cell.className).match(this.cellRE);
52453         if(id){
52454             return parseInt(id[1], 10);
52455         }
52456         return 0;
52457     },
52458
52459     findHeaderIndex : function(n){
52460         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52461         return r ? this.getCellIndex(r) : false;
52462     },
52463
52464     findHeaderCell : function(n){
52465         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52466         return r ? r : false;
52467     },
52468
52469     findRowIndex : function(n){
52470         if(!n){
52471             return false;
52472         }
52473         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52474         return r ? r.rowIndex : false;
52475     },
52476
52477     findCellIndex : function(node){
52478         var stop = this.el.dom;
52479         while(node && node != stop){
52480             if(this.findRE.test(node.className)){
52481                 return this.getCellIndex(node);
52482             }
52483             node = node.parentNode;
52484         }
52485         return false;
52486     },
52487
52488     getColumnId : function(index){
52489         return this.cm.getColumnId(index);
52490     },
52491
52492     getSplitters : function()
52493     {
52494         if(this.splitterSelector){
52495            return Roo.DomQuery.select(this.splitterSelector);
52496         }else{
52497             return null;
52498       }
52499     },
52500
52501     getSplitter : function(index){
52502         return this.getSplitters()[index];
52503     },
52504
52505     onRowOver : function(e, t){
52506         var row;
52507         if((row = this.findRowIndex(t)) !== false){
52508             this.getRowComposite(row).addClass("x-grid-row-over");
52509         }
52510     },
52511
52512     onRowOut : function(e, t){
52513         var row;
52514         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52515             this.getRowComposite(row).removeClass("x-grid-row-over");
52516         }
52517     },
52518
52519     renderHeaders : function(){
52520         var cm = this.cm;
52521         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52522         var cb = [], lb = [], sb = [], lsb = [], p = {};
52523         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52524             p.cellId = "x-grid-hd-0-" + i;
52525             p.splitId = "x-grid-csplit-0-" + i;
52526             p.id = cm.getColumnId(i);
52527             p.title = cm.getColumnTooltip(i) || "";
52528             p.value = cm.getColumnHeader(i) || "";
52529             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52530             if(!cm.isLocked(i)){
52531                 cb[cb.length] = ct.apply(p);
52532                 sb[sb.length] = st.apply(p);
52533             }else{
52534                 lb[lb.length] = ct.apply(p);
52535                 lsb[lsb.length] = st.apply(p);
52536             }
52537         }
52538         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52539                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52540     },
52541
52542     updateHeaders : function(){
52543         var html = this.renderHeaders();
52544         this.lockedHd.update(html[0]);
52545         this.mainHd.update(html[1]);
52546     },
52547
52548     /**
52549      * Focuses the specified row.
52550      * @param {Number} row The row index
52551      */
52552     focusRow : function(row)
52553     {
52554         //Roo.log('GridView.focusRow');
52555         var x = this.scroller.dom.scrollLeft;
52556         this.focusCell(row, 0, false);
52557         this.scroller.dom.scrollLeft = x;
52558     },
52559
52560     /**
52561      * Focuses the specified cell.
52562      * @param {Number} row The row index
52563      * @param {Number} col The column index
52564      * @param {Boolean} hscroll false to disable horizontal scrolling
52565      */
52566     focusCell : function(row, col, hscroll)
52567     {
52568         //Roo.log('GridView.focusCell');
52569         var el = this.ensureVisible(row, col, hscroll);
52570         this.focusEl.alignTo(el, "tl-tl");
52571         if(Roo.isGecko){
52572             this.focusEl.focus();
52573         }else{
52574             this.focusEl.focus.defer(1, this.focusEl);
52575         }
52576     },
52577
52578     /**
52579      * Scrolls the specified cell into view
52580      * @param {Number} row The row index
52581      * @param {Number} col The column index
52582      * @param {Boolean} hscroll false to disable horizontal scrolling
52583      */
52584     ensureVisible : function(row, col, hscroll)
52585     {
52586         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52587         //return null; //disable for testing.
52588         if(typeof row != "number"){
52589             row = row.rowIndex;
52590         }
52591         if(row < 0 && row >= this.ds.getCount()){
52592             return  null;
52593         }
52594         col = (col !== undefined ? col : 0);
52595         var cm = this.grid.colModel;
52596         while(cm.isHidden(col)){
52597             col++;
52598         }
52599
52600         var el = this.getCell(row, col);
52601         if(!el){
52602             return null;
52603         }
52604         var c = this.scroller.dom;
52605
52606         var ctop = parseInt(el.offsetTop, 10);
52607         var cleft = parseInt(el.offsetLeft, 10);
52608         var cbot = ctop + el.offsetHeight;
52609         var cright = cleft + el.offsetWidth;
52610         
52611         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52612         var stop = parseInt(c.scrollTop, 10);
52613         var sleft = parseInt(c.scrollLeft, 10);
52614         var sbot = stop + ch;
52615         var sright = sleft + c.clientWidth;
52616         /*
52617         Roo.log('GridView.ensureVisible:' +
52618                 ' ctop:' + ctop +
52619                 ' c.clientHeight:' + c.clientHeight +
52620                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52621                 ' stop:' + stop +
52622                 ' cbot:' + cbot +
52623                 ' sbot:' + sbot +
52624                 ' ch:' + ch  
52625                 );
52626         */
52627         if(ctop < stop){
52628              c.scrollTop = ctop;
52629             //Roo.log("set scrolltop to ctop DISABLE?");
52630         }else if(cbot > sbot){
52631             //Roo.log("set scrolltop to cbot-ch");
52632             c.scrollTop = cbot-ch;
52633         }
52634         
52635         if(hscroll !== false){
52636             if(cleft < sleft){
52637                 c.scrollLeft = cleft;
52638             }else if(cright > sright){
52639                 c.scrollLeft = cright-c.clientWidth;
52640             }
52641         }
52642          
52643         return el;
52644     },
52645
52646     updateColumns : function(){
52647         this.grid.stopEditing();
52648         var cm = this.grid.colModel, colIds = this.getColumnIds();
52649         //var totalWidth = cm.getTotalWidth();
52650         var pos = 0;
52651         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52652             //if(cm.isHidden(i)) continue;
52653             var w = cm.getColumnWidth(i);
52654             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52655             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52656         }
52657         this.updateSplitters();
52658     },
52659
52660     generateRules : function(cm){
52661         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
52662         Roo.util.CSS.removeStyleSheet(rulesId);
52663         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52664             var cid = cm.getColumnId(i);
52665             var align = '';
52666             if(cm.config[i].align){
52667                 align = 'text-align:'+cm.config[i].align+';';
52668             }
52669             var hidden = '';
52670             if(cm.isHidden(i)){
52671                 hidden = 'display:none;';
52672             }
52673             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
52674             ruleBuf.push(
52675                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
52676                     this.hdSelector, cid, " {\n", align, width, "}\n",
52677                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
52678                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
52679         }
52680         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52681     },
52682
52683     updateSplitters : function(){
52684         var cm = this.cm, s = this.getSplitters();
52685         if(s){ // splitters not created yet
52686             var pos = 0, locked = true;
52687             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52688                 if(cm.isHidden(i)) continue;
52689                 var w = cm.getColumnWidth(i); // make sure it's a number
52690                 if(!cm.isLocked(i) && locked){
52691                     pos = 0;
52692                     locked = false;
52693                 }
52694                 pos += w;
52695                 s[i].style.left = (pos-this.splitOffset) + "px";
52696             }
52697         }
52698     },
52699
52700     handleHiddenChange : function(colModel, colIndex, hidden){
52701         if(hidden){
52702             this.hideColumn(colIndex);
52703         }else{
52704             this.unhideColumn(colIndex);
52705         }
52706     },
52707
52708     hideColumn : function(colIndex){
52709         var cid = this.getColumnId(colIndex);
52710         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
52711         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52712         if(Roo.isSafari){
52713             this.updateHeaders();
52714         }
52715         this.updateSplitters();
52716         this.layout();
52717     },
52718
52719     unhideColumn : function(colIndex){
52720         var cid = this.getColumnId(colIndex);
52721         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52722         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52723
52724         if(Roo.isSafari){
52725             this.updateHeaders();
52726         }
52727         this.updateSplitters();
52728         this.layout();
52729     },
52730
52731     insertRows : function(dm, firstRow, lastRow, isUpdate){
52732         if(firstRow == 0 && lastRow == dm.getCount()-1){
52733             this.refresh();
52734         }else{
52735             if(!isUpdate){
52736                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52737             }
52738             var s = this.getScrollState();
52739             var markup = this.renderRows(firstRow, lastRow);
52740             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52741             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52742             this.restoreScroll(s);
52743             if(!isUpdate){
52744                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52745                 this.syncRowHeights(firstRow, lastRow);
52746                 this.stripeRows(firstRow);
52747                 this.layout();
52748             }
52749         }
52750     },
52751
52752     bufferRows : function(markup, target, index){
52753         var before = null, trows = target.rows, tbody = target.tBodies[0];
52754         if(index < trows.length){
52755             before = trows[index];
52756         }
52757         var b = document.createElement("div");
52758         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52759         var rows = b.firstChild.rows;
52760         for(var i = 0, len = rows.length; i < len; i++){
52761             if(before){
52762                 tbody.insertBefore(rows[0], before);
52763             }else{
52764                 tbody.appendChild(rows[0]);
52765             }
52766         }
52767         b.innerHTML = "";
52768         b = null;
52769     },
52770
52771     deleteRows : function(dm, firstRow, lastRow){
52772         if(dm.getRowCount()<1){
52773             this.fireEvent("beforerefresh", this);
52774             this.mainBody.update("");
52775             this.lockedBody.update("");
52776             this.fireEvent("refresh", this);
52777         }else{
52778             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52779             var bt = this.getBodyTable();
52780             var tbody = bt.firstChild;
52781             var rows = bt.rows;
52782             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52783                 tbody.removeChild(rows[firstRow]);
52784             }
52785             this.stripeRows(firstRow);
52786             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52787         }
52788     },
52789
52790     updateRows : function(dataSource, firstRow, lastRow){
52791         var s = this.getScrollState();
52792         this.refresh();
52793         this.restoreScroll(s);
52794     },
52795
52796     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52797         if(!noRefresh){
52798            this.refresh();
52799         }
52800         this.updateHeaderSortState();
52801     },
52802
52803     getScrollState : function(){
52804         
52805         var sb = this.scroller.dom;
52806         return {left: sb.scrollLeft, top: sb.scrollTop};
52807     },
52808
52809     stripeRows : function(startRow){
52810         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52811             return;
52812         }
52813         startRow = startRow || 0;
52814         var rows = this.getBodyTable().rows;
52815         var lrows = this.getLockedTable().rows;
52816         var cls = ' x-grid-row-alt ';
52817         for(var i = startRow, len = rows.length; i < len; i++){
52818             var row = rows[i], lrow = lrows[i];
52819             var isAlt = ((i+1) % 2 == 0);
52820             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52821             if(isAlt == hasAlt){
52822                 continue;
52823             }
52824             if(isAlt){
52825                 row.className += " x-grid-row-alt";
52826             }else{
52827                 row.className = row.className.replace("x-grid-row-alt", "");
52828             }
52829             if(lrow){
52830                 lrow.className = row.className;
52831             }
52832         }
52833     },
52834
52835     restoreScroll : function(state){
52836         //Roo.log('GridView.restoreScroll');
52837         var sb = this.scroller.dom;
52838         sb.scrollLeft = state.left;
52839         sb.scrollTop = state.top;
52840         this.syncScroll();
52841     },
52842
52843     syncScroll : function(){
52844         //Roo.log('GridView.syncScroll');
52845         var sb = this.scroller.dom;
52846         var sh = this.mainHd.dom;
52847         var bs = this.mainBody.dom;
52848         var lv = this.lockedBody.dom;
52849         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52850         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52851     },
52852
52853     handleScroll : function(e){
52854         this.syncScroll();
52855         var sb = this.scroller.dom;
52856         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52857         e.stopEvent();
52858     },
52859
52860     handleWheel : function(e){
52861         var d = e.getWheelDelta();
52862         this.scroller.dom.scrollTop -= d*22;
52863         // set this here to prevent jumpy scrolling on large tables
52864         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52865         e.stopEvent();
52866     },
52867
52868     renderRows : function(startRow, endRow){
52869         // pull in all the crap needed to render rows
52870         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52871         var colCount = cm.getColumnCount();
52872
52873         if(ds.getCount() < 1){
52874             return ["", ""];
52875         }
52876
52877         // build a map for all the columns
52878         var cs = [];
52879         for(var i = 0; i < colCount; i++){
52880             var name = cm.getDataIndex(i);
52881             cs[i] = {
52882                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52883                 renderer : cm.getRenderer(i),
52884                 id : cm.getColumnId(i),
52885                 locked : cm.isLocked(i)
52886             };
52887         }
52888
52889         startRow = startRow || 0;
52890         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52891
52892         // records to render
52893         var rs = ds.getRange(startRow, endRow);
52894
52895         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52896     },
52897
52898     // As much as I hate to duplicate code, this was branched because FireFox really hates
52899     // [].join("") on strings. The performance difference was substantial enough to
52900     // branch this function
52901     doRender : Roo.isGecko ?
52902             function(cs, rs, ds, startRow, colCount, stripe){
52903                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52904                 // buffers
52905                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52906                 
52907                 var hasListener = this.grid.hasListener('rowclass');
52908                 var rowcfg = {};
52909                 for(var j = 0, len = rs.length; j < len; j++){
52910                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52911                     for(var i = 0; i < colCount; i++){
52912                         c = cs[i];
52913                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52914                         p.id = c.id;
52915                         p.css = p.attr = "";
52916                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52917                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52918                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52919                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52920                         }
52921                         var markup = ct.apply(p);
52922                         if(!c.locked){
52923                             cb+= markup;
52924                         }else{
52925                             lcb+= markup;
52926                         }
52927                     }
52928                     var alt = [];
52929                     if(stripe && ((rowIndex+1) % 2 == 0)){
52930                         alt.push("x-grid-row-alt")
52931                     }
52932                     if(r.dirty){
52933                         alt.push(  " x-grid-dirty-row");
52934                     }
52935                     rp.cells = lcb;
52936                     if(this.getRowClass){
52937                         alt.push(this.getRowClass(r, rowIndex));
52938                     }
52939                     if (hasListener) {
52940                         rowcfg = {
52941                              
52942                             record: r,
52943                             rowIndex : rowIndex,
52944                             rowClass : ''
52945                         }
52946                         this.grid.fireEvent('rowclass', this, rowcfg);
52947                         alt.push(rowcfg.rowClass);
52948                     }
52949                     rp.alt = alt.join(" ");
52950                     lbuf+= rt.apply(rp);
52951                     rp.cells = cb;
52952                     buf+=  rt.apply(rp);
52953                 }
52954                 return [lbuf, buf];
52955             } :
52956             function(cs, rs, ds, startRow, colCount, stripe){
52957                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52958                 // buffers
52959                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52960                 var hasListener = this.grid.hasListener('rowclass');
52961  
52962                 var rowcfg = {};
52963                 for(var j = 0, len = rs.length; j < len; j++){
52964                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52965                     for(var i = 0; i < colCount; i++){
52966                         c = cs[i];
52967                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52968                         p.id = c.id;
52969                         p.css = p.attr = "";
52970                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52971                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52972                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52973                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52974                         }
52975                         
52976                         var markup = ct.apply(p);
52977                         if(!c.locked){
52978                             cb[cb.length] = markup;
52979                         }else{
52980                             lcb[lcb.length] = markup;
52981                         }
52982                     }
52983                     var alt = [];
52984                     if(stripe && ((rowIndex+1) % 2 == 0)){
52985                         alt.push( "x-grid-row-alt");
52986                     }
52987                     if(r.dirty){
52988                         alt.push(" x-grid-dirty-row");
52989                     }
52990                     rp.cells = lcb;
52991                     if(this.getRowClass){
52992                         alt.push( this.getRowClass(r, rowIndex));
52993                     }
52994                     if (hasListener) {
52995                         rowcfg = {
52996                              
52997                             record: r,
52998                             rowIndex : rowIndex,
52999                             rowClass : ''
53000                         }
53001                         this.grid.fireEvent('rowclass', this, rowcfg);
53002                         alt.push(rowcfg.rowClass);
53003                     }
53004                     rp.alt = alt.join(" ");
53005                     rp.cells = lcb.join("");
53006                     lbuf[lbuf.length] = rt.apply(rp);
53007                     rp.cells = cb.join("");
53008                     buf[buf.length] =  rt.apply(rp);
53009                 }
53010                 return [lbuf.join(""), buf.join("")];
53011             },
53012
53013     renderBody : function(){
53014         var markup = this.renderRows();
53015         var bt = this.templates.body;
53016         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53017     },
53018
53019     /**
53020      * Refreshes the grid
53021      * @param {Boolean} headersToo
53022      */
53023     refresh : function(headersToo){
53024         this.fireEvent("beforerefresh", this);
53025         this.grid.stopEditing();
53026         var result = this.renderBody();
53027         this.lockedBody.update(result[0]);
53028         this.mainBody.update(result[1]);
53029         if(headersToo === true){
53030             this.updateHeaders();
53031             this.updateColumns();
53032             this.updateSplitters();
53033             this.updateHeaderSortState();
53034         }
53035         this.syncRowHeights();
53036         this.layout();
53037         this.fireEvent("refresh", this);
53038     },
53039
53040     handleColumnMove : function(cm, oldIndex, newIndex){
53041         this.indexMap = null;
53042         var s = this.getScrollState();
53043         this.refresh(true);
53044         this.restoreScroll(s);
53045         this.afterMove(newIndex);
53046     },
53047
53048     afterMove : function(colIndex){
53049         if(this.enableMoveAnim && Roo.enableFx){
53050             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53051         }
53052         // if multisort - fix sortOrder, and reload..
53053         if (this.grid.dataSource.multiSort) {
53054             // the we can call sort again..
53055             var dm = this.grid.dataSource;
53056             var cm = this.grid.colModel;
53057             var so = [];
53058             for(var i = 0; i < cm.config.length; i++ ) {
53059                 
53060                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53061                     continue; // dont' bother, it's not in sort list or being set.
53062                 }
53063                 
53064                 so.push(cm.config[i].dataIndex);
53065             };
53066             dm.sortOrder = so;
53067             dm.load(dm.lastOptions);
53068             
53069             
53070         }
53071         
53072     },
53073
53074     updateCell : function(dm, rowIndex, dataIndex){
53075         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53076         if(typeof colIndex == "undefined"){ // not present in grid
53077             return;
53078         }
53079         var cm = this.grid.colModel;
53080         var cell = this.getCell(rowIndex, colIndex);
53081         var cellText = this.getCellText(rowIndex, colIndex);
53082
53083         var p = {
53084             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53085             id : cm.getColumnId(colIndex),
53086             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53087         };
53088         var renderer = cm.getRenderer(colIndex);
53089         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53090         if(typeof val == "undefined" || val === "") val = "&#160;";
53091         cellText.innerHTML = val;
53092         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53093         this.syncRowHeights(rowIndex, rowIndex);
53094     },
53095
53096     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53097         var maxWidth = 0;
53098         if(this.grid.autoSizeHeaders){
53099             var h = this.getHeaderCellMeasure(colIndex);
53100             maxWidth = Math.max(maxWidth, h.scrollWidth);
53101         }
53102         var tb, index;
53103         if(this.cm.isLocked(colIndex)){
53104             tb = this.getLockedTable();
53105             index = colIndex;
53106         }else{
53107             tb = this.getBodyTable();
53108             index = colIndex - this.cm.getLockedCount();
53109         }
53110         if(tb && tb.rows){
53111             var rows = tb.rows;
53112             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53113             for(var i = 0; i < stopIndex; i++){
53114                 var cell = rows[i].childNodes[index].firstChild;
53115                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53116             }
53117         }
53118         return maxWidth + /*margin for error in IE*/ 5;
53119     },
53120     /**
53121      * Autofit a column to its content.
53122      * @param {Number} colIndex
53123      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53124      */
53125      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53126          if(this.cm.isHidden(colIndex)){
53127              return; // can't calc a hidden column
53128          }
53129         if(forceMinSize){
53130             var cid = this.cm.getColumnId(colIndex);
53131             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53132            if(this.grid.autoSizeHeaders){
53133                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53134            }
53135         }
53136         var newWidth = this.calcColumnWidth(colIndex);
53137         this.cm.setColumnWidth(colIndex,
53138             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53139         if(!suppressEvent){
53140             this.grid.fireEvent("columnresize", colIndex, newWidth);
53141         }
53142     },
53143
53144     /**
53145      * Autofits all columns to their content and then expands to fit any extra space in the grid
53146      */
53147      autoSizeColumns : function(){
53148         var cm = this.grid.colModel;
53149         var colCount = cm.getColumnCount();
53150         for(var i = 0; i < colCount; i++){
53151             this.autoSizeColumn(i, true, true);
53152         }
53153         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53154             this.fitColumns();
53155         }else{
53156             this.updateColumns();
53157             this.layout();
53158         }
53159     },
53160
53161     /**
53162      * Autofits all columns to the grid's width proportionate with their current size
53163      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53164      */
53165     fitColumns : function(reserveScrollSpace){
53166         var cm = this.grid.colModel;
53167         var colCount = cm.getColumnCount();
53168         var cols = [];
53169         var width = 0;
53170         var i, w;
53171         for (i = 0; i < colCount; i++){
53172             if(!cm.isHidden(i) && !cm.isFixed(i)){
53173                 w = cm.getColumnWidth(i);
53174                 cols.push(i);
53175                 cols.push(w);
53176                 width += w;
53177             }
53178         }
53179         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53180         if(reserveScrollSpace){
53181             avail -= 17;
53182         }
53183         var frac = (avail - cm.getTotalWidth())/width;
53184         while (cols.length){
53185             w = cols.pop();
53186             i = cols.pop();
53187             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53188         }
53189         this.updateColumns();
53190         this.layout();
53191     },
53192
53193     onRowSelect : function(rowIndex){
53194         var row = this.getRowComposite(rowIndex);
53195         row.addClass("x-grid-row-selected");
53196     },
53197
53198     onRowDeselect : function(rowIndex){
53199         var row = this.getRowComposite(rowIndex);
53200         row.removeClass("x-grid-row-selected");
53201     },
53202
53203     onCellSelect : function(row, col){
53204         var cell = this.getCell(row, col);
53205         if(cell){
53206             Roo.fly(cell).addClass("x-grid-cell-selected");
53207         }
53208     },
53209
53210     onCellDeselect : function(row, col){
53211         var cell = this.getCell(row, col);
53212         if(cell){
53213             Roo.fly(cell).removeClass("x-grid-cell-selected");
53214         }
53215     },
53216
53217     updateHeaderSortState : function(){
53218         
53219         // sort state can be single { field: xxx, direction : yyy}
53220         // or   { xxx=>ASC , yyy : DESC ..... }
53221         
53222         var mstate = {};
53223         if (!this.ds.multiSort) { 
53224             var state = this.ds.getSortState();
53225             if(!state){
53226                 return;
53227             }
53228             mstate[state.field] = state.direction;
53229             // FIXME... - this is not used here.. but might be elsewhere..
53230             this.sortState = state;
53231             
53232         } else {
53233             mstate = this.ds.sortToggle;
53234         }
53235         //remove existing sort classes..
53236         
53237         var sc = this.sortClasses;
53238         var hds = this.el.select(this.headerSelector).removeClass(sc);
53239         
53240         for(var f in mstate) {
53241         
53242             var sortColumn = this.cm.findColumnIndex(f);
53243             
53244             if(sortColumn != -1){
53245                 var sortDir = mstate[f];        
53246                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53247             }
53248         }
53249         
53250          
53251         
53252     },
53253
53254
53255     handleHeaderClick : function(g, index){
53256         if(this.headersDisabled){
53257             return;
53258         }
53259         var dm = g.dataSource, cm = g.colModel;
53260         if(!cm.isSortable(index)){
53261             return;
53262         }
53263         g.stopEditing();
53264         
53265         if (dm.multiSort) {
53266             // update the sortOrder
53267             var so = [];
53268             for(var i = 0; i < cm.config.length; i++ ) {
53269                 
53270                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53271                     continue; // dont' bother, it's not in sort list or being set.
53272                 }
53273                 
53274                 so.push(cm.config[i].dataIndex);
53275             };
53276             dm.sortOrder = so;
53277         }
53278         
53279         
53280         dm.sort(cm.getDataIndex(index));
53281     },
53282
53283
53284     destroy : function(){
53285         if(this.colMenu){
53286             this.colMenu.removeAll();
53287             Roo.menu.MenuMgr.unregister(this.colMenu);
53288             this.colMenu.getEl().remove();
53289             delete this.colMenu;
53290         }
53291         if(this.hmenu){
53292             this.hmenu.removeAll();
53293             Roo.menu.MenuMgr.unregister(this.hmenu);
53294             this.hmenu.getEl().remove();
53295             delete this.hmenu;
53296         }
53297         if(this.grid.enableColumnMove){
53298             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53299             if(dds){
53300                 for(var dd in dds){
53301                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53302                         var elid = dds[dd].dragElId;
53303                         dds[dd].unreg();
53304                         Roo.get(elid).remove();
53305                     } else if(dds[dd].config.isTarget){
53306                         dds[dd].proxyTop.remove();
53307                         dds[dd].proxyBottom.remove();
53308                         dds[dd].unreg();
53309                     }
53310                     if(Roo.dd.DDM.locationCache[dd]){
53311                         delete Roo.dd.DDM.locationCache[dd];
53312                     }
53313                 }
53314                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53315             }
53316         }
53317         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53318         this.bind(null, null);
53319         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53320     },
53321
53322     handleLockChange : function(){
53323         this.refresh(true);
53324     },
53325
53326     onDenyColumnLock : function(){
53327
53328     },
53329
53330     onDenyColumnHide : function(){
53331
53332     },
53333
53334     handleHdMenuClick : function(item){
53335         var index = this.hdCtxIndex;
53336         var cm = this.cm, ds = this.ds;
53337         switch(item.id){
53338             case "asc":
53339                 ds.sort(cm.getDataIndex(index), "ASC");
53340                 break;
53341             case "desc":
53342                 ds.sort(cm.getDataIndex(index), "DESC");
53343                 break;
53344             case "lock":
53345                 var lc = cm.getLockedCount();
53346                 if(cm.getColumnCount(true) <= lc+1){
53347                     this.onDenyColumnLock();
53348                     return;
53349                 }
53350                 if(lc != index){
53351                     cm.setLocked(index, true, true);
53352                     cm.moveColumn(index, lc);
53353                     this.grid.fireEvent("columnmove", index, lc);
53354                 }else{
53355                     cm.setLocked(index, true);
53356                 }
53357             break;
53358             case "unlock":
53359                 var lc = cm.getLockedCount();
53360                 if((lc-1) != index){
53361                     cm.setLocked(index, false, true);
53362                     cm.moveColumn(index, lc-1);
53363                     this.grid.fireEvent("columnmove", index, lc-1);
53364                 }else{
53365                     cm.setLocked(index, false);
53366                 }
53367             break;
53368             default:
53369                 index = cm.getIndexById(item.id.substr(4));
53370                 if(index != -1){
53371                     if(item.checked && cm.getColumnCount(true) <= 1){
53372                         this.onDenyColumnHide();
53373                         return false;
53374                     }
53375                     cm.setHidden(index, item.checked);
53376                 }
53377         }
53378         return true;
53379     },
53380
53381     beforeColMenuShow : function(){
53382         var cm = this.cm,  colCount = cm.getColumnCount();
53383         this.colMenu.removeAll();
53384         for(var i = 0; i < colCount; i++){
53385             this.colMenu.add(new Roo.menu.CheckItem({
53386                 id: "col-"+cm.getColumnId(i),
53387                 text: cm.getColumnHeader(i),
53388                 checked: !cm.isHidden(i),
53389                 hideOnClick:false
53390             }));
53391         }
53392     },
53393
53394     handleHdCtx : function(g, index, e){
53395         e.stopEvent();
53396         var hd = this.getHeaderCell(index);
53397         this.hdCtxIndex = index;
53398         var ms = this.hmenu.items, cm = this.cm;
53399         ms.get("asc").setDisabled(!cm.isSortable(index));
53400         ms.get("desc").setDisabled(!cm.isSortable(index));
53401         if(this.grid.enableColLock !== false){
53402             ms.get("lock").setDisabled(cm.isLocked(index));
53403             ms.get("unlock").setDisabled(!cm.isLocked(index));
53404         }
53405         this.hmenu.show(hd, "tl-bl");
53406     },
53407
53408     handleHdOver : function(e){
53409         var hd = this.findHeaderCell(e.getTarget());
53410         if(hd && !this.headersDisabled){
53411             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53412                this.fly(hd).addClass("x-grid-hd-over");
53413             }
53414         }
53415     },
53416
53417     handleHdOut : function(e){
53418         var hd = this.findHeaderCell(e.getTarget());
53419         if(hd){
53420             this.fly(hd).removeClass("x-grid-hd-over");
53421         }
53422     },
53423
53424     handleSplitDblClick : function(e, t){
53425         var i = this.getCellIndex(t);
53426         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53427             this.autoSizeColumn(i, true);
53428             this.layout();
53429         }
53430     },
53431
53432     render : function(){
53433
53434         var cm = this.cm;
53435         var colCount = cm.getColumnCount();
53436
53437         if(this.grid.monitorWindowResize === true){
53438             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53439         }
53440         var header = this.renderHeaders();
53441         var body = this.templates.body.apply({rows:""});
53442         var html = this.templates.master.apply({
53443             lockedBody: body,
53444             body: body,
53445             lockedHeader: header[0],
53446             header: header[1]
53447         });
53448
53449         //this.updateColumns();
53450
53451         this.grid.getGridEl().dom.innerHTML = html;
53452
53453         this.initElements();
53454         
53455         // a kludge to fix the random scolling effect in webkit
53456         this.el.on("scroll", function() {
53457             this.el.dom.scrollTop=0; // hopefully not recursive..
53458         },this);
53459
53460         this.scroller.on("scroll", this.handleScroll, this);
53461         this.lockedBody.on("mousewheel", this.handleWheel, this);
53462         this.mainBody.on("mousewheel", this.handleWheel, this);
53463
53464         this.mainHd.on("mouseover", this.handleHdOver, this);
53465         this.mainHd.on("mouseout", this.handleHdOut, this);
53466         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53467                 {delegate: "."+this.splitClass});
53468
53469         this.lockedHd.on("mouseover", this.handleHdOver, this);
53470         this.lockedHd.on("mouseout", this.handleHdOut, this);
53471         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53472                 {delegate: "."+this.splitClass});
53473
53474         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53475             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53476         }
53477
53478         this.updateSplitters();
53479
53480         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53481             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53482             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53483         }
53484
53485         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53486             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53487             this.hmenu.add(
53488                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53489                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53490             );
53491             if(this.grid.enableColLock !== false){
53492                 this.hmenu.add('-',
53493                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53494                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53495                 );
53496             }
53497             if(this.grid.enableColumnHide !== false){
53498
53499                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53500                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53501                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53502
53503                 this.hmenu.add('-',
53504                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53505                 );
53506             }
53507             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53508
53509             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53510         }
53511
53512         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53513             this.dd = new Roo.grid.GridDragZone(this.grid, {
53514                 ddGroup : this.grid.ddGroup || 'GridDD'
53515             });
53516             
53517         }
53518
53519         /*
53520         for(var i = 0; i < colCount; i++){
53521             if(cm.isHidden(i)){
53522                 this.hideColumn(i);
53523             }
53524             if(cm.config[i].align){
53525                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53526                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53527             }
53528         }*/
53529         
53530         this.updateHeaderSortState();
53531
53532         this.beforeInitialResize();
53533         this.layout(true);
53534
53535         // two part rendering gives faster view to the user
53536         this.renderPhase2.defer(1, this);
53537     },
53538
53539     renderPhase2 : function(){
53540         // render the rows now
53541         this.refresh();
53542         if(this.grid.autoSizeColumns){
53543             this.autoSizeColumns();
53544         }
53545     },
53546
53547     beforeInitialResize : function(){
53548
53549     },
53550
53551     onColumnSplitterMoved : function(i, w){
53552         this.userResized = true;
53553         var cm = this.grid.colModel;
53554         cm.setColumnWidth(i, w, true);
53555         var cid = cm.getColumnId(i);
53556         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53557         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53558         this.updateSplitters();
53559         this.layout();
53560         this.grid.fireEvent("columnresize", i, w);
53561     },
53562
53563     syncRowHeights : function(startIndex, endIndex){
53564         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53565             startIndex = startIndex || 0;
53566             var mrows = this.getBodyTable().rows;
53567             var lrows = this.getLockedTable().rows;
53568             var len = mrows.length-1;
53569             endIndex = Math.min(endIndex || len, len);
53570             for(var i = startIndex; i <= endIndex; i++){
53571                 var m = mrows[i], l = lrows[i];
53572                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53573                 m.style.height = l.style.height = h + "px";
53574             }
53575         }
53576     },
53577
53578     layout : function(initialRender, is2ndPass){
53579         var g = this.grid;
53580         var auto = g.autoHeight;
53581         var scrollOffset = 16;
53582         var c = g.getGridEl(), cm = this.cm,
53583                 expandCol = g.autoExpandColumn,
53584                 gv = this;
53585         //c.beginMeasure();
53586
53587         if(!c.dom.offsetWidth){ // display:none?
53588             if(initialRender){
53589                 this.lockedWrap.show();
53590                 this.mainWrap.show();
53591             }
53592             return;
53593         }
53594
53595         var hasLock = this.cm.isLocked(0);
53596
53597         var tbh = this.headerPanel.getHeight();
53598         var bbh = this.footerPanel.getHeight();
53599
53600         if(auto){
53601             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53602             var newHeight = ch + c.getBorderWidth("tb");
53603             if(g.maxHeight){
53604                 newHeight = Math.min(g.maxHeight, newHeight);
53605             }
53606             c.setHeight(newHeight);
53607         }
53608
53609         if(g.autoWidth){
53610             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
53611         }
53612
53613         var s = this.scroller;
53614
53615         var csize = c.getSize(true);
53616
53617         this.el.setSize(csize.width, csize.height);
53618
53619         this.headerPanel.setWidth(csize.width);
53620         this.footerPanel.setWidth(csize.width);
53621
53622         var hdHeight = this.mainHd.getHeight();
53623         var vw = csize.width;
53624         var vh = csize.height - (tbh + bbh);
53625
53626         s.setSize(vw, vh);
53627
53628         var bt = this.getBodyTable();
53629         var ltWidth = hasLock ?
53630                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
53631
53632         var scrollHeight = bt.offsetHeight;
53633         var scrollWidth = ltWidth + bt.offsetWidth;
53634         var vscroll = false, hscroll = false;
53635
53636         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
53637
53638         var lw = this.lockedWrap, mw = this.mainWrap;
53639         var lb = this.lockedBody, mb = this.mainBody;
53640
53641         setTimeout(function(){
53642             var t = s.dom.offsetTop;
53643             var w = s.dom.clientWidth,
53644                 h = s.dom.clientHeight;
53645
53646             lw.setTop(t);
53647             lw.setSize(ltWidth, h);
53648
53649             mw.setLeftTop(ltWidth, t);
53650             mw.setSize(w-ltWidth, h);
53651
53652             lb.setHeight(h-hdHeight);
53653             mb.setHeight(h-hdHeight);
53654
53655             if(is2ndPass !== true && !gv.userResized && expandCol){
53656                 // high speed resize without full column calculation
53657                 
53658                 var ci = cm.getIndexById(expandCol);
53659                 if (ci < 0) {
53660                     ci = cm.findColumnIndex(expandCol);
53661                 }
53662                 ci = Math.max(0, ci); // make sure it's got at least the first col.
53663                 var expandId = cm.getColumnId(ci);
53664                 var  tw = cm.getTotalWidth(false);
53665                 var currentWidth = cm.getColumnWidth(ci);
53666                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
53667                 if(currentWidth != cw){
53668                     cm.setColumnWidth(ci, cw, true);
53669                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53670                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53671                     gv.updateSplitters();
53672                     gv.layout(false, true);
53673                 }
53674             }
53675
53676             if(initialRender){
53677                 lw.show();
53678                 mw.show();
53679             }
53680             //c.endMeasure();
53681         }, 10);
53682     },
53683
53684     onWindowResize : function(){
53685         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
53686             return;
53687         }
53688         this.layout();
53689     },
53690
53691     appendFooter : function(parentEl){
53692         return null;
53693     },
53694
53695     sortAscText : "Sort Ascending",
53696     sortDescText : "Sort Descending",
53697     lockText : "Lock Column",
53698     unlockText : "Unlock Column",
53699     columnsText : "Columns"
53700 });
53701
53702
53703 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
53704     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
53705     this.proxy.el.addClass('x-grid3-col-dd');
53706 };
53707
53708 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
53709     handleMouseDown : function(e){
53710
53711     },
53712
53713     callHandleMouseDown : function(e){
53714         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53715     }
53716 });
53717 /*
53718  * Based on:
53719  * Ext JS Library 1.1.1
53720  * Copyright(c) 2006-2007, Ext JS, LLC.
53721  *
53722  * Originally Released Under LGPL - original licence link has changed is not relivant.
53723  *
53724  * Fork - LGPL
53725  * <script type="text/javascript">
53726  */
53727  
53728 // private
53729 // This is a support class used internally by the Grid components
53730 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53731     this.grid = grid;
53732     this.view = grid.getView();
53733     this.proxy = this.view.resizeProxy;
53734     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53735         "gridSplitters" + this.grid.getGridEl().id, {
53736         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53737     });
53738     this.setHandleElId(Roo.id(hd));
53739     this.setOuterHandleElId(Roo.id(hd2));
53740     this.scroll = false;
53741 };
53742 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53743     fly: Roo.Element.fly,
53744
53745     b4StartDrag : function(x, y){
53746         this.view.headersDisabled = true;
53747         this.proxy.setHeight(this.view.mainWrap.getHeight());
53748         var w = this.cm.getColumnWidth(this.cellIndex);
53749         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53750         this.resetConstraints();
53751         this.setXConstraint(minw, 1000);
53752         this.setYConstraint(0, 0);
53753         this.minX = x - minw;
53754         this.maxX = x + 1000;
53755         this.startPos = x;
53756         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53757     },
53758
53759
53760     handleMouseDown : function(e){
53761         ev = Roo.EventObject.setEvent(e);
53762         var t = this.fly(ev.getTarget());
53763         if(t.hasClass("x-grid-split")){
53764             this.cellIndex = this.view.getCellIndex(t.dom);
53765             this.split = t.dom;
53766             this.cm = this.grid.colModel;
53767             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53768                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53769             }
53770         }
53771     },
53772
53773     endDrag : function(e){
53774         this.view.headersDisabled = false;
53775         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53776         var diff = endX - this.startPos;
53777         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53778     },
53779
53780     autoOffset : function(){
53781         this.setDelta(0,0);
53782     }
53783 });/*
53784  * Based on:
53785  * Ext JS Library 1.1.1
53786  * Copyright(c) 2006-2007, Ext JS, LLC.
53787  *
53788  * Originally Released Under LGPL - original licence link has changed is not relivant.
53789  *
53790  * Fork - LGPL
53791  * <script type="text/javascript">
53792  */
53793  
53794 // private
53795 // This is a support class used internally by the Grid components
53796 Roo.grid.GridDragZone = function(grid, config){
53797     this.view = grid.getView();
53798     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53799     if(this.view.lockedBody){
53800         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53801         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53802     }
53803     this.scroll = false;
53804     this.grid = grid;
53805     this.ddel = document.createElement('div');
53806     this.ddel.className = 'x-grid-dd-wrap';
53807 };
53808
53809 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53810     ddGroup : "GridDD",
53811
53812     getDragData : function(e){
53813         var t = Roo.lib.Event.getTarget(e);
53814         var rowIndex = this.view.findRowIndex(t);
53815         var sm = this.grid.selModel;
53816             
53817         //Roo.log(rowIndex);
53818         
53819         if (sm.getSelectedCell) {
53820             // cell selection..
53821             if (!sm.getSelectedCell()) {
53822                 return false;
53823             }
53824             if (rowIndex != sm.getSelectedCell()[0]) {
53825                 return false;
53826             }
53827         
53828         }
53829         
53830         if(rowIndex !== false){
53831             
53832             // if editorgrid.. 
53833             
53834             
53835             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53836                
53837             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53838               //  
53839             //}
53840             if (e.hasModifier()){
53841                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53842             }
53843             
53844             Roo.log("getDragData");
53845             
53846             return {
53847                 grid: this.grid,
53848                 ddel: this.ddel,
53849                 rowIndex: rowIndex,
53850                 selections:sm.getSelections ? sm.getSelections() : (
53851                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53852                 )
53853             };
53854         }
53855         return false;
53856     },
53857
53858     onInitDrag : function(e){
53859         var data = this.dragData;
53860         this.ddel.innerHTML = this.grid.getDragDropText();
53861         this.proxy.update(this.ddel);
53862         // fire start drag?
53863     },
53864
53865     afterRepair : function(){
53866         this.dragging = false;
53867     },
53868
53869     getRepairXY : function(e, data){
53870         return false;
53871     },
53872
53873     onEndDrag : function(data, e){
53874         // fire end drag?
53875     },
53876
53877     onValidDrop : function(dd, e, id){
53878         // fire drag drop?
53879         this.hideProxy();
53880     },
53881
53882     beforeInvalidDrop : function(e, id){
53883
53884     }
53885 });/*
53886  * Based on:
53887  * Ext JS Library 1.1.1
53888  * Copyright(c) 2006-2007, Ext JS, LLC.
53889  *
53890  * Originally Released Under LGPL - original licence link has changed is not relivant.
53891  *
53892  * Fork - LGPL
53893  * <script type="text/javascript">
53894  */
53895  
53896
53897 /**
53898  * @class Roo.grid.ColumnModel
53899  * @extends Roo.util.Observable
53900  * This is the default implementation of a ColumnModel used by the Grid. It defines
53901  * the columns in the grid.
53902  * <br>Usage:<br>
53903  <pre><code>
53904  var colModel = new Roo.grid.ColumnModel([
53905         {header: "Ticker", width: 60, sortable: true, locked: true},
53906         {header: "Company Name", width: 150, sortable: true},
53907         {header: "Market Cap.", width: 100, sortable: true},
53908         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53909         {header: "Employees", width: 100, sortable: true, resizable: false}
53910  ]);
53911  </code></pre>
53912  * <p>
53913  
53914  * The config options listed for this class are options which may appear in each
53915  * individual column definition.
53916  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53917  * @constructor
53918  * @param {Object} config An Array of column config objects. See this class's
53919  * config objects for details.
53920 */
53921 Roo.grid.ColumnModel = function(config){
53922         /**
53923      * The config passed into the constructor
53924      */
53925     this.config = config;
53926     this.lookup = {};
53927
53928     // if no id, create one
53929     // if the column does not have a dataIndex mapping,
53930     // map it to the order it is in the config
53931     for(var i = 0, len = config.length; i < len; i++){
53932         var c = config[i];
53933         if(typeof c.dataIndex == "undefined"){
53934             c.dataIndex = i;
53935         }
53936         if(typeof c.renderer == "string"){
53937             c.renderer = Roo.util.Format[c.renderer];
53938         }
53939         if(typeof c.id == "undefined"){
53940             c.id = Roo.id();
53941         }
53942         if(c.editor && c.editor.xtype){
53943             c.editor  = Roo.factory(c.editor, Roo.grid);
53944         }
53945         if(c.editor && c.editor.isFormField){
53946             c.editor = new Roo.grid.GridEditor(c.editor);
53947         }
53948         this.lookup[c.id] = c;
53949     }
53950
53951     /**
53952      * The width of columns which have no width specified (defaults to 100)
53953      * @type Number
53954      */
53955     this.defaultWidth = 100;
53956
53957     /**
53958      * Default sortable of columns which have no sortable specified (defaults to false)
53959      * @type Boolean
53960      */
53961     this.defaultSortable = false;
53962
53963     this.addEvents({
53964         /**
53965              * @event widthchange
53966              * Fires when the width of a column changes.
53967              * @param {ColumnModel} this
53968              * @param {Number} columnIndex The column index
53969              * @param {Number} newWidth The new width
53970              */
53971             "widthchange": true,
53972         /**
53973              * @event headerchange
53974              * Fires when the text of a header changes.
53975              * @param {ColumnModel} this
53976              * @param {Number} columnIndex The column index
53977              * @param {Number} newText The new header text
53978              */
53979             "headerchange": true,
53980         /**
53981              * @event hiddenchange
53982              * Fires when a column is hidden or "unhidden".
53983              * @param {ColumnModel} this
53984              * @param {Number} columnIndex The column index
53985              * @param {Boolean} hidden true if hidden, false otherwise
53986              */
53987             "hiddenchange": true,
53988             /**
53989          * @event columnmoved
53990          * Fires when a column is moved.
53991          * @param {ColumnModel} this
53992          * @param {Number} oldIndex
53993          * @param {Number} newIndex
53994          */
53995         "columnmoved" : true,
53996         /**
53997          * @event columlockchange
53998          * Fires when a column's locked state is changed
53999          * @param {ColumnModel} this
54000          * @param {Number} colIndex
54001          * @param {Boolean} locked true if locked
54002          */
54003         "columnlockchange" : true
54004     });
54005     Roo.grid.ColumnModel.superclass.constructor.call(this);
54006 };
54007 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54008     /**
54009      * @cfg {String} header The header text to display in the Grid view.
54010      */
54011     /**
54012      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54013      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54014      * specified, the column's index is used as an index into the Record's data Array.
54015      */
54016     /**
54017      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54018      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54019      */
54020     /**
54021      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54022      * Defaults to the value of the {@link #defaultSortable} property.
54023      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54024      */
54025     /**
54026      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54027      */
54028     /**
54029      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54030      */
54031     /**
54032      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54033      */
54034     /**
54035      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54036      */
54037     /**
54038      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54039      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54040      * default renderer uses the raw data value.
54041      */
54042        /**
54043      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54044      */
54045     /**
54046      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54047      */
54048
54049     /**
54050      * Returns the id of the column at the specified index.
54051      * @param {Number} index The column index
54052      * @return {String} the id
54053      */
54054     getColumnId : function(index){
54055         return this.config[index].id;
54056     },
54057
54058     /**
54059      * Returns the column for a specified id.
54060      * @param {String} id The column id
54061      * @return {Object} the column
54062      */
54063     getColumnById : function(id){
54064         return this.lookup[id];
54065     },
54066
54067     
54068     /**
54069      * Returns the column for a specified dataIndex.
54070      * @param {String} dataIndex The column dataIndex
54071      * @return {Object|Boolean} the column or false if not found
54072      */
54073     getColumnByDataIndex: function(dataIndex){
54074         var index = this.findColumnIndex(dataIndex);
54075         return index > -1 ? this.config[index] : false;
54076     },
54077     
54078     /**
54079      * Returns the index for a specified column id.
54080      * @param {String} id The column id
54081      * @return {Number} the index, or -1 if not found
54082      */
54083     getIndexById : function(id){
54084         for(var i = 0, len = this.config.length; i < len; i++){
54085             if(this.config[i].id == id){
54086                 return i;
54087             }
54088         }
54089         return -1;
54090     },
54091     
54092     /**
54093      * Returns the index for a specified column dataIndex.
54094      * @param {String} dataIndex The column dataIndex
54095      * @return {Number} the index, or -1 if not found
54096      */
54097     
54098     findColumnIndex : function(dataIndex){
54099         for(var i = 0, len = this.config.length; i < len; i++){
54100             if(this.config[i].dataIndex == dataIndex){
54101                 return i;
54102             }
54103         }
54104         return -1;
54105     },
54106     
54107     
54108     moveColumn : function(oldIndex, newIndex){
54109         var c = this.config[oldIndex];
54110         this.config.splice(oldIndex, 1);
54111         this.config.splice(newIndex, 0, c);
54112         this.dataMap = null;
54113         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54114     },
54115
54116     isLocked : function(colIndex){
54117         return this.config[colIndex].locked === true;
54118     },
54119
54120     setLocked : function(colIndex, value, suppressEvent){
54121         if(this.isLocked(colIndex) == value){
54122             return;
54123         }
54124         this.config[colIndex].locked = value;
54125         if(!suppressEvent){
54126             this.fireEvent("columnlockchange", this, colIndex, value);
54127         }
54128     },
54129
54130     getTotalLockedWidth : function(){
54131         var totalWidth = 0;
54132         for(var i = 0; i < this.config.length; i++){
54133             if(this.isLocked(i) && !this.isHidden(i)){
54134                 this.totalWidth += this.getColumnWidth(i);
54135             }
54136         }
54137         return totalWidth;
54138     },
54139
54140     getLockedCount : function(){
54141         for(var i = 0, len = this.config.length; i < len; i++){
54142             if(!this.isLocked(i)){
54143                 return i;
54144             }
54145         }
54146     },
54147
54148     /**
54149      * Returns the number of columns.
54150      * @return {Number}
54151      */
54152     getColumnCount : function(visibleOnly){
54153         if(visibleOnly === true){
54154             var c = 0;
54155             for(var i = 0, len = this.config.length; i < len; i++){
54156                 if(!this.isHidden(i)){
54157                     c++;
54158                 }
54159             }
54160             return c;
54161         }
54162         return this.config.length;
54163     },
54164
54165     /**
54166      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54167      * @param {Function} fn
54168      * @param {Object} scope (optional)
54169      * @return {Array} result
54170      */
54171     getColumnsBy : function(fn, scope){
54172         var r = [];
54173         for(var i = 0, len = this.config.length; i < len; i++){
54174             var c = this.config[i];
54175             if(fn.call(scope||this, c, i) === true){
54176                 r[r.length] = c;
54177             }
54178         }
54179         return r;
54180     },
54181
54182     /**
54183      * Returns true if the specified column is sortable.
54184      * @param {Number} col The column index
54185      * @return {Boolean}
54186      */
54187     isSortable : function(col){
54188         if(typeof this.config[col].sortable == "undefined"){
54189             return this.defaultSortable;
54190         }
54191         return this.config[col].sortable;
54192     },
54193
54194     /**
54195      * Returns the rendering (formatting) function defined for the column.
54196      * @param {Number} col The column index.
54197      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54198      */
54199     getRenderer : function(col){
54200         if(!this.config[col].renderer){
54201             return Roo.grid.ColumnModel.defaultRenderer;
54202         }
54203         return this.config[col].renderer;
54204     },
54205
54206     /**
54207      * Sets the rendering (formatting) function for a column.
54208      * @param {Number} col The column index
54209      * @param {Function} fn The function to use to process the cell's raw data
54210      * to return HTML markup for the grid view. The render function is called with
54211      * the following parameters:<ul>
54212      * <li>Data value.</li>
54213      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54214      * <li>css A CSS style string to apply to the table cell.</li>
54215      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54216      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54217      * <li>Row index</li>
54218      * <li>Column index</li>
54219      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54220      */
54221     setRenderer : function(col, fn){
54222         this.config[col].renderer = fn;
54223     },
54224
54225     /**
54226      * Returns the width for the specified column.
54227      * @param {Number} col The column index
54228      * @return {Number}
54229      */
54230     getColumnWidth : function(col){
54231         return this.config[col].width * 1 || this.defaultWidth;
54232     },
54233
54234     /**
54235      * Sets the width for a column.
54236      * @param {Number} col The column index
54237      * @param {Number} width The new width
54238      */
54239     setColumnWidth : function(col, width, suppressEvent){
54240         this.config[col].width = width;
54241         this.totalWidth = null;
54242         if(!suppressEvent){
54243              this.fireEvent("widthchange", this, col, width);
54244         }
54245     },
54246
54247     /**
54248      * Returns the total width of all columns.
54249      * @param {Boolean} includeHidden True to include hidden column widths
54250      * @return {Number}
54251      */
54252     getTotalWidth : function(includeHidden){
54253         if(!this.totalWidth){
54254             this.totalWidth = 0;
54255             for(var i = 0, len = this.config.length; i < len; i++){
54256                 if(includeHidden || !this.isHidden(i)){
54257                     this.totalWidth += this.getColumnWidth(i);
54258                 }
54259             }
54260         }
54261         return this.totalWidth;
54262     },
54263
54264     /**
54265      * Returns the header for the specified column.
54266      * @param {Number} col The column index
54267      * @return {String}
54268      */
54269     getColumnHeader : function(col){
54270         return this.config[col].header;
54271     },
54272
54273     /**
54274      * Sets the header for a column.
54275      * @param {Number} col The column index
54276      * @param {String} header The new header
54277      */
54278     setColumnHeader : function(col, header){
54279         this.config[col].header = header;
54280         this.fireEvent("headerchange", this, col, header);
54281     },
54282
54283     /**
54284      * Returns the tooltip for the specified column.
54285      * @param {Number} col The column index
54286      * @return {String}
54287      */
54288     getColumnTooltip : function(col){
54289             return this.config[col].tooltip;
54290     },
54291     /**
54292      * Sets the tooltip for a column.
54293      * @param {Number} col The column index
54294      * @param {String} tooltip The new tooltip
54295      */
54296     setColumnTooltip : function(col, tooltip){
54297             this.config[col].tooltip = tooltip;
54298     },
54299
54300     /**
54301      * Returns the dataIndex for the specified column.
54302      * @param {Number} col The column index
54303      * @return {Number}
54304      */
54305     getDataIndex : function(col){
54306         return this.config[col].dataIndex;
54307     },
54308
54309     /**
54310      * Sets the dataIndex for a column.
54311      * @param {Number} col The column index
54312      * @param {Number} dataIndex The new dataIndex
54313      */
54314     setDataIndex : function(col, dataIndex){
54315         this.config[col].dataIndex = dataIndex;
54316     },
54317
54318     
54319     
54320     /**
54321      * Returns true if the cell is editable.
54322      * @param {Number} colIndex The column index
54323      * @param {Number} rowIndex The row index
54324      * @return {Boolean}
54325      */
54326     isCellEditable : function(colIndex, rowIndex){
54327         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54328     },
54329
54330     /**
54331      * Returns the editor defined for the cell/column.
54332      * return false or null to disable editing.
54333      * @param {Number} colIndex The column index
54334      * @param {Number} rowIndex The row index
54335      * @return {Object}
54336      */
54337     getCellEditor : function(colIndex, rowIndex){
54338         return this.config[colIndex].editor;
54339     },
54340
54341     /**
54342      * Sets if a column is editable.
54343      * @param {Number} col The column index
54344      * @param {Boolean} editable True if the column is editable
54345      */
54346     setEditable : function(col, editable){
54347         this.config[col].editable = editable;
54348     },
54349
54350
54351     /**
54352      * Returns true if the column is hidden.
54353      * @param {Number} colIndex The column index
54354      * @return {Boolean}
54355      */
54356     isHidden : function(colIndex){
54357         return this.config[colIndex].hidden;
54358     },
54359
54360
54361     /**
54362      * Returns true if the column width cannot be changed
54363      */
54364     isFixed : function(colIndex){
54365         return this.config[colIndex].fixed;
54366     },
54367
54368     /**
54369      * Returns true if the column can be resized
54370      * @return {Boolean}
54371      */
54372     isResizable : function(colIndex){
54373         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54374     },
54375     /**
54376      * Sets if a column is hidden.
54377      * @param {Number} colIndex The column index
54378      * @param {Boolean} hidden True if the column is hidden
54379      */
54380     setHidden : function(colIndex, hidden){
54381         this.config[colIndex].hidden = hidden;
54382         this.totalWidth = null;
54383         this.fireEvent("hiddenchange", this, colIndex, hidden);
54384     },
54385
54386     /**
54387      * Sets the editor for a column.
54388      * @param {Number} col The column index
54389      * @param {Object} editor The editor object
54390      */
54391     setEditor : function(col, editor){
54392         this.config[col].editor = editor;
54393     }
54394 });
54395
54396 Roo.grid.ColumnModel.defaultRenderer = function(value){
54397         if(typeof value == "string" && value.length < 1){
54398             return "&#160;";
54399         }
54400         return value;
54401 };
54402
54403 // Alias for backwards compatibility
54404 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54405 /*
54406  * Based on:
54407  * Ext JS Library 1.1.1
54408  * Copyright(c) 2006-2007, Ext JS, LLC.
54409  *
54410  * Originally Released Under LGPL - original licence link has changed is not relivant.
54411  *
54412  * Fork - LGPL
54413  * <script type="text/javascript">
54414  */
54415
54416 /**
54417  * @class Roo.grid.AbstractSelectionModel
54418  * @extends Roo.util.Observable
54419  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54420  * implemented by descendant classes.  This class should not be directly instantiated.
54421  * @constructor
54422  */
54423 Roo.grid.AbstractSelectionModel = function(){
54424     this.locked = false;
54425     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54426 };
54427
54428 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54429     /** @ignore Called by the grid automatically. Do not call directly. */
54430     init : function(grid){
54431         this.grid = grid;
54432         this.initEvents();
54433     },
54434
54435     /**
54436      * Locks the selections.
54437      */
54438     lock : function(){
54439         this.locked = true;
54440     },
54441
54442     /**
54443      * Unlocks the selections.
54444      */
54445     unlock : function(){
54446         this.locked = false;
54447     },
54448
54449     /**
54450      * Returns true if the selections are locked.
54451      * @return {Boolean}
54452      */
54453     isLocked : function(){
54454         return this.locked;
54455     }
54456 });/*
54457  * Based on:
54458  * Ext JS Library 1.1.1
54459  * Copyright(c) 2006-2007, Ext JS, LLC.
54460  *
54461  * Originally Released Under LGPL - original licence link has changed is not relivant.
54462  *
54463  * Fork - LGPL
54464  * <script type="text/javascript">
54465  */
54466 /**
54467  * @extends Roo.grid.AbstractSelectionModel
54468  * @class Roo.grid.RowSelectionModel
54469  * The default SelectionModel used by {@link Roo.grid.Grid}.
54470  * It supports multiple selections and keyboard selection/navigation. 
54471  * @constructor
54472  * @param {Object} config
54473  */
54474 Roo.grid.RowSelectionModel = function(config){
54475     Roo.apply(this, config);
54476     this.selections = new Roo.util.MixedCollection(false, function(o){
54477         return o.id;
54478     });
54479
54480     this.last = false;
54481     this.lastActive = false;
54482
54483     this.addEvents({
54484         /**
54485              * @event selectionchange
54486              * Fires when the selection changes
54487              * @param {SelectionModel} this
54488              */
54489             "selectionchange" : true,
54490         /**
54491              * @event afterselectionchange
54492              * Fires after the selection changes (eg. by key press or clicking)
54493              * @param {SelectionModel} this
54494              */
54495             "afterselectionchange" : true,
54496         /**
54497              * @event beforerowselect
54498              * Fires when a row is selected being selected, return false to cancel.
54499              * @param {SelectionModel} this
54500              * @param {Number} rowIndex The selected index
54501              * @param {Boolean} keepExisting False if other selections will be cleared
54502              */
54503             "beforerowselect" : true,
54504         /**
54505              * @event rowselect
54506              * Fires when a row is selected.
54507              * @param {SelectionModel} this
54508              * @param {Number} rowIndex The selected index
54509              * @param {Roo.data.Record} r The record
54510              */
54511             "rowselect" : true,
54512         /**
54513              * @event rowdeselect
54514              * Fires when a row is deselected.
54515              * @param {SelectionModel} this
54516              * @param {Number} rowIndex The selected index
54517              */
54518         "rowdeselect" : true
54519     });
54520     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54521     this.locked = false;
54522 };
54523
54524 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54525     /**
54526      * @cfg {Boolean} singleSelect
54527      * True to allow selection of only one row at a time (defaults to false)
54528      */
54529     singleSelect : false,
54530
54531     // private
54532     initEvents : function(){
54533
54534         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54535             this.grid.on("mousedown", this.handleMouseDown, this);
54536         }else{ // allow click to work like normal
54537             this.grid.on("rowclick", this.handleDragableRowClick, this);
54538         }
54539
54540         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54541             "up" : function(e){
54542                 if(!e.shiftKey){
54543                     this.selectPrevious(e.shiftKey);
54544                 }else if(this.last !== false && this.lastActive !== false){
54545                     var last = this.last;
54546                     this.selectRange(this.last,  this.lastActive-1);
54547                     this.grid.getView().focusRow(this.lastActive);
54548                     if(last !== false){
54549                         this.last = last;
54550                     }
54551                 }else{
54552                     this.selectFirstRow();
54553                 }
54554                 this.fireEvent("afterselectionchange", this);
54555             },
54556             "down" : function(e){
54557                 if(!e.shiftKey){
54558                     this.selectNext(e.shiftKey);
54559                 }else if(this.last !== false && this.lastActive !== false){
54560                     var last = this.last;
54561                     this.selectRange(this.last,  this.lastActive+1);
54562                     this.grid.getView().focusRow(this.lastActive);
54563                     if(last !== false){
54564                         this.last = last;
54565                     }
54566                 }else{
54567                     this.selectFirstRow();
54568                 }
54569                 this.fireEvent("afterselectionchange", this);
54570             },
54571             scope: this
54572         });
54573
54574         var view = this.grid.view;
54575         view.on("refresh", this.onRefresh, this);
54576         view.on("rowupdated", this.onRowUpdated, this);
54577         view.on("rowremoved", this.onRemove, this);
54578     },
54579
54580     // private
54581     onRefresh : function(){
54582         var ds = this.grid.dataSource, i, v = this.grid.view;
54583         var s = this.selections;
54584         s.each(function(r){
54585             if((i = ds.indexOfId(r.id)) != -1){
54586                 v.onRowSelect(i);
54587             }else{
54588                 s.remove(r);
54589             }
54590         });
54591     },
54592
54593     // private
54594     onRemove : function(v, index, r){
54595         this.selections.remove(r);
54596     },
54597
54598     // private
54599     onRowUpdated : function(v, index, r){
54600         if(this.isSelected(r)){
54601             v.onRowSelect(index);
54602         }
54603     },
54604
54605     /**
54606      * Select records.
54607      * @param {Array} records The records to select
54608      * @param {Boolean} keepExisting (optional) True to keep existing selections
54609      */
54610     selectRecords : function(records, keepExisting){
54611         if(!keepExisting){
54612             this.clearSelections();
54613         }
54614         var ds = this.grid.dataSource;
54615         for(var i = 0, len = records.length; i < len; i++){
54616             this.selectRow(ds.indexOf(records[i]), true);
54617         }
54618     },
54619
54620     /**
54621      * Gets the number of selected rows.
54622      * @return {Number}
54623      */
54624     getCount : function(){
54625         return this.selections.length;
54626     },
54627
54628     /**
54629      * Selects the first row in the grid.
54630      */
54631     selectFirstRow : function(){
54632         this.selectRow(0);
54633     },
54634
54635     /**
54636      * Select the last row.
54637      * @param {Boolean} keepExisting (optional) True to keep existing selections
54638      */
54639     selectLastRow : function(keepExisting){
54640         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
54641     },
54642
54643     /**
54644      * Selects the row immediately following the last selected row.
54645      * @param {Boolean} keepExisting (optional) True to keep existing selections
54646      */
54647     selectNext : function(keepExisting){
54648         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
54649             this.selectRow(this.last+1, keepExisting);
54650             this.grid.getView().focusRow(this.last);
54651         }
54652     },
54653
54654     /**
54655      * Selects the row that precedes the last selected row.
54656      * @param {Boolean} keepExisting (optional) True to keep existing selections
54657      */
54658     selectPrevious : function(keepExisting){
54659         if(this.last){
54660             this.selectRow(this.last-1, keepExisting);
54661             this.grid.getView().focusRow(this.last);
54662         }
54663     },
54664
54665     /**
54666      * Returns the selected records
54667      * @return {Array} Array of selected records
54668      */
54669     getSelections : function(){
54670         return [].concat(this.selections.items);
54671     },
54672
54673     /**
54674      * Returns the first selected record.
54675      * @return {Record}
54676      */
54677     getSelected : function(){
54678         return this.selections.itemAt(0);
54679     },
54680
54681
54682     /**
54683      * Clears all selections.
54684      */
54685     clearSelections : function(fast){
54686         if(this.locked) return;
54687         if(fast !== true){
54688             var ds = this.grid.dataSource;
54689             var s = this.selections;
54690             s.each(function(r){
54691                 this.deselectRow(ds.indexOfId(r.id));
54692             }, this);
54693             s.clear();
54694         }else{
54695             this.selections.clear();
54696         }
54697         this.last = false;
54698     },
54699
54700
54701     /**
54702      * Selects all rows.
54703      */
54704     selectAll : function(){
54705         if(this.locked) return;
54706         this.selections.clear();
54707         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
54708             this.selectRow(i, true);
54709         }
54710     },
54711
54712     /**
54713      * Returns True if there is a selection.
54714      * @return {Boolean}
54715      */
54716     hasSelection : function(){
54717         return this.selections.length > 0;
54718     },
54719
54720     /**
54721      * Returns True if the specified row is selected.
54722      * @param {Number/Record} record The record or index of the record to check
54723      * @return {Boolean}
54724      */
54725     isSelected : function(index){
54726         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54727         return (r && this.selections.key(r.id) ? true : false);
54728     },
54729
54730     /**
54731      * Returns True if the specified record id is selected.
54732      * @param {String} id The id of record to check
54733      * @return {Boolean}
54734      */
54735     isIdSelected : function(id){
54736         return (this.selections.key(id) ? true : false);
54737     },
54738
54739     // private
54740     handleMouseDown : function(e, t){
54741         var view = this.grid.getView(), rowIndex;
54742         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54743             return;
54744         };
54745         if(e.shiftKey && this.last !== false){
54746             var last = this.last;
54747             this.selectRange(last, rowIndex, e.ctrlKey);
54748             this.last = last; // reset the last
54749             view.focusRow(rowIndex);
54750         }else{
54751             var isSelected = this.isSelected(rowIndex);
54752             if(e.button !== 0 && isSelected){
54753                 view.focusRow(rowIndex);
54754             }else if(e.ctrlKey && isSelected){
54755                 this.deselectRow(rowIndex);
54756             }else if(!isSelected){
54757                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54758                 view.focusRow(rowIndex);
54759             }
54760         }
54761         this.fireEvent("afterselectionchange", this);
54762     },
54763     // private
54764     handleDragableRowClick :  function(grid, rowIndex, e) 
54765     {
54766         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54767             this.selectRow(rowIndex, false);
54768             grid.view.focusRow(rowIndex);
54769              this.fireEvent("afterselectionchange", this);
54770         }
54771     },
54772     
54773     /**
54774      * Selects multiple rows.
54775      * @param {Array} rows Array of the indexes of the row to select
54776      * @param {Boolean} keepExisting (optional) True to keep existing selections
54777      */
54778     selectRows : function(rows, keepExisting){
54779         if(!keepExisting){
54780             this.clearSelections();
54781         }
54782         for(var i = 0, len = rows.length; i < len; i++){
54783             this.selectRow(rows[i], true);
54784         }
54785     },
54786
54787     /**
54788      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54789      * @param {Number} startRow The index of the first row in the range
54790      * @param {Number} endRow The index of the last row in the range
54791      * @param {Boolean} keepExisting (optional) True to retain existing selections
54792      */
54793     selectRange : function(startRow, endRow, keepExisting){
54794         if(this.locked) return;
54795         if(!keepExisting){
54796             this.clearSelections();
54797         }
54798         if(startRow <= endRow){
54799             for(var i = startRow; i <= endRow; i++){
54800                 this.selectRow(i, true);
54801             }
54802         }else{
54803             for(var i = startRow; i >= endRow; i--){
54804                 this.selectRow(i, true);
54805             }
54806         }
54807     },
54808
54809     /**
54810      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54811      * @param {Number} startRow The index of the first row in the range
54812      * @param {Number} endRow The index of the last row in the range
54813      */
54814     deselectRange : function(startRow, endRow, preventViewNotify){
54815         if(this.locked) return;
54816         for(var i = startRow; i <= endRow; i++){
54817             this.deselectRow(i, preventViewNotify);
54818         }
54819     },
54820
54821     /**
54822      * Selects a row.
54823      * @param {Number} row The index of the row to select
54824      * @param {Boolean} keepExisting (optional) True to keep existing selections
54825      */
54826     selectRow : function(index, keepExisting, preventViewNotify){
54827         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54828         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54829             if(!keepExisting || this.singleSelect){
54830                 this.clearSelections();
54831             }
54832             var r = this.grid.dataSource.getAt(index);
54833             this.selections.add(r);
54834             this.last = this.lastActive = index;
54835             if(!preventViewNotify){
54836                 this.grid.getView().onRowSelect(index);
54837             }
54838             this.fireEvent("rowselect", this, index, r);
54839             this.fireEvent("selectionchange", this);
54840         }
54841     },
54842
54843     /**
54844      * Deselects a row.
54845      * @param {Number} row The index of the row to deselect
54846      */
54847     deselectRow : function(index, preventViewNotify){
54848         if(this.locked) return;
54849         if(this.last == index){
54850             this.last = false;
54851         }
54852         if(this.lastActive == index){
54853             this.lastActive = false;
54854         }
54855         var r = this.grid.dataSource.getAt(index);
54856         this.selections.remove(r);
54857         if(!preventViewNotify){
54858             this.grid.getView().onRowDeselect(index);
54859         }
54860         this.fireEvent("rowdeselect", this, index);
54861         this.fireEvent("selectionchange", this);
54862     },
54863
54864     // private
54865     restoreLast : function(){
54866         if(this._last){
54867             this.last = this._last;
54868         }
54869     },
54870
54871     // private
54872     acceptsNav : function(row, col, cm){
54873         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54874     },
54875
54876     // private
54877     onEditorKey : function(field, e){
54878         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54879         if(k == e.TAB){
54880             e.stopEvent();
54881             ed.completeEdit();
54882             if(e.shiftKey){
54883                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54884             }else{
54885                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54886             }
54887         }else if(k == e.ENTER && !e.ctrlKey){
54888             e.stopEvent();
54889             ed.completeEdit();
54890             if(e.shiftKey){
54891                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54892             }else{
54893                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54894             }
54895         }else if(k == e.ESC){
54896             ed.cancelEdit();
54897         }
54898         if(newCell){
54899             g.startEditing(newCell[0], newCell[1]);
54900         }
54901     }
54902 });/*
54903  * Based on:
54904  * Ext JS Library 1.1.1
54905  * Copyright(c) 2006-2007, Ext JS, LLC.
54906  *
54907  * Originally Released Under LGPL - original licence link has changed is not relivant.
54908  *
54909  * Fork - LGPL
54910  * <script type="text/javascript">
54911  */
54912 /**
54913  * @class Roo.grid.CellSelectionModel
54914  * @extends Roo.grid.AbstractSelectionModel
54915  * This class provides the basic implementation for cell selection in a grid.
54916  * @constructor
54917  * @param {Object} config The object containing the configuration of this model.
54918  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54919  */
54920 Roo.grid.CellSelectionModel = function(config){
54921     Roo.apply(this, config);
54922
54923     this.selection = null;
54924
54925     this.addEvents({
54926         /**
54927              * @event beforerowselect
54928              * Fires before a cell is selected.
54929              * @param {SelectionModel} this
54930              * @param {Number} rowIndex The selected row index
54931              * @param {Number} colIndex The selected cell index
54932              */
54933             "beforecellselect" : true,
54934         /**
54935              * @event cellselect
54936              * Fires when a cell is selected.
54937              * @param {SelectionModel} this
54938              * @param {Number} rowIndex The selected row index
54939              * @param {Number} colIndex The selected cell index
54940              */
54941             "cellselect" : true,
54942         /**
54943              * @event selectionchange
54944              * Fires when the active selection changes.
54945              * @param {SelectionModel} this
54946              * @param {Object} selection null for no selection or an object (o) with two properties
54947                 <ul>
54948                 <li>o.record: the record object for the row the selection is in</li>
54949                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54950                 </ul>
54951              */
54952             "selectionchange" : true,
54953         /**
54954              * @event tabend
54955              * Fires when the tab (or enter) was pressed on the last editable cell
54956              * You can use this to trigger add new row.
54957              * @param {SelectionModel} this
54958              */
54959             "tabend" : true,
54960          /**
54961              * @event beforeeditnext
54962              * Fires before the next editable sell is made active
54963              * You can use this to skip to another cell or fire the tabend
54964              *    if you set cell to false
54965              * @param {Object} eventdata object : { cell : [ row, col ] } 
54966              */
54967             "beforeeditnext" : true
54968     });
54969     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54970 };
54971
54972 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54973     
54974     enter_is_tab: false,
54975
54976     /** @ignore */
54977     initEvents : function(){
54978         this.grid.on("mousedown", this.handleMouseDown, this);
54979         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54980         var view = this.grid.view;
54981         view.on("refresh", this.onViewChange, this);
54982         view.on("rowupdated", this.onRowUpdated, this);
54983         view.on("beforerowremoved", this.clearSelections, this);
54984         view.on("beforerowsinserted", this.clearSelections, this);
54985         if(this.grid.isEditor){
54986             this.grid.on("beforeedit", this.beforeEdit,  this);
54987         }
54988     },
54989
54990         //private
54991     beforeEdit : function(e){
54992         this.select(e.row, e.column, false, true, e.record);
54993     },
54994
54995         //private
54996     onRowUpdated : function(v, index, r){
54997         if(this.selection && this.selection.record == r){
54998             v.onCellSelect(index, this.selection.cell[1]);
54999         }
55000     },
55001
55002         //private
55003     onViewChange : function(){
55004         this.clearSelections(true);
55005     },
55006
55007         /**
55008          * Returns the currently selected cell,.
55009          * @return {Array} The selected cell (row, column) or null if none selected.
55010          */
55011     getSelectedCell : function(){
55012         return this.selection ? this.selection.cell : null;
55013     },
55014
55015     /**
55016      * Clears all selections.
55017      * @param {Boolean} true to prevent the gridview from being notified about the change.
55018      */
55019     clearSelections : function(preventNotify){
55020         var s = this.selection;
55021         if(s){
55022             if(preventNotify !== true){
55023                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55024             }
55025             this.selection = null;
55026             this.fireEvent("selectionchange", this, null);
55027         }
55028     },
55029
55030     /**
55031      * Returns true if there is a selection.
55032      * @return {Boolean}
55033      */
55034     hasSelection : function(){
55035         return this.selection ? true : false;
55036     },
55037
55038     /** @ignore */
55039     handleMouseDown : function(e, t){
55040         var v = this.grid.getView();
55041         if(this.isLocked()){
55042             return;
55043         };
55044         var row = v.findRowIndex(t);
55045         var cell = v.findCellIndex(t);
55046         if(row !== false && cell !== false){
55047             this.select(row, cell);
55048         }
55049     },
55050
55051     /**
55052      * Selects a cell.
55053      * @param {Number} rowIndex
55054      * @param {Number} collIndex
55055      */
55056     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55057         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55058             this.clearSelections();
55059             r = r || this.grid.dataSource.getAt(rowIndex);
55060             this.selection = {
55061                 record : r,
55062                 cell : [rowIndex, colIndex]
55063             };
55064             if(!preventViewNotify){
55065                 var v = this.grid.getView();
55066                 v.onCellSelect(rowIndex, colIndex);
55067                 if(preventFocus !== true){
55068                     v.focusCell(rowIndex, colIndex);
55069                 }
55070             }
55071             this.fireEvent("cellselect", this, rowIndex, colIndex);
55072             this.fireEvent("selectionchange", this, this.selection);
55073         }
55074     },
55075
55076         //private
55077     isSelectable : function(rowIndex, colIndex, cm){
55078         return !cm.isHidden(colIndex);
55079     },
55080
55081     /** @ignore */
55082     handleKeyDown : function(e){
55083         //Roo.log('Cell Sel Model handleKeyDown');
55084         if(!e.isNavKeyPress()){
55085             return;
55086         }
55087         var g = this.grid, s = this.selection;
55088         if(!s){
55089             e.stopEvent();
55090             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55091             if(cell){
55092                 this.select(cell[0], cell[1]);
55093             }
55094             return;
55095         }
55096         var sm = this;
55097         var walk = function(row, col, step){
55098             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55099         };
55100         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55101         var newCell;
55102
55103       
55104
55105         switch(k){
55106             case e.TAB:
55107                 // handled by onEditorKey
55108                 if (g.isEditor && g.editing) {
55109                     return;
55110                 }
55111                 if(e.shiftKey) {
55112                     newCell = walk(r, c-1, -1);
55113                 } else {
55114                     newCell = walk(r, c+1, 1);
55115                 }
55116                 break;
55117             
55118             case e.DOWN:
55119                newCell = walk(r+1, c, 1);
55120                 break;
55121             
55122             case e.UP:
55123                 newCell = walk(r-1, c, -1);
55124                 break;
55125             
55126             case e.RIGHT:
55127                 newCell = walk(r, c+1, 1);
55128                 break;
55129             
55130             case e.LEFT:
55131                 newCell = walk(r, c-1, -1);
55132                 break;
55133             
55134             case e.ENTER:
55135                 
55136                 if(g.isEditor && !g.editing){
55137                    g.startEditing(r, c);
55138                    e.stopEvent();
55139                    return;
55140                 }
55141                 
55142                 
55143              break;
55144         };
55145         if(newCell){
55146             this.select(newCell[0], newCell[1]);
55147             e.stopEvent();
55148             
55149         }
55150     },
55151
55152     acceptsNav : function(row, col, cm){
55153         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55154     },
55155     /**
55156      * Selects a cell.
55157      * @param {Number} field (not used) - as it's normally used as a listener
55158      * @param {Number} e - event - fake it by using
55159      *
55160      * var e = Roo.EventObjectImpl.prototype;
55161      * e.keyCode = e.TAB
55162      *
55163      * 
55164      */
55165     onEditorKey : function(field, e){
55166         
55167         var k = e.getKey(),
55168             newCell,
55169             g = this.grid,
55170             ed = g.activeEditor,
55171             forward = false;
55172         ///Roo.log('onEditorKey' + k);
55173         
55174         
55175         if (this.enter_is_tab && k == e.ENTER) {
55176             k = e.TAB;
55177         }
55178         
55179         if(k == e.TAB){
55180             if(e.shiftKey){
55181                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55182             }else{
55183                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55184                 forward = true;
55185             }
55186             
55187             e.stopEvent();
55188             
55189         } else if(k == e.ENTER &&  !e.ctrlKey){
55190             ed.completeEdit();
55191             e.stopEvent();
55192             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55193         
55194                 } else if(k == e.ESC){
55195             ed.cancelEdit();
55196         }
55197                 
55198         if (newCell) {
55199             var ecall = { cell : newCell, forward : forward };
55200             this.fireEvent('beforeeditnext', ecall );
55201             newCell = ecall.cell;
55202                         forward = ecall.forward;
55203         }
55204                 
55205         if(newCell){
55206             //Roo.log('next cell after edit');
55207             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55208         } else if (forward) {
55209             // tabbed past last
55210             this.fireEvent.defer(100, this, ['tabend',this]);
55211         }
55212     }
55213 });/*
55214  * Based on:
55215  * Ext JS Library 1.1.1
55216  * Copyright(c) 2006-2007, Ext JS, LLC.
55217  *
55218  * Originally Released Under LGPL - original licence link has changed is not relivant.
55219  *
55220  * Fork - LGPL
55221  * <script type="text/javascript">
55222  */
55223  
55224 /**
55225  * @class Roo.grid.EditorGrid
55226  * @extends Roo.grid.Grid
55227  * Class for creating and editable grid.
55228  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55229  * The container MUST have some type of size defined for the grid to fill. The container will be 
55230  * automatically set to position relative if it isn't already.
55231  * @param {Object} dataSource The data model to bind to
55232  * @param {Object} colModel The column model with info about this grid's columns
55233  */
55234 Roo.grid.EditorGrid = function(container, config){
55235     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55236     this.getGridEl().addClass("xedit-grid");
55237
55238     if(!this.selModel){
55239         this.selModel = new Roo.grid.CellSelectionModel();
55240     }
55241
55242     this.activeEditor = null;
55243
55244         this.addEvents({
55245             /**
55246              * @event beforeedit
55247              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55248              * <ul style="padding:5px;padding-left:16px;">
55249              * <li>grid - This grid</li>
55250              * <li>record - The record being edited</li>
55251              * <li>field - The field name being edited</li>
55252              * <li>value - The value for the field being edited.</li>
55253              * <li>row - The grid row index</li>
55254              * <li>column - The grid column index</li>
55255              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55256              * </ul>
55257              * @param {Object} e An edit event (see above for description)
55258              */
55259             "beforeedit" : true,
55260             /**
55261              * @event afteredit
55262              * Fires after a cell is edited. <br />
55263              * <ul style="padding:5px;padding-left:16px;">
55264              * <li>grid - This grid</li>
55265              * <li>record - The record being edited</li>
55266              * <li>field - The field name being edited</li>
55267              * <li>value - The value being set</li>
55268              * <li>originalValue - The original value for the field, before the edit.</li>
55269              * <li>row - The grid row index</li>
55270              * <li>column - The grid column index</li>
55271              * </ul>
55272              * @param {Object} e An edit event (see above for description)
55273              */
55274             "afteredit" : true,
55275             /**
55276              * @event validateedit
55277              * Fires after a cell is edited, but before the value is set in the record. 
55278          * You can use this to modify the value being set in the field, Return false
55279              * to cancel the change. The edit event object has the following properties <br />
55280              * <ul style="padding:5px;padding-left:16px;">
55281          * <li>editor - This editor</li>
55282              * <li>grid - This grid</li>
55283              * <li>record - The record being edited</li>
55284              * <li>field - The field name being edited</li>
55285              * <li>value - The value being set</li>
55286              * <li>originalValue - The original value for the field, before the edit.</li>
55287              * <li>row - The grid row index</li>
55288              * <li>column - The grid column index</li>
55289              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55290              * </ul>
55291              * @param {Object} e An edit event (see above for description)
55292              */
55293             "validateedit" : true
55294         });
55295     this.on("bodyscroll", this.stopEditing,  this);
55296     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55297 };
55298
55299 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55300     /**
55301      * @cfg {Number} clicksToEdit
55302      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55303      */
55304     clicksToEdit: 2,
55305
55306     // private
55307     isEditor : true,
55308     // private
55309     trackMouseOver: false, // causes very odd FF errors
55310
55311     onCellDblClick : function(g, row, col){
55312         this.startEditing(row, col);
55313     },
55314
55315     onEditComplete : function(ed, value, startValue){
55316         this.editing = false;
55317         this.activeEditor = null;
55318         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55319         var r = ed.record;
55320         var field = this.colModel.getDataIndex(ed.col);
55321         var e = {
55322             grid: this,
55323             record: r,
55324             field: field,
55325             originalValue: startValue,
55326             value: value,
55327             row: ed.row,
55328             column: ed.col,
55329             cancel:false,
55330             editor: ed
55331         };
55332         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55333         cell.show();
55334           
55335         if(String(value) !== String(startValue)){
55336             
55337             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55338                 r.set(field, e.value);
55339                 // if we are dealing with a combo box..
55340                 // then we also set the 'name' colum to be the displayField
55341                 if (ed.field.displayField && ed.field.name) {
55342                     r.set(ed.field.name, ed.field.el.dom.value);
55343                 }
55344                 
55345                 delete e.cancel; //?? why!!!
55346                 this.fireEvent("afteredit", e);
55347             }
55348         } else {
55349             this.fireEvent("afteredit", e); // always fire it!
55350         }
55351         this.view.focusCell(ed.row, ed.col);
55352     },
55353
55354     /**
55355      * Starts editing the specified for the specified row/column
55356      * @param {Number} rowIndex
55357      * @param {Number} colIndex
55358      */
55359     startEditing : function(row, col){
55360         this.stopEditing();
55361         if(this.colModel.isCellEditable(col, row)){
55362             this.view.ensureVisible(row, col, true);
55363           
55364             var r = this.dataSource.getAt(row);
55365             var field = this.colModel.getDataIndex(col);
55366             var cell = Roo.get(this.view.getCell(row,col));
55367             var e = {
55368                 grid: this,
55369                 record: r,
55370                 field: field,
55371                 value: r.data[field],
55372                 row: row,
55373                 column: col,
55374                 cancel:false 
55375             };
55376             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55377                 this.editing = true;
55378                 var ed = this.colModel.getCellEditor(col, row);
55379                 
55380                 if (!ed) {
55381                     return;
55382                 }
55383                 if(!ed.rendered){
55384                     ed.render(ed.parentEl || document.body);
55385                 }
55386                 ed.field.reset();
55387                
55388                 cell.hide();
55389                 
55390                 (function(){ // complex but required for focus issues in safari, ie and opera
55391                     ed.row = row;
55392                     ed.col = col;
55393                     ed.record = r;
55394                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55395                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55396                     this.activeEditor = ed;
55397                     var v = r.data[field];
55398                     ed.startEdit(this.view.getCell(row, col), v);
55399                     // combo's with 'displayField and name set
55400                     if (ed.field.displayField && ed.field.name) {
55401                         ed.field.el.dom.value = r.data[ed.field.name];
55402                     }
55403                     
55404                     
55405                 }).defer(50, this);
55406             }
55407         }
55408     },
55409         
55410     /**
55411      * Stops any active editing
55412      */
55413     stopEditing : function(){
55414         if(this.activeEditor){
55415             this.activeEditor.completeEdit();
55416         }
55417         this.activeEditor = null;
55418     },
55419         
55420          /**
55421      * Called to get grid's drag proxy text, by default returns this.ddText.
55422      * @return {String}
55423      */
55424     getDragDropText : function(){
55425         var count = this.selModel.getSelectedCell() ? 1 : 0;
55426         return String.format(this.ddText, count, count == 1 ? '' : 's');
55427     }
55428         
55429 });/*
55430  * Based on:
55431  * Ext JS Library 1.1.1
55432  * Copyright(c) 2006-2007, Ext JS, LLC.
55433  *
55434  * Originally Released Under LGPL - original licence link has changed is not relivant.
55435  *
55436  * Fork - LGPL
55437  * <script type="text/javascript">
55438  */
55439
55440 // private - not really -- you end up using it !
55441 // This is a support class used internally by the Grid components
55442
55443 /**
55444  * @class Roo.grid.GridEditor
55445  * @extends Roo.Editor
55446  * Class for creating and editable grid elements.
55447  * @param {Object} config any settings (must include field)
55448  */
55449 Roo.grid.GridEditor = function(field, config){
55450     if (!config && field.field) {
55451         config = field;
55452         field = Roo.factory(config.field, Roo.form);
55453     }
55454     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55455     field.monitorTab = false;
55456 };
55457
55458 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55459     
55460     /**
55461      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55462      */
55463     
55464     alignment: "tl-tl",
55465     autoSize: "width",
55466     hideEl : false,
55467     cls: "x-small-editor x-grid-editor",
55468     shim:false,
55469     shadow:"frame"
55470 });/*
55471  * Based on:
55472  * Ext JS Library 1.1.1
55473  * Copyright(c) 2006-2007, Ext JS, LLC.
55474  *
55475  * Originally Released Under LGPL - original licence link has changed is not relivant.
55476  *
55477  * Fork - LGPL
55478  * <script type="text/javascript">
55479  */
55480   
55481
55482   
55483 Roo.grid.PropertyRecord = Roo.data.Record.create([
55484     {name:'name',type:'string'},  'value'
55485 ]);
55486
55487
55488 Roo.grid.PropertyStore = function(grid, source){
55489     this.grid = grid;
55490     this.store = new Roo.data.Store({
55491         recordType : Roo.grid.PropertyRecord
55492     });
55493     this.store.on('update', this.onUpdate,  this);
55494     if(source){
55495         this.setSource(source);
55496     }
55497     Roo.grid.PropertyStore.superclass.constructor.call(this);
55498 };
55499
55500
55501
55502 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55503     setSource : function(o){
55504         this.source = o;
55505         this.store.removeAll();
55506         var data = [];
55507         for(var k in o){
55508             if(this.isEditableValue(o[k])){
55509                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55510             }
55511         }
55512         this.store.loadRecords({records: data}, {}, true);
55513     },
55514
55515     onUpdate : function(ds, record, type){
55516         if(type == Roo.data.Record.EDIT){
55517             var v = record.data['value'];
55518             var oldValue = record.modified['value'];
55519             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55520                 this.source[record.id] = v;
55521                 record.commit();
55522                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55523             }else{
55524                 record.reject();
55525             }
55526         }
55527     },
55528
55529     getProperty : function(row){
55530        return this.store.getAt(row);
55531     },
55532
55533     isEditableValue: function(val){
55534         if(val && val instanceof Date){
55535             return true;
55536         }else if(typeof val == 'object' || typeof val == 'function'){
55537             return false;
55538         }
55539         return true;
55540     },
55541
55542     setValue : function(prop, value){
55543         this.source[prop] = value;
55544         this.store.getById(prop).set('value', value);
55545     },
55546
55547     getSource : function(){
55548         return this.source;
55549     }
55550 });
55551
55552 Roo.grid.PropertyColumnModel = function(grid, store){
55553     this.grid = grid;
55554     var g = Roo.grid;
55555     g.PropertyColumnModel.superclass.constructor.call(this, [
55556         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55557         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55558     ]);
55559     this.store = store;
55560     this.bselect = Roo.DomHelper.append(document.body, {
55561         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55562             {tag: 'option', value: 'true', html: 'true'},
55563             {tag: 'option', value: 'false', html: 'false'}
55564         ]
55565     });
55566     Roo.id(this.bselect);
55567     var f = Roo.form;
55568     this.editors = {
55569         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55570         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55571         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55572         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55573         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55574     };
55575     this.renderCellDelegate = this.renderCell.createDelegate(this);
55576     this.renderPropDelegate = this.renderProp.createDelegate(this);
55577 };
55578
55579 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55580     
55581     
55582     nameText : 'Name',
55583     valueText : 'Value',
55584     
55585     dateFormat : 'm/j/Y',
55586     
55587     
55588     renderDate : function(dateVal){
55589         return dateVal.dateFormat(this.dateFormat);
55590     },
55591
55592     renderBool : function(bVal){
55593         return bVal ? 'true' : 'false';
55594     },
55595
55596     isCellEditable : function(colIndex, rowIndex){
55597         return colIndex == 1;
55598     },
55599
55600     getRenderer : function(col){
55601         return col == 1 ?
55602             this.renderCellDelegate : this.renderPropDelegate;
55603     },
55604
55605     renderProp : function(v){
55606         return this.getPropertyName(v);
55607     },
55608
55609     renderCell : function(val){
55610         var rv = val;
55611         if(val instanceof Date){
55612             rv = this.renderDate(val);
55613         }else if(typeof val == 'boolean'){
55614             rv = this.renderBool(val);
55615         }
55616         return Roo.util.Format.htmlEncode(rv);
55617     },
55618
55619     getPropertyName : function(name){
55620         var pn = this.grid.propertyNames;
55621         return pn && pn[name] ? pn[name] : name;
55622     },
55623
55624     getCellEditor : function(colIndex, rowIndex){
55625         var p = this.store.getProperty(rowIndex);
55626         var n = p.data['name'], val = p.data['value'];
55627         
55628         if(typeof(this.grid.customEditors[n]) == 'string'){
55629             return this.editors[this.grid.customEditors[n]];
55630         }
55631         if(typeof(this.grid.customEditors[n]) != 'undefined'){
55632             return this.grid.customEditors[n];
55633         }
55634         if(val instanceof Date){
55635             return this.editors['date'];
55636         }else if(typeof val == 'number'){
55637             return this.editors['number'];
55638         }else if(typeof val == 'boolean'){
55639             return this.editors['boolean'];
55640         }else{
55641             return this.editors['string'];
55642         }
55643     }
55644 });
55645
55646 /**
55647  * @class Roo.grid.PropertyGrid
55648  * @extends Roo.grid.EditorGrid
55649  * This class represents the  interface of a component based property grid control.
55650  * <br><br>Usage:<pre><code>
55651  var grid = new Roo.grid.PropertyGrid("my-container-id", {
55652       
55653  });
55654  // set any options
55655  grid.render();
55656  * </code></pre>
55657   
55658  * @constructor
55659  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55660  * The container MUST have some type of size defined for the grid to fill. The container will be
55661  * automatically set to position relative if it isn't already.
55662  * @param {Object} config A config object that sets properties on this grid.
55663  */
55664 Roo.grid.PropertyGrid = function(container, config){
55665     config = config || {};
55666     var store = new Roo.grid.PropertyStore(this);
55667     this.store = store;
55668     var cm = new Roo.grid.PropertyColumnModel(this, store);
55669     store.store.sort('name', 'ASC');
55670     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
55671         ds: store.store,
55672         cm: cm,
55673         enableColLock:false,
55674         enableColumnMove:false,
55675         stripeRows:false,
55676         trackMouseOver: false,
55677         clicksToEdit:1
55678     }, config));
55679     this.getGridEl().addClass('x-props-grid');
55680     this.lastEditRow = null;
55681     this.on('columnresize', this.onColumnResize, this);
55682     this.addEvents({
55683          /**
55684              * @event beforepropertychange
55685              * Fires before a property changes (return false to stop?)
55686              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55687              * @param {String} id Record Id
55688              * @param {String} newval New Value
55689          * @param {String} oldval Old Value
55690              */
55691         "beforepropertychange": true,
55692         /**
55693              * @event propertychange
55694              * Fires after a property changes
55695              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55696              * @param {String} id Record Id
55697              * @param {String} newval New Value
55698          * @param {String} oldval Old Value
55699              */
55700         "propertychange": true
55701     });
55702     this.customEditors = this.customEditors || {};
55703 };
55704 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
55705     
55706      /**
55707      * @cfg {Object} customEditors map of colnames=> custom editors.
55708      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
55709      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
55710      * false disables editing of the field.
55711          */
55712     
55713       /**
55714      * @cfg {Object} propertyNames map of property Names to their displayed value
55715          */
55716     
55717     render : function(){
55718         Roo.grid.PropertyGrid.superclass.render.call(this);
55719         this.autoSize.defer(100, this);
55720     },
55721
55722     autoSize : function(){
55723         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55724         if(this.view){
55725             this.view.fitColumns();
55726         }
55727     },
55728
55729     onColumnResize : function(){
55730         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55731         this.autoSize();
55732     },
55733     /**
55734      * Sets the data for the Grid
55735      * accepts a Key => Value object of all the elements avaiable.
55736      * @param {Object} data  to appear in grid.
55737      */
55738     setSource : function(source){
55739         this.store.setSource(source);
55740         //this.autoSize();
55741     },
55742     /**
55743      * Gets all the data from the grid.
55744      * @return {Object} data  data stored in grid
55745      */
55746     getSource : function(){
55747         return this.store.getSource();
55748     }
55749 });/*
55750  * Based on:
55751  * Ext JS Library 1.1.1
55752  * Copyright(c) 2006-2007, Ext JS, LLC.
55753  *
55754  * Originally Released Under LGPL - original licence link has changed is not relivant.
55755  *
55756  * Fork - LGPL
55757  * <script type="text/javascript">
55758  */
55759  
55760 /**
55761  * @class Roo.LoadMask
55762  * A simple utility class for generically masking elements while loading data.  If the element being masked has
55763  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
55764  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
55765  * element's UpdateManager load indicator and will be destroyed after the initial load.
55766  * @constructor
55767  * Create a new LoadMask
55768  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
55769  * @param {Object} config The config object
55770  */
55771 Roo.LoadMask = function(el, config){
55772     this.el = Roo.get(el);
55773     Roo.apply(this, config);
55774     if(this.store){
55775         this.store.on('beforeload', this.onBeforeLoad, this);
55776         this.store.on('load', this.onLoad, this);
55777         this.store.on('loadexception', this.onLoadException, this);
55778         this.removeMask = false;
55779     }else{
55780         var um = this.el.getUpdateManager();
55781         um.showLoadIndicator = false; // disable the default indicator
55782         um.on('beforeupdate', this.onBeforeLoad, this);
55783         um.on('update', this.onLoad, this);
55784         um.on('failure', this.onLoad, this);
55785         this.removeMask = true;
55786     }
55787 };
55788
55789 Roo.LoadMask.prototype = {
55790     /**
55791      * @cfg {Boolean} removeMask
55792      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
55793      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
55794      */
55795     /**
55796      * @cfg {String} msg
55797      * The text to display in a centered loading message box (defaults to 'Loading...')
55798      */
55799     msg : 'Loading...',
55800     /**
55801      * @cfg {String} msgCls
55802      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
55803      */
55804     msgCls : 'x-mask-loading',
55805
55806     /**
55807      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
55808      * @type Boolean
55809      */
55810     disabled: false,
55811
55812     /**
55813      * Disables the mask to prevent it from being displayed
55814      */
55815     disable : function(){
55816        this.disabled = true;
55817     },
55818
55819     /**
55820      * Enables the mask so that it can be displayed
55821      */
55822     enable : function(){
55823         this.disabled = false;
55824     },
55825     
55826     onLoadException : function()
55827     {
55828         Roo.log(arguments);
55829         
55830         if (typeof(arguments[3]) != 'undefined') {
55831             Roo.MessageBox.alert("Error loading",arguments[3]);
55832         } 
55833         /*
55834         try {
55835             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
55836                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
55837             }   
55838         } catch(e) {
55839             
55840         }
55841         */
55842     
55843         
55844         
55845         this.el.unmask(this.removeMask);
55846     },
55847     // private
55848     onLoad : function()
55849     {
55850         this.el.unmask(this.removeMask);
55851     },
55852
55853     // private
55854     onBeforeLoad : function(){
55855         if(!this.disabled){
55856             this.el.mask(this.msg, this.msgCls);
55857         }
55858     },
55859
55860     // private
55861     destroy : function(){
55862         if(this.store){
55863             this.store.un('beforeload', this.onBeforeLoad, this);
55864             this.store.un('load', this.onLoad, this);
55865             this.store.un('loadexception', this.onLoadException, this);
55866         }else{
55867             var um = this.el.getUpdateManager();
55868             um.un('beforeupdate', this.onBeforeLoad, this);
55869             um.un('update', this.onLoad, this);
55870             um.un('failure', this.onLoad, this);
55871         }
55872     }
55873 };/*
55874  * Based on:
55875  * Ext JS Library 1.1.1
55876  * Copyright(c) 2006-2007, Ext JS, LLC.
55877  *
55878  * Originally Released Under LGPL - original licence link has changed is not relivant.
55879  *
55880  * Fork - LGPL
55881  * <script type="text/javascript">
55882  */
55883
55884
55885 /**
55886  * @class Roo.XTemplate
55887  * @extends Roo.Template
55888  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
55889 <pre><code>
55890 var t = new Roo.XTemplate(
55891         '&lt;select name="{name}"&gt;',
55892                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
55893         '&lt;/select&gt;'
55894 );
55895  
55896 // then append, applying the master template values
55897  </code></pre>
55898  *
55899  * Supported features:
55900  *
55901  *  Tags:
55902
55903 <pre><code>
55904       {a_variable} - output encoded.
55905       {a_variable.format:("Y-m-d")} - call a method on the variable
55906       {a_variable:raw} - unencoded output
55907       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
55908       {a_variable:this.method_on_template(...)} - call a method on the template object.
55909  
55910 </code></pre>
55911  *  The tpl tag:
55912 <pre><code>
55913         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
55914         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
55915         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
55916         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
55917   
55918         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
55919         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
55920 </code></pre>
55921  *      
55922  */
55923 Roo.XTemplate = function()
55924 {
55925     Roo.XTemplate.superclass.constructor.apply(this, arguments);
55926     if (this.html) {
55927         this.compile();
55928     }
55929 };
55930
55931
55932 Roo.extend(Roo.XTemplate, Roo.Template, {
55933
55934     /**
55935      * The various sub templates
55936      */
55937     tpls : false,
55938     /**
55939      *
55940      * basic tag replacing syntax
55941      * WORD:WORD()
55942      *
55943      * // you can fake an object call by doing this
55944      *  x.t:(test,tesT) 
55945      * 
55946      */
55947     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
55948
55949     /**
55950      * compile the template
55951      *
55952      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
55953      *
55954      */
55955     compile: function()
55956     {
55957         var s = this.html;
55958      
55959         s = ['<tpl>', s, '</tpl>'].join('');
55960     
55961         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
55962             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
55963             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
55964             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
55965             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
55966             m,
55967             id     = 0,
55968             tpls   = [];
55969     
55970         while(true == !!(m = s.match(re))){
55971             var forMatch   = m[0].match(nameRe),
55972                 ifMatch   = m[0].match(ifRe),
55973                 execMatch   = m[0].match(execRe),
55974                 namedMatch   = m[0].match(namedRe),
55975                 
55976                 exp  = null, 
55977                 fn   = null,
55978                 exec = null,
55979                 name = forMatch && forMatch[1] ? forMatch[1] : '';
55980                 
55981             if (ifMatch) {
55982                 // if - puts fn into test..
55983                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
55984                 if(exp){
55985                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
55986                 }
55987             }
55988             
55989             if (execMatch) {
55990                 // exec - calls a function... returns empty if true is  returned.
55991                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
55992                 if(exp){
55993                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
55994                 }
55995             }
55996             
55997             
55998             if (name) {
55999                 // for = 
56000                 switch(name){
56001                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
56002                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
56003                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
56004                 }
56005             }
56006             var uid = namedMatch ? namedMatch[1] : id;
56007             
56008             
56009             tpls.push({
56010                 id:     namedMatch ? namedMatch[1] : id,
56011                 target: name,
56012                 exec:   exec,
56013                 test:   fn,
56014                 body:   m[1] || ''
56015             });
56016             if (namedMatch) {
56017                 s = s.replace(m[0], '');
56018             } else { 
56019                 s = s.replace(m[0], '{xtpl'+ id + '}');
56020             }
56021             ++id;
56022         }
56023         this.tpls = [];
56024         for(var i = tpls.length-1; i >= 0; --i){
56025             this.compileTpl(tpls[i]);
56026             this.tpls[tpls[i].id] = tpls[i];
56027         }
56028         this.master = tpls[tpls.length-1];
56029         return this;
56030     },
56031     /**
56032      * same as applyTemplate, except it's done to one of the subTemplates
56033      * when using named templates, you can do:
56034      *
56035      * var str = pl.applySubTemplate('your-name', values);
56036      *
56037      * 
56038      * @param {Number} id of the template
56039      * @param {Object} values to apply to template
56040      * @param {Object} parent (normaly the instance of this object)
56041      */
56042     applySubTemplate : function(id, values, parent)
56043     {
56044         
56045         
56046         var t = this.tpls[id];
56047         
56048         
56049         try { 
56050             if(t.test && !t.test.call(this, values, parent)){
56051                 return '';
56052             }
56053         } catch(e) {
56054             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
56055             Roo.log(e.toString());
56056             Roo.log(t.test);
56057             return ''
56058         }
56059         try { 
56060             
56061             if(t.exec && t.exec.call(this, values, parent)){
56062                 return '';
56063             }
56064         } catch(e) {
56065             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
56066             Roo.log(e.toString());
56067             Roo.log(t.exec);
56068             return ''
56069         }
56070         try {
56071             var vs = t.target ? t.target.call(this, values, parent) : values;
56072             parent = t.target ? values : parent;
56073             if(t.target && vs instanceof Array){
56074                 var buf = [];
56075                 for(var i = 0, len = vs.length; i < len; i++){
56076                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
56077                 }
56078                 return buf.join('');
56079             }
56080             return t.compiled.call(this, vs, parent);
56081         } catch (e) {
56082             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
56083             Roo.log(e.toString());
56084             Roo.log(t.compiled);
56085             return '';
56086         }
56087     },
56088
56089     compileTpl : function(tpl)
56090     {
56091         var fm = Roo.util.Format;
56092         var useF = this.disableFormats !== true;
56093         var sep = Roo.isGecko ? "+" : ",";
56094         var undef = function(str) {
56095             Roo.log("Property not found :"  + str);
56096             return '';
56097         };
56098         
56099         var fn = function(m, name, format, args)
56100         {
56101             //Roo.log(arguments);
56102             args = args ? args.replace(/\\'/g,"'") : args;
56103             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
56104             if (typeof(format) == 'undefined') {
56105                 format= 'htmlEncode';
56106             }
56107             if (format == 'raw' ) {
56108                 format = false;
56109             }
56110             
56111             if(name.substr(0, 4) == 'xtpl'){
56112                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
56113             }
56114             
56115             // build an array of options to determine if value is undefined..
56116             
56117             // basically get 'xxxx.yyyy' then do
56118             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
56119             //    (function () { Roo.log("Property not found"); return ''; })() :
56120             //    ......
56121             
56122             var udef_ar = [];
56123             var lookfor = '';
56124             Roo.each(name.split('.'), function(st) {
56125                 lookfor += (lookfor.length ? '.': '') + st;
56126                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
56127             });
56128             
56129             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
56130             
56131             
56132             if(format && useF){
56133                 
56134                 args = args ? ',' + args : "";
56135                  
56136                 if(format.substr(0, 5) != "this."){
56137                     format = "fm." + format + '(';
56138                 }else{
56139                     format = 'this.call("'+ format.substr(5) + '", ';
56140                     args = ", values";
56141                 }
56142                 
56143                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
56144             }
56145              
56146             if (args.length) {
56147                 // called with xxyx.yuu:(test,test)
56148                 // change to ()
56149                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
56150             }
56151             // raw.. - :raw modifier..
56152             return "'"+ sep + udef_st  + name + ")"+sep+"'";
56153             
56154         };
56155         var body;
56156         // branched to use + in gecko and [].join() in others
56157         if(Roo.isGecko){
56158             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
56159                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
56160                     "';};};";
56161         }else{
56162             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
56163             body.push(tpl.body.replace(/(\r\n|\n)/g,
56164                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
56165             body.push("'].join('');};};");
56166             body = body.join('');
56167         }
56168         
56169         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
56170        
56171         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
56172         eval(body);
56173         
56174         return this;
56175     },
56176
56177     applyTemplate : function(values){
56178         return this.master.compiled.call(this, values, {});
56179         //var s = this.subs;
56180     },
56181
56182     apply : function(){
56183         return this.applyTemplate.apply(this, arguments);
56184     }
56185
56186  });
56187
56188 Roo.XTemplate.from = function(el){
56189     el = Roo.getDom(el);
56190     return new Roo.XTemplate(el.value || el.innerHTML);
56191 };