buildSDK/dependancy_bootstrap.txt
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4848 <p>
4849 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4850
4851 <p>
4852 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String} name The attribute name
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (!this.dom.hasAttribute(name)) {
9582                 return undefined;
9583             }
9584             return this.dom.getAttribute(name);
9585         }
9586         
9587         
9588         
9589     };
9590
9591     var ep = El.prototype;
9592
9593     /**
9594      * Appends an event handler (Shorthand for addListener)
9595      * @param {String}   eventName     The type of event to append
9596      * @param {Function} fn        The method the event invokes
9597      * @param {Object} scope       (optional) The scope (this object) of the fn
9598      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9599      * @method
9600      */
9601     ep.on = ep.addListener;
9602         // backwards compat
9603     ep.mon = ep.addListener;
9604
9605     /**
9606      * Removes an event handler from this element (shorthand for removeListener)
9607      * @param {String} eventName the type of event to remove
9608      * @param {Function} fn the method the event invokes
9609      * @return {Roo.Element} this
9610      * @method
9611      */
9612     ep.un = ep.removeListener;
9613
9614     /**
9615      * true to automatically adjust width and height settings for box-model issues (default to true)
9616      */
9617     ep.autoBoxAdjust = true;
9618
9619     // private
9620     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9621
9622     // private
9623     El.addUnits = function(v, defaultUnit){
9624         if(v === "" || v == "auto"){
9625             return v;
9626         }
9627         if(v === undefined){
9628             return '';
9629         }
9630         if(typeof v == "number" || !El.unitPattern.test(v)){
9631             return v + (defaultUnit || 'px');
9632         }
9633         return v;
9634     };
9635
9636     // special markup used throughout Roo when box wrapping elements
9637     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9638     /**
9639      * Visibility mode constant - Use visibility to hide element
9640      * @static
9641      * @type Number
9642      */
9643     El.VISIBILITY = 1;
9644     /**
9645      * Visibility mode constant - Use display to hide element
9646      * @static
9647      * @type Number
9648      */
9649     El.DISPLAY = 2;
9650
9651     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9652     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9653     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9654
9655
9656
9657     /**
9658      * @private
9659      */
9660     El.cache = {};
9661
9662     var docEl;
9663
9664     /**
9665      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9666      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9667      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9668      * @return {Element} The Element object
9669      * @static
9670      */
9671     El.get = function(el){
9672         var ex, elm, id;
9673         if(!el){ return null; }
9674         if(typeof el == "string"){ // element id
9675             if(!(elm = document.getElementById(el))){
9676                 return null;
9677             }
9678             if(ex = El.cache[el]){
9679                 ex.dom = elm;
9680             }else{
9681                 ex = El.cache[el] = new El(elm);
9682             }
9683             return ex;
9684         }else if(el.tagName){ // dom element
9685             if(!(id = el.id)){
9686                 id = Roo.id(el);
9687             }
9688             if(ex = El.cache[id]){
9689                 ex.dom = el;
9690             }else{
9691                 ex = El.cache[id] = new El(el);
9692             }
9693             return ex;
9694         }else if(el instanceof El){
9695             if(el != docEl){
9696                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9697                                                               // catch case where it hasn't been appended
9698                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9699             }
9700             return el;
9701         }else if(el.isComposite){
9702             return el;
9703         }else if(el instanceof Array){
9704             return El.select(el);
9705         }else if(el == document){
9706             // create a bogus element object representing the document object
9707             if(!docEl){
9708                 var f = function(){};
9709                 f.prototype = El.prototype;
9710                 docEl = new f();
9711                 docEl.dom = document;
9712             }
9713             return docEl;
9714         }
9715         return null;
9716     };
9717
9718     // private
9719     El.uncache = function(el){
9720         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9721             if(a[i]){
9722                 delete El.cache[a[i].id || a[i]];
9723             }
9724         }
9725     };
9726
9727     // private
9728     // Garbage collection - uncache elements/purge listeners on orphaned elements
9729     // so we don't hold a reference and cause the browser to retain them
9730     El.garbageCollect = function(){
9731         if(!Roo.enableGarbageCollector){
9732             clearInterval(El.collectorThread);
9733             return;
9734         }
9735         for(var eid in El.cache){
9736             var el = El.cache[eid], d = el.dom;
9737             // -------------------------------------------------------
9738             // Determining what is garbage:
9739             // -------------------------------------------------------
9740             // !d
9741             // dom node is null, definitely garbage
9742             // -------------------------------------------------------
9743             // !d.parentNode
9744             // no parentNode == direct orphan, definitely garbage
9745             // -------------------------------------------------------
9746             // !d.offsetParent && !document.getElementById(eid)
9747             // display none elements have no offsetParent so we will
9748             // also try to look it up by it's id. However, check
9749             // offsetParent first so we don't do unneeded lookups.
9750             // This enables collection of elements that are not orphans
9751             // directly, but somewhere up the line they have an orphan
9752             // parent.
9753             // -------------------------------------------------------
9754             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9755                 delete El.cache[eid];
9756                 if(d && Roo.enableListenerCollection){
9757                     E.purgeElement(d);
9758                 }
9759             }
9760         }
9761     }
9762     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9763
9764
9765     // dom is optional
9766     El.Flyweight = function(dom){
9767         this.dom = dom;
9768     };
9769     El.Flyweight.prototype = El.prototype;
9770
9771     El._flyweights = {};
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * @param {String/HTMLElement} el The dom node or id
9776      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9777      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9778      * @static
9779      * @return {Element} The shared Element object
9780      */
9781     El.fly = function(el, named){
9782         named = named || '_global';
9783         el = Roo.getDom(el);
9784         if(!el){
9785             return null;
9786         }
9787         if(!El._flyweights[named]){
9788             El._flyweights[named] = new El.Flyweight();
9789         }
9790         El._flyweights[named].dom = el;
9791         return El._flyweights[named];
9792     };
9793
9794     /**
9795      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9796      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9797      * Shorthand of {@link Roo.Element#get}
9798      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9799      * @return {Element} The Element object
9800      * @member Roo
9801      * @method get
9802      */
9803     Roo.get = El.get;
9804     /**
9805      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9806      * the dom node can be overwritten by other code.
9807      * Shorthand of {@link Roo.Element#fly}
9808      * @param {String/HTMLElement} el The dom node or id
9809      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9810      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9811      * @static
9812      * @return {Element} The shared Element object
9813      * @member Roo
9814      * @method fly
9815      */
9816     Roo.fly = El.fly;
9817
9818     // speedy lookup for elements never to box adjust
9819     var noBoxAdjust = Roo.isStrict ? {
9820         select:1
9821     } : {
9822         input:1, select:1, textarea:1
9823     };
9824     if(Roo.isIE || Roo.isGecko){
9825         noBoxAdjust['button'] = 1;
9826     }
9827
9828
9829     Roo.EventManager.on(window, 'unload', function(){
9830         delete El.cache;
9831         delete El._flyweights;
9832     });
9833 })();
9834
9835
9836
9837
9838 if(Roo.DomQuery){
9839     Roo.Element.selectorFunction = Roo.DomQuery.select;
9840 }
9841
9842 Roo.Element.select = function(selector, unique, root){
9843     var els;
9844     if(typeof selector == "string"){
9845         els = Roo.Element.selectorFunction(selector, root);
9846     }else if(selector.length !== undefined){
9847         els = selector;
9848     }else{
9849         throw "Invalid selector";
9850     }
9851     if(unique === true){
9852         return new Roo.CompositeElement(els);
9853     }else{
9854         return new Roo.CompositeElementLite(els);
9855     }
9856 };
9857 /**
9858  * Selects elements based on the passed CSS selector to enable working on them as 1.
9859  * @param {String/Array} selector The CSS selector or an array of elements
9860  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9861  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9862  * @return {CompositeElementLite/CompositeElement}
9863  * @member Roo
9864  * @method select
9865  */
9866 Roo.select = Roo.Element.select;
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881 /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893
9894 //Notifies Element that fx methods are available
9895 Roo.enableFx = true;
9896
9897 /**
9898  * @class Roo.Fx
9899  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9900  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9901  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9902  * Element effects to work.</p><br/>
9903  *
9904  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9905  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9906  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9907  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9908  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9909  * expected results and should be done with care.</p><br/>
9910  *
9911  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9912  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9913 <pre>
9914 Value  Description
9915 -----  -----------------------------
9916 tl     The top left corner
9917 t      The center of the top edge
9918 tr     The top right corner
9919 l      The center of the left edge
9920 r      The center of the right edge
9921 bl     The bottom left corner
9922 b      The center of the bottom edge
9923 br     The bottom right corner
9924 </pre>
9925  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9926  * below are common options that can be passed to any Fx method.</b>
9927  * @cfg {Function} callback A function called when the effect is finished
9928  * @cfg {Object} scope The scope of the effect function
9929  * @cfg {String} easing A valid Easing value for the effect
9930  * @cfg {String} afterCls A css class to apply after the effect
9931  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9932  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9933  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9934  * effects that end with the element being visually hidden, ignored otherwise)
9935  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9936  * a function which returns such a specification that will be applied to the Element after the effect finishes
9937  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9938  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9939  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9940  */
9941 Roo.Fx = {
9942         /**
9943          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9944          * origin for the slide effect.  This function automatically handles wrapping the element with
9945          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element in from the top
9949 el.slideIn();
9950
9951 // custom: slide the element in from the right with a 2-second duration
9952 el.slideIn('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideIn('t', {
9956     easing: 'easeOut',
9957     duration: .5
9958 });
9959 </code></pre>
9960          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9961          * @param {Object} options (optional) Object literal with any of the Fx config options
9962          * @return {Roo.Element} The Element
9963          */
9964     slideIn : function(anchor, o){
9965         var el = this.getFxEl();
9966         o = o || {};
9967
9968         el.queueFx(o, function(){
9969
9970             anchor = anchor || "t";
9971
9972             // fix display to visibility
9973             this.fixDisplay();
9974
9975             // restore values after effect
9976             var r = this.getFxRestore();
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "hidden");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             // clear out temp styles after slide and unwrap
9989             var after = function(){
9990                 el.fxUnwrap(wrap, r.pos, o);
9991                 st.width = r.width;
9992                 st.height = r.height;
9993                 el.afterFx(o);
9994             };
9995             // time to calc the positions
9996             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9997
9998             switch(anchor.toLowerCase()){
9999                 case "t":
10000                     wrap.setSize(b.width, 0);
10001                     st.left = st.bottom = "0";
10002                     a = {height: bh};
10003                 break;
10004                 case "l":
10005                     wrap.setSize(0, b.height);
10006                     st.right = st.top = "0";
10007                     a = {width: bw};
10008                 break;
10009                 case "r":
10010                     wrap.setSize(0, b.height);
10011                     wrap.setX(b.right);
10012                     st.left = st.top = "0";
10013                     a = {width: bw, points: pt};
10014                 break;
10015                 case "b":
10016                     wrap.setSize(b.width, 0);
10017                     wrap.setY(b.bottom);
10018                     st.left = st.top = "0";
10019                     a = {height: bh, points: pt};
10020                 break;
10021                 case "tl":
10022                     wrap.setSize(0, 0);
10023                     st.right = st.bottom = "0";
10024                     a = {width: bw, height: bh};
10025                 break;
10026                 case "bl":
10027                     wrap.setSize(0, 0);
10028                     wrap.setY(b.y+b.height);
10029                     st.right = st.top = "0";
10030                     a = {width: bw, height: bh, points: pt};
10031                 break;
10032                 case "br":
10033                     wrap.setSize(0, 0);
10034                     wrap.setXY([b.right, b.bottom]);
10035                     st.left = st.top = "0";
10036                     a = {width: bw, height: bh, points: pt};
10037                 break;
10038                 case "tr":
10039                     wrap.setSize(0, 0);
10040                     wrap.setX(b.x+b.width);
10041                     st.left = st.bottom = "0";
10042                     a = {width: bw, height: bh, points: pt};
10043                 break;
10044             }
10045             this.dom.style.visibility = "visible";
10046             wrap.show();
10047
10048             arguments.callee.anim = wrap.fxanim(a,
10049                 o,
10050                 'motion',
10051                 .5,
10052                 'easeOut', after);
10053         });
10054         return this;
10055     },
10056     
10057         /**
10058          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10059          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10060          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10061          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10062          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10063          * Usage:
10064          *<pre><code>
10065 // default: slide the element out to the top
10066 el.slideOut();
10067
10068 // custom: slide the element out to the right with a 2-second duration
10069 el.slideOut('r', { duration: 2 });
10070
10071 // common config options shown with default values
10072 el.slideOut('t', {
10073     easing: 'easeOut',
10074     duration: .5,
10075     remove: false,
10076     useDisplay: false
10077 });
10078 </code></pre>
10079          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10080          * @param {Object} options (optional) Object literal with any of the Fx config options
10081          * @return {Roo.Element} The Element
10082          */
10083     slideOut : function(anchor, o){
10084         var el = this.getFxEl();
10085         o = o || {};
10086
10087         el.queueFx(o, function(){
10088
10089             anchor = anchor || "t";
10090
10091             // restore values after effect
10092             var r = this.getFxRestore();
10093             
10094             var b = this.getBox();
10095             // fixed size for slide
10096             this.setSize(b);
10097
10098             // wrap if needed
10099             var wrap = this.fxWrap(r.pos, o, "visible");
10100
10101             var st = this.dom.style;
10102             st.visibility = "visible";
10103             st.position = "absolute";
10104
10105             wrap.setSize(b);
10106
10107             var after = function(){
10108                 if(o.useDisplay){
10109                     el.setDisplayed(false);
10110                 }else{
10111                     el.hide();
10112                 }
10113
10114                 el.fxUnwrap(wrap, r.pos, o);
10115
10116                 st.width = r.width;
10117                 st.height = r.height;
10118
10119                 el.afterFx(o);
10120             };
10121
10122             var a, zero = {to: 0};
10123             switch(anchor.toLowerCase()){
10124                 case "t":
10125                     st.left = st.bottom = "0";
10126                     a = {height: zero};
10127                 break;
10128                 case "l":
10129                     st.right = st.top = "0";
10130                     a = {width: zero};
10131                 break;
10132                 case "r":
10133                     st.left = st.top = "0";
10134                     a = {width: zero, points: {to:[b.right, b.y]}};
10135                 break;
10136                 case "b":
10137                     st.left = st.top = "0";
10138                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10139                 break;
10140                 case "tl":
10141                     st.right = st.bottom = "0";
10142                     a = {width: zero, height: zero};
10143                 break;
10144                 case "bl":
10145                     st.right = st.top = "0";
10146                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "br":
10149                     st.left = st.top = "0";
10150                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10151                 break;
10152                 case "tr":
10153                     st.left = st.bottom = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10155                 break;
10156             }
10157
10158             arguments.callee.anim = wrap.fxanim(a,
10159                 o,
10160                 'motion',
10161                 .5,
10162                 "easeOut", after);
10163         });
10164         return this;
10165     },
10166
10167         /**
10168          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10169          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10170          * The element must be removed from the DOM using the 'remove' config option if desired.
10171          * Usage:
10172          *<pre><code>
10173 // default
10174 el.puff();
10175
10176 // common config options shown with default values
10177 el.puff({
10178     easing: 'easeOut',
10179     duration: .5,
10180     remove: false,
10181     useDisplay: false
10182 });
10183 </code></pre>
10184          * @param {Object} options (optional) Object literal with any of the Fx config options
10185          * @return {Roo.Element} The Element
10186          */
10187     puff : function(o){
10188         var el = this.getFxEl();
10189         o = o || {};
10190
10191         el.queueFx(o, function(){
10192             this.clearOpacity();
10193             this.show();
10194
10195             // restore values after effect
10196             var r = this.getFxRestore();
10197             var st = this.dom.style;
10198
10199             var after = function(){
10200                 if(o.useDisplay){
10201                     el.setDisplayed(false);
10202                 }else{
10203                     el.hide();
10204                 }
10205
10206                 el.clearOpacity();
10207
10208                 el.setPositioning(r.pos);
10209                 st.width = r.width;
10210                 st.height = r.height;
10211                 st.fontSize = '';
10212                 el.afterFx(o);
10213             };
10214
10215             var width = this.getWidth();
10216             var height = this.getHeight();
10217
10218             arguments.callee.anim = this.fxanim({
10219                     width : {to: this.adjustWidth(width * 2)},
10220                     height : {to: this.adjustHeight(height * 2)},
10221                     points : {by: [-(width * .5), -(height * .5)]},
10222                     opacity : {to: 0},
10223                     fontSize: {to:200, unit: "%"}
10224                 },
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10235          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10236          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.switchOff();
10241
10242 // all config options shown with default values
10243 el.switchOff({
10244     easing: 'easeIn',
10245     duration: .3,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     switchOff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.clip();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273                 el.setPositioning(r.pos);
10274                 st.width = r.width;
10275                 st.height = r.height;
10276
10277                 el.afterFx(o);
10278             };
10279
10280             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10281                 this.clearOpacity();
10282                 (function(){
10283                     this.fxanim({
10284                         height:{to:1},
10285                         points:{by:[0, this.getHeight() * .5]}
10286                     }, o, 'motion', 0.3, 'easeIn', after);
10287                 }).defer(100, this);
10288             });
10289         });
10290         return this;
10291     },
10292
10293     /**
10294      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10295      * changed using the "attr" config option) and then fading back to the original color. If no original
10296      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10297      * Usage:
10298 <pre><code>
10299 // default: highlight background to yellow
10300 el.highlight();
10301
10302 // custom: highlight foreground text to blue for 2 seconds
10303 el.highlight("0000ff", { attr: 'color', duration: 2 });
10304
10305 // common config options shown with default values
10306 el.highlight("ffff9c", {
10307     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10308     endColor: (current color) or "ffffff",
10309     easing: 'easeIn',
10310     duration: 1
10311 });
10312 </code></pre>
10313      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10314      * @param {Object} options (optional) Object literal with any of the Fx config options
10315      * @return {Roo.Element} The Element
10316      */ 
10317     highlight : function(color, o){
10318         var el = this.getFxEl();
10319         o = o || {};
10320
10321         el.queueFx(o, function(){
10322             color = color || "ffff9c";
10323             attr = o.attr || "backgroundColor";
10324
10325             this.clearOpacity();
10326             this.show();
10327
10328             var origColor = this.getColor(attr);
10329             var restoreColor = this.dom.style[attr];
10330             endColor = (o.endColor || origColor) || "ffffff";
10331
10332             var after = function(){
10333                 el.dom.style[attr] = restoreColor;
10334                 el.afterFx(o);
10335             };
10336
10337             var a = {};
10338             a[attr] = {from: color, to: endColor};
10339             arguments.callee.anim = this.fxanim(a,
10340                 o,
10341                 'color',
10342                 1,
10343                 'easeIn', after);
10344         });
10345         return this;
10346     },
10347
10348    /**
10349     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10350     * Usage:
10351 <pre><code>
10352 // default: a single light blue ripple
10353 el.frame();
10354
10355 // custom: 3 red ripples lasting 3 seconds total
10356 el.frame("ff0000", 3, { duration: 3 });
10357
10358 // common config options shown with default values
10359 el.frame("C3DAF9", 1, {
10360     duration: 1 //duration of entire animation (not each individual ripple)
10361     // Note: Easing is not configurable and will be ignored if included
10362 });
10363 </code></pre>
10364     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10365     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10366     * @param {Object} options (optional) Object literal with any of the Fx config options
10367     * @return {Roo.Element} The Element
10368     */
10369     frame : function(color, count, o){
10370         var el = this.getFxEl();
10371         o = o || {};
10372
10373         el.queueFx(o, function(){
10374             color = color || "#C3DAF9";
10375             if(color.length == 6){
10376                 color = "#" + color;
10377             }
10378             count = count || 1;
10379             duration = o.duration || 1;
10380             this.show();
10381
10382             var b = this.getBox();
10383             var animFn = function(){
10384                 var proxy = this.createProxy({
10385
10386                      style:{
10387                         visbility:"hidden",
10388                         position:"absolute",
10389                         "z-index":"35000", // yee haw
10390                         border:"0px solid " + color
10391                      }
10392                   });
10393                 var scale = Roo.isBorderBox ? 2 : 1;
10394                 proxy.animate({
10395                     top:{from:b.y, to:b.y - 20},
10396                     left:{from:b.x, to:b.x - 20},
10397                     borderWidth:{from:0, to:10},
10398                     opacity:{from:1, to:0},
10399                     height:{from:b.height, to:(b.height + (20*scale))},
10400                     width:{from:b.width, to:(b.width + (20*scale))}
10401                 }, duration, function(){
10402                     proxy.remove();
10403                 });
10404                 if(--count > 0){
10405                      animFn.defer((duration/2)*1000, this);
10406                 }else{
10407                     el.afterFx(o);
10408                 }
10409             };
10410             animFn.call(this);
10411         });
10412         return this;
10413     },
10414
10415    /**
10416     * Creates a pause before any subsequent queued effects begin.  If there are
10417     * no effects queued after the pause it will have no effect.
10418     * Usage:
10419 <pre><code>
10420 el.pause(1);
10421 </code></pre>
10422     * @param {Number} seconds The length of time to pause (in seconds)
10423     * @return {Roo.Element} The Element
10424     */
10425     pause : function(seconds){
10426         var el = this.getFxEl();
10427         var o = {};
10428
10429         el.queueFx(o, function(){
10430             setTimeout(function(){
10431                 el.afterFx(o);
10432             }, seconds * 1000);
10433         });
10434         return this;
10435     },
10436
10437    /**
10438     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10439     * using the "endOpacity" config option.
10440     * Usage:
10441 <pre><code>
10442 // default: fade in from opacity 0 to 100%
10443 el.fadeIn();
10444
10445 // custom: fade in from opacity 0 to 75% over 2 seconds
10446 el.fadeIn({ endOpacity: .75, duration: 2});
10447
10448 // common config options shown with default values
10449 el.fadeIn({
10450     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10451     easing: 'easeOut',
10452     duration: .5
10453 });
10454 </code></pre>
10455     * @param {Object} options (optional) Object literal with any of the Fx config options
10456     * @return {Roo.Element} The Element
10457     */
10458     fadeIn : function(o){
10459         var el = this.getFxEl();
10460         o = o || {};
10461         el.queueFx(o, function(){
10462             this.setOpacity(0);
10463             this.fixDisplay();
10464             this.dom.style.visibility = 'visible';
10465             var to = o.endOpacity || 1;
10466             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10467                 o, null, .5, "easeOut", function(){
10468                 if(to == 1){
10469                     this.clearOpacity();
10470                 }
10471                 el.afterFx(o);
10472             });
10473         });
10474         return this;
10475     },
10476
10477    /**
10478     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10479     * using the "endOpacity" config option.
10480     * Usage:
10481 <pre><code>
10482 // default: fade out from the element's current opacity to 0
10483 el.fadeOut();
10484
10485 // custom: fade out from the element's current opacity to 25% over 2 seconds
10486 el.fadeOut({ endOpacity: .25, duration: 2});
10487
10488 // common config options shown with default values
10489 el.fadeOut({
10490     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10491     easing: 'easeOut',
10492     duration: .5
10493     remove: false,
10494     useDisplay: false
10495 });
10496 </code></pre>
10497     * @param {Object} options (optional) Object literal with any of the Fx config options
10498     * @return {Roo.Element} The Element
10499     */
10500     fadeOut : function(o){
10501         var el = this.getFxEl();
10502         o = o || {};
10503         el.queueFx(o, function(){
10504             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10505                 o, null, .5, "easeOut", function(){
10506                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10507                      this.dom.style.display = "none";
10508                 }else{
10509                      this.dom.style.visibility = "hidden";
10510                 }
10511                 this.clearOpacity();
10512                 el.afterFx(o);
10513             });
10514         });
10515         return this;
10516     },
10517
10518    /**
10519     * Animates the transition of an element's dimensions from a starting height/width
10520     * to an ending height/width.
10521     * Usage:
10522 <pre><code>
10523 // change height and width to 100x100 pixels
10524 el.scale(100, 100);
10525
10526 // common config options shown with default values.  The height and width will default to
10527 // the element's existing values if passed as null.
10528 el.scale(
10529     [element's width],
10530     [element's height], {
10531     easing: 'easeOut',
10532     duration: .35
10533 });
10534 </code></pre>
10535     * @param {Number} width  The new width (pass undefined to keep the original width)
10536     * @param {Number} height  The new height (pass undefined to keep the original height)
10537     * @param {Object} options (optional) Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     scale : function(w, h, o){
10541         this.shift(Roo.apply({}, o, {
10542             width: w,
10543             height: h
10544         }));
10545         return this;
10546     },
10547
10548    /**
10549     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10550     * Any of these properties not specified in the config object will not be changed.  This effect 
10551     * requires that at least one new dimension, position or opacity setting must be passed in on
10552     * the config object in order for the function to have any effect.
10553     * Usage:
10554 <pre><code>
10555 // slide the element horizontally to x position 200 while changing the height and opacity
10556 el.shift({ x: 200, height: 50, opacity: .8 });
10557
10558 // common config options shown with default values.
10559 el.shift({
10560     width: [element's width],
10561     height: [element's height],
10562     x: [element's x position],
10563     y: [element's y position],
10564     opacity: [element's opacity],
10565     easing: 'easeOut',
10566     duration: .35
10567 });
10568 </code></pre>
10569     * @param {Object} options  Object literal with any of the Fx config options
10570     * @return {Roo.Element} The Element
10571     */
10572     shift : function(o){
10573         var el = this.getFxEl();
10574         o = o || {};
10575         el.queueFx(o, function(){
10576             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10577             if(w !== undefined){
10578                 a.width = {to: this.adjustWidth(w)};
10579             }
10580             if(h !== undefined){
10581                 a.height = {to: this.adjustHeight(h)};
10582             }
10583             if(x !== undefined || y !== undefined){
10584                 a.points = {to: [
10585                     x !== undefined ? x : this.getX(),
10586                     y !== undefined ? y : this.getY()
10587                 ]};
10588             }
10589             if(op !== undefined){
10590                 a.opacity = {to: op};
10591             }
10592             if(o.xy !== undefined){
10593                 a.points = {to: o.xy};
10594             }
10595             arguments.callee.anim = this.fxanim(a,
10596                 o, 'motion', .35, "easeOut", function(){
10597                 el.afterFx(o);
10598             });
10599         });
10600         return this;
10601     },
10602
10603         /**
10604          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10605          * ending point of the effect.
10606          * Usage:
10607          *<pre><code>
10608 // default: slide the element downward while fading out
10609 el.ghost();
10610
10611 // custom: slide the element out to the right with a 2-second duration
10612 el.ghost('r', { duration: 2 });
10613
10614 // common config options shown with default values
10615 el.ghost('b', {
10616     easing: 'easeOut',
10617     duration: .5
10618     remove: false,
10619     useDisplay: false
10620 });
10621 </code></pre>
10622          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10623          * @param {Object} options (optional) Object literal with any of the Fx config options
10624          * @return {Roo.Element} The Element
10625          */
10626     ghost : function(anchor, o){
10627         var el = this.getFxEl();
10628         o = o || {};
10629
10630         el.queueFx(o, function(){
10631             anchor = anchor || "b";
10632
10633             // restore values after effect
10634             var r = this.getFxRestore();
10635             var w = this.getWidth(),
10636                 h = this.getHeight();
10637
10638             var st = this.dom.style;
10639
10640             var after = function(){
10641                 if(o.useDisplay){
10642                     el.setDisplayed(false);
10643                 }else{
10644                     el.hide();
10645                 }
10646
10647                 el.clearOpacity();
10648                 el.setPositioning(r.pos);
10649                 st.width = r.width;
10650                 st.height = r.height;
10651
10652                 el.afterFx(o);
10653             };
10654
10655             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10656             switch(anchor.toLowerCase()){
10657                 case "t":
10658                     pt.by = [0, -h];
10659                 break;
10660                 case "l":
10661                     pt.by = [-w, 0];
10662                 break;
10663                 case "r":
10664                     pt.by = [w, 0];
10665                 break;
10666                 case "b":
10667                     pt.by = [0, h];
10668                 break;
10669                 case "tl":
10670                     pt.by = [-w, -h];
10671                 break;
10672                 case "bl":
10673                     pt.by = [-w, h];
10674                 break;
10675                 case "br":
10676                     pt.by = [w, h];
10677                 break;
10678                 case "tr":
10679                     pt.by = [w, -h];
10680                 break;
10681             }
10682
10683             arguments.callee.anim = this.fxanim(a,
10684                 o,
10685                 'motion',
10686                 .5,
10687                 "easeOut", after);
10688         });
10689         return this;
10690     },
10691
10692         /**
10693          * Ensures that all effects queued after syncFx is called on the element are
10694          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10695          * @return {Roo.Element} The Element
10696          */
10697     syncFx : function(){
10698         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10699             block : false,
10700             concurrent : true,
10701             stopFx : false
10702         });
10703         return this;
10704     },
10705
10706         /**
10707          * Ensures that all effects queued after sequenceFx is called on the element are
10708          * run in sequence.  This is the opposite of {@link #syncFx}.
10709          * @return {Roo.Element} The Element
10710          */
10711     sequenceFx : function(){
10712         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10713             block : false,
10714             concurrent : false,
10715             stopFx : false
10716         });
10717         return this;
10718     },
10719
10720         /* @private */
10721     nextFx : function(){
10722         var ef = this.fxQueue[0];
10723         if(ef){
10724             ef.call(this);
10725         }
10726     },
10727
10728         /**
10729          * Returns true if the element has any effects actively running or queued, else returns false.
10730          * @return {Boolean} True if element has active effects, else false
10731          */
10732     hasActiveFx : function(){
10733         return this.fxQueue && this.fxQueue[0];
10734     },
10735
10736         /**
10737          * Stops any running effects and clears the element's internal effects queue if it contains
10738          * any additional effects that haven't started yet.
10739          * @return {Roo.Element} The Element
10740          */
10741     stopFx : function(){
10742         if(this.hasActiveFx()){
10743             var cur = this.fxQueue[0];
10744             if(cur && cur.anim && cur.anim.isAnimated()){
10745                 this.fxQueue = [cur]; // clear out others
10746                 cur.anim.stop(true);
10747             }
10748         }
10749         return this;
10750     },
10751
10752         /* @private */
10753     beforeFx : function(o){
10754         if(this.hasActiveFx() && !o.concurrent){
10755            if(o.stopFx){
10756                this.stopFx();
10757                return true;
10758            }
10759            return false;
10760         }
10761         return true;
10762     },
10763
10764         /**
10765          * Returns true if the element is currently blocking so that no other effect can be queued
10766          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10767          * used to ensure that an effect initiated by a user action runs to completion prior to the
10768          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10769          * @return {Boolean} True if blocking, else false
10770          */
10771     hasFxBlock : function(){
10772         var q = this.fxQueue;
10773         return q && q[0] && q[0].block;
10774     },
10775
10776         /* @private */
10777     queueFx : function(o, fn){
10778         if(!this.fxQueue){
10779             this.fxQueue = [];
10780         }
10781         if(!this.hasFxBlock()){
10782             Roo.applyIf(o, this.fxDefaults);
10783             if(!o.concurrent){
10784                 var run = this.beforeFx(o);
10785                 fn.block = o.block;
10786                 this.fxQueue.push(fn);
10787                 if(run){
10788                     this.nextFx();
10789                 }
10790             }else{
10791                 fn.call(this);
10792             }
10793         }
10794         return this;
10795     },
10796
10797         /* @private */
10798     fxWrap : function(pos, o, vis){
10799         var wrap;
10800         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10801             var wrapXY;
10802             if(o.fixPosition){
10803                 wrapXY = this.getXY();
10804             }
10805             var div = document.createElement("div");
10806             div.style.visibility = vis;
10807             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10808             wrap.setPositioning(pos);
10809             if(wrap.getStyle("position") == "static"){
10810                 wrap.position("relative");
10811             }
10812             this.clearPositioning('auto');
10813             wrap.clip();
10814             wrap.dom.appendChild(this.dom);
10815             if(wrapXY){
10816                 wrap.setXY(wrapXY);
10817             }
10818         }
10819         return wrap;
10820     },
10821
10822         /* @private */
10823     fxUnwrap : function(wrap, pos, o){
10824         this.clearPositioning();
10825         this.setPositioning(pos);
10826         if(!o.wrap){
10827             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10828             wrap.remove();
10829         }
10830     },
10831
10832         /* @private */
10833     getFxRestore : function(){
10834         var st = this.dom.style;
10835         return {pos: this.getPositioning(), width: st.width, height : st.height};
10836     },
10837
10838         /* @private */
10839     afterFx : function(o){
10840         if(o.afterStyle){
10841             this.applyStyles(o.afterStyle);
10842         }
10843         if(o.afterCls){
10844             this.addClass(o.afterCls);
10845         }
10846         if(o.remove === true){
10847             this.remove();
10848         }
10849         Roo.callback(o.callback, o.scope, [this]);
10850         if(!o.concurrent){
10851             this.fxQueue.shift();
10852             this.nextFx();
10853         }
10854     },
10855
10856         /* @private */
10857     getFxEl : function(){ // support for composite element fx
10858         return Roo.get(this.dom);
10859     },
10860
10861         /* @private */
10862     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10863         animType = animType || 'run';
10864         opt = opt || {};
10865         var anim = Roo.lib.Anim[animType](
10866             this.dom, args,
10867             (opt.duration || defaultDur) || .35,
10868             (opt.easing || defaultEase) || 'easeOut',
10869             function(){
10870                 Roo.callback(cb, this);
10871             },
10872             this
10873         );
10874         opt.anim = anim;
10875         return anim;
10876     }
10877 };
10878
10879 // backwords compat
10880 Roo.Fx.resize = Roo.Fx.scale;
10881
10882 //When included, Roo.Fx is automatically applied to Element so that all basic
10883 //effects are available directly via the Element API
10884 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10885  * Based on:
10886  * Ext JS Library 1.1.1
10887  * Copyright(c) 2006-2007, Ext JS, LLC.
10888  *
10889  * Originally Released Under LGPL - original licence link has changed is not relivant.
10890  *
10891  * Fork - LGPL
10892  * <script type="text/javascript">
10893  */
10894
10895
10896 /**
10897  * @class Roo.CompositeElement
10898  * Standard composite class. Creates a Roo.Element for every element in the collection.
10899  * <br><br>
10900  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10901  * actions will be performed on all the elements in this collection.</b>
10902  * <br><br>
10903  * All methods return <i>this</i> and can be chained.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class", true);
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class', true);
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre>
10915  */
10916 Roo.CompositeElement = function(els){
10917     this.elements = [];
10918     this.addElements(els);
10919 };
10920 Roo.CompositeElement.prototype = {
10921     isComposite: true,
10922     addElements : function(els){
10923         if(!els) return this;
10924         if(typeof els == "string"){
10925             els = Roo.Element.selectorFunction(els);
10926         }
10927         var yels = this.elements;
10928         var index = yels.length-1;
10929         for(var i = 0, len = els.length; i < len; i++) {
10930                 yels[++index] = Roo.get(els[i]);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Clears this composite and adds the elements returned by the passed selector.
10937     * @param {String/Array} els A string CSS selector, an array of elements or an element
10938     * @return {CompositeElement} this
10939     */
10940     fill : function(els){
10941         this.elements = [];
10942         this.add(els);
10943         return this;
10944     },
10945
10946     /**
10947     * Filters this composite to only elements that match the passed selector.
10948     * @param {String} selector A string CSS selector
10949     * @return {CompositeElement} this
10950     */
10951     filter : function(selector){
10952         var els = [];
10953         this.each(function(el){
10954             if(el.is(selector)){
10955                 els[els.length] = el.dom;
10956             }
10957         });
10958         this.fill(els);
10959         return this;
10960     },
10961
10962     invoke : function(fn, args){
10963         var els = this.elements;
10964         for(var i = 0, len = els.length; i < len; i++) {
10965                 Roo.Element.prototype[fn].apply(els[i], args);
10966         }
10967         return this;
10968     },
10969     /**
10970     * Adds elements to this composite.
10971     * @param {String/Array} els A string CSS selector, an array of elements or an element
10972     * @return {CompositeElement} this
10973     */
10974     add : function(els){
10975         if(typeof els == "string"){
10976             this.addElements(Roo.Element.selectorFunction(els));
10977         }else if(els.length !== undefined){
10978             this.addElements(els);
10979         }else{
10980             this.addElements([els]);
10981         }
10982         return this;
10983     },
10984     /**
10985     * Calls the passed function passing (el, this, index) for each element in this composite.
10986     * @param {Function} fn The function to call
10987     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10988     * @return {CompositeElement} this
10989     */
10990     each : function(fn, scope){
10991         var els = this.elements;
10992         for(var i = 0, len = els.length; i < len; i++){
10993             if(fn.call(scope || els[i], els[i], this, i) === false) {
10994                 break;
10995             }
10996         }
10997         return this;
10998     },
10999
11000     /**
11001      * Returns the Element object at the specified index
11002      * @param {Number} index
11003      * @return {Roo.Element}
11004      */
11005     item : function(index){
11006         return this.elements[index] || null;
11007     },
11008
11009     /**
11010      * Returns the first Element
11011      * @return {Roo.Element}
11012      */
11013     first : function(){
11014         return this.item(0);
11015     },
11016
11017     /**
11018      * Returns the last Element
11019      * @return {Roo.Element}
11020      */
11021     last : function(){
11022         return this.item(this.elements.length-1);
11023     },
11024
11025     /**
11026      * Returns the number of elements in this composite
11027      * @return Number
11028      */
11029     getCount : function(){
11030         return this.elements.length;
11031     },
11032
11033     /**
11034      * Returns true if this composite contains the passed element
11035      * @return Boolean
11036      */
11037     contains : function(el){
11038         return this.indexOf(el) !== -1;
11039     },
11040
11041     /**
11042      * Returns true if this composite contains the passed element
11043      * @return Boolean
11044      */
11045     indexOf : function(el){
11046         return this.elements.indexOf(Roo.get(el));
11047     },
11048
11049
11050     /**
11051     * Removes the specified element(s).
11052     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11053     * or an array of any of those.
11054     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11055     * @return {CompositeElement} this
11056     */
11057     removeElement : function(el, removeDom){
11058         if(el instanceof Array){
11059             for(var i = 0, len = el.length; i < len; i++){
11060                 this.removeElement(el[i]);
11061             }
11062             return this;
11063         }
11064         var index = typeof el == 'number' ? el : this.indexOf(el);
11065         if(index !== -1){
11066             if(removeDom){
11067                 var d = this.elements[index];
11068                 if(d.dom){
11069                     d.remove();
11070                 }else{
11071                     d.parentNode.removeChild(d);
11072                 }
11073             }
11074             this.elements.splice(index, 1);
11075         }
11076         return this;
11077     },
11078
11079     /**
11080     * Replaces the specified element with the passed element.
11081     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11082     * to replace.
11083     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11084     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11085     * @return {CompositeElement} this
11086     */
11087     replaceElement : function(el, replacement, domReplace){
11088         var index = typeof el == 'number' ? el : this.indexOf(el);
11089         if(index !== -1){
11090             if(domReplace){
11091                 this.elements[index].replaceWith(replacement);
11092             }else{
11093                 this.elements.splice(index, 1, Roo.get(replacement))
11094             }
11095         }
11096         return this;
11097     },
11098
11099     /**
11100      * Removes all elements.
11101      */
11102     clear : function(){
11103         this.elements = [];
11104     }
11105 };
11106 (function(){
11107     Roo.CompositeElement.createCall = function(proto, fnName){
11108         if(!proto[fnName]){
11109             proto[fnName] = function(){
11110                 return this.invoke(fnName, arguments);
11111             };
11112         }
11113     };
11114     for(var fnName in Roo.Element.prototype){
11115         if(typeof Roo.Element.prototype[fnName] == "function"){
11116             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11117         }
11118     };
11119 })();
11120 /*
11121  * Based on:
11122  * Ext JS Library 1.1.1
11123  * Copyright(c) 2006-2007, Ext JS, LLC.
11124  *
11125  * Originally Released Under LGPL - original licence link has changed is not relivant.
11126  *
11127  * Fork - LGPL
11128  * <script type="text/javascript">
11129  */
11130
11131 /**
11132  * @class Roo.CompositeElementLite
11133  * @extends Roo.CompositeElement
11134  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11135  <pre><code>
11136  var els = Roo.select("#some-el div.some-class");
11137  // or select directly from an existing element
11138  var el = Roo.get('some-el');
11139  el.select('div.some-class');
11140
11141  els.setWidth(100); // all elements become 100 width
11142  els.hide(true); // all elements fade out and hide
11143  // or
11144  els.setWidth(100).hide(true);
11145  </code></pre><br><br>
11146  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11147  * actions will be performed on all the elements in this collection.</b>
11148  */
11149 Roo.CompositeElementLite = function(els){
11150     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11151     this.el = new Roo.Element.Flyweight();
11152 };
11153 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11154     addElements : function(els){
11155         if(els){
11156             if(els instanceof Array){
11157                 this.elements = this.elements.concat(els);
11158             }else{
11159                 var yels = this.elements;
11160                 var index = yels.length-1;
11161                 for(var i = 0, len = els.length; i < len; i++) {
11162                     yels[++index] = els[i];
11163                 }
11164             }
11165         }
11166         return this;
11167     },
11168     invoke : function(fn, args){
11169         var els = this.elements;
11170         var el = this.el;
11171         for(var i = 0, len = els.length; i < len; i++) {
11172             el.dom = els[i];
11173                 Roo.Element.prototype[fn].apply(el, args);
11174         }
11175         return this;
11176     },
11177     /**
11178      * Returns a flyweight Element of the dom element object at the specified index
11179      * @param {Number} index
11180      * @return {Roo.Element}
11181      */
11182     item : function(index){
11183         if(!this.elements[index]){
11184             return null;
11185         }
11186         this.el.dom = this.elements[index];
11187         return this.el;
11188     },
11189
11190     // fixes scope with flyweight
11191     addListener : function(eventName, handler, scope, opt){
11192         var els = this.elements;
11193         for(var i = 0, len = els.length; i < len; i++) {
11194             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11195         }
11196         return this;
11197     },
11198
11199     /**
11200     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11201     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11202     * a reference to the dom node, use el.dom.</b>
11203     * @param {Function} fn The function to call
11204     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11205     * @return {CompositeElement} this
11206     */
11207     each : function(fn, scope){
11208         var els = this.elements;
11209         var el = this.el;
11210         for(var i = 0, len = els.length; i < len; i++){
11211             el.dom = els[i];
11212                 if(fn.call(scope || el, el, this, i) === false){
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     indexOf : function(el){
11220         return this.elements.indexOf(Roo.getDom(el));
11221     },
11222
11223     replaceElement : function(el, replacement, domReplace){
11224         var index = typeof el == 'number' ? el : this.indexOf(el);
11225         if(index !== -1){
11226             replacement = Roo.getDom(replacement);
11227             if(domReplace){
11228                 var d = this.elements[index];
11229                 d.parentNode.insertBefore(replacement, d);
11230                 d.parentNode.removeChild(d);
11231             }
11232             this.elements.splice(index, 1, replacement);
11233         }
11234         return this;
11235     }
11236 });
11237 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11238
11239 /*
11240  * Based on:
11241  * Ext JS Library 1.1.1
11242  * Copyright(c) 2006-2007, Ext JS, LLC.
11243  *
11244  * Originally Released Under LGPL - original licence link has changed is not relivant.
11245  *
11246  * Fork - LGPL
11247  * <script type="text/javascript">
11248  */
11249
11250  
11251
11252 /**
11253  * @class Roo.data.Connection
11254  * @extends Roo.util.Observable
11255  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11256  * either to a configured URL, or to a URL specified at request time.<br><br>
11257  * <p>
11258  * Requests made by this class are asynchronous, and will return immediately. No data from
11259  * the server will be available to the statement immediately following the {@link #request} call.
11260  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11261  * <p>
11262  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11263  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11264  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11265  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11266  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11267  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11268  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11269  * standard DOM methods.
11270  * @constructor
11271  * @param {Object} config a configuration object.
11272  */
11273 Roo.data.Connection = function(config){
11274     Roo.apply(this, config);
11275     this.addEvents({
11276         /**
11277          * @event beforerequest
11278          * Fires before a network request is made to retrieve a data object.
11279          * @param {Connection} conn This Connection object.
11280          * @param {Object} options The options config object passed to the {@link #request} method.
11281          */
11282         "beforerequest" : true,
11283         /**
11284          * @event requestcomplete
11285          * Fires if the request was successfully completed.
11286          * @param {Connection} conn This Connection object.
11287          * @param {Object} response The XHR object containing the response data.
11288          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11289          * @param {Object} options The options config object passed to the {@link #request} method.
11290          */
11291         "requestcomplete" : true,
11292         /**
11293          * @event requestexception
11294          * Fires if an error HTTP status was returned from the server.
11295          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11296          * @param {Connection} conn This Connection object.
11297          * @param {Object} response The XHR object containing the response data.
11298          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11299          * @param {Object} options The options config object passed to the {@link #request} method.
11300          */
11301         "requestexception" : true
11302     });
11303     Roo.data.Connection.superclass.constructor.call(this);
11304 };
11305
11306 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11307     /**
11308      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11309      */
11310     /**
11311      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11312      * extra parameters to each request made by this object. (defaults to undefined)
11313      */
11314     /**
11315      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11316      *  to each request made by this object. (defaults to undefined)
11317      */
11318     /**
11319      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11320      */
11321     /**
11322      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11323      */
11324     timeout : 30000,
11325     /**
11326      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11327      * @type Boolean
11328      */
11329     autoAbort:false,
11330
11331     /**
11332      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11333      * @type Boolean
11334      */
11335     disableCaching: true,
11336
11337     /**
11338      * Sends an HTTP request to a remote server.
11339      * @param {Object} options An object which may contain the following properties:<ul>
11340      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11341      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11342      * request, a url encoded string or a function to call to get either.</li>
11343      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11344      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11345      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11346      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11347      * <li>options {Object} The parameter to the request call.</li>
11348      * <li>success {Boolean} True if the request succeeded.</li>
11349      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11350      * </ul></li>
11351      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11352      * The callback is passed the following parameters:<ul>
11353      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11354      * <li>options {Object} The parameter to the request call.</li>
11355      * </ul></li>
11356      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11357      * The callback is passed the following parameters:<ul>
11358      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11359      * <li>options {Object} The parameter to the request call.</li>
11360      * </ul></li>
11361      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11362      * for the callback function. Defaults to the browser window.</li>
11363      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11364      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11365      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11366      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11367      * params for the post data. Any params will be appended to the URL.</li>
11368      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11369      * </ul>
11370      * @return {Number} transactionId
11371      */
11372     request : function(o){
11373         if(this.fireEvent("beforerequest", this, o) !== false){
11374             var p = o.params;
11375
11376             if(typeof p == "function"){
11377                 p = p.call(o.scope||window, o);
11378             }
11379             if(typeof p == "object"){
11380                 p = Roo.urlEncode(o.params);
11381             }
11382             if(this.extraParams){
11383                 var extras = Roo.urlEncode(this.extraParams);
11384                 p = p ? (p + '&' + extras) : extras;
11385             }
11386
11387             var url = o.url || this.url;
11388             if(typeof url == 'function'){
11389                 url = url.call(o.scope||window, o);
11390             }
11391
11392             if(o.form){
11393                 var form = Roo.getDom(o.form);
11394                 url = url || form.action;
11395
11396                 var enctype = form.getAttribute("enctype");
11397                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11398                     return this.doFormUpload(o, p, url);
11399                 }
11400                 var f = Roo.lib.Ajax.serializeForm(form);
11401                 p = p ? (p + '&' + f) : f;
11402             }
11403
11404             var hs = o.headers;
11405             if(this.defaultHeaders){
11406                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11407                 if(!o.headers){
11408                     o.headers = hs;
11409                 }
11410             }
11411
11412             var cb = {
11413                 success: this.handleResponse,
11414                 failure: this.handleFailure,
11415                 scope: this,
11416                 argument: {options: o},
11417                 timeout : o.timeout || this.timeout
11418             };
11419
11420             var method = o.method||this.method||(p ? "POST" : "GET");
11421
11422             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11423                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11424             }
11425
11426             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11427                 if(o.autoAbort){
11428                     this.abort();
11429                 }
11430             }else if(this.autoAbort !== false){
11431                 this.abort();
11432             }
11433
11434             if((method == 'GET' && p) || o.xmlData){
11435                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11436                 p = '';
11437             }
11438             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11439             return this.transId;
11440         }else{
11441             Roo.callback(o.callback, o.scope, [o, null, null]);
11442             return null;
11443         }
11444     },
11445
11446     /**
11447      * Determine whether this object has a request outstanding.
11448      * @param {Number} transactionId (Optional) defaults to the last transaction
11449      * @return {Boolean} True if there is an outstanding request.
11450      */
11451     isLoading : function(transId){
11452         if(transId){
11453             return Roo.lib.Ajax.isCallInProgress(transId);
11454         }else{
11455             return this.transId ? true : false;
11456         }
11457     },
11458
11459     /**
11460      * Aborts any outstanding request.
11461      * @param {Number} transactionId (Optional) defaults to the last transaction
11462      */
11463     abort : function(transId){
11464         if(transId || this.isLoading()){
11465             Roo.lib.Ajax.abort(transId || this.transId);
11466         }
11467     },
11468
11469     // private
11470     handleResponse : function(response){
11471         this.transId = false;
11472         var options = response.argument.options;
11473         response.argument = options ? options.argument : null;
11474         this.fireEvent("requestcomplete", this, response, options);
11475         Roo.callback(options.success, options.scope, [response, options]);
11476         Roo.callback(options.callback, options.scope, [options, true, response]);
11477     },
11478
11479     // private
11480     handleFailure : function(response, e){
11481         this.transId = false;
11482         var options = response.argument.options;
11483         response.argument = options ? options.argument : null;
11484         this.fireEvent("requestexception", this, response, options, e);
11485         Roo.callback(options.failure, options.scope, [response, options]);
11486         Roo.callback(options.callback, options.scope, [options, false, response]);
11487     },
11488
11489     // private
11490     doFormUpload : function(o, ps, url){
11491         var id = Roo.id();
11492         var frame = document.createElement('iframe');
11493         frame.id = id;
11494         frame.name = id;
11495         frame.className = 'x-hidden';
11496         if(Roo.isIE){
11497             frame.src = Roo.SSL_SECURE_URL;
11498         }
11499         document.body.appendChild(frame);
11500
11501         if(Roo.isIE){
11502            document.frames[id].name = id;
11503         }
11504
11505         var form = Roo.getDom(o.form);
11506         form.target = id;
11507         form.method = 'POST';
11508         form.enctype = form.encoding = 'multipart/form-data';
11509         if(url){
11510             form.action = url;
11511         }
11512
11513         var hiddens, hd;
11514         if(ps){ // add dynamic params
11515             hiddens = [];
11516             ps = Roo.urlDecode(ps, false);
11517             for(var k in ps){
11518                 if(ps.hasOwnProperty(k)){
11519                     hd = document.createElement('input');
11520                     hd.type = 'hidden';
11521                     hd.name = k;
11522                     hd.value = ps[k];
11523                     form.appendChild(hd);
11524                     hiddens.push(hd);
11525                 }
11526             }
11527         }
11528
11529         function cb(){
11530             var r = {  // bogus response object
11531                 responseText : '',
11532                 responseXML : null
11533             };
11534
11535             r.argument = o ? o.argument : null;
11536
11537             try { //
11538                 var doc;
11539                 if(Roo.isIE){
11540                     doc = frame.contentWindow.document;
11541                 }else {
11542                     doc = (frame.contentDocument || window.frames[id].document);
11543                 }
11544                 if(doc && doc.body){
11545                     r.responseText = doc.body.innerHTML;
11546                 }
11547                 if(doc && doc.XMLDocument){
11548                     r.responseXML = doc.XMLDocument;
11549                 }else {
11550                     r.responseXML = doc;
11551                 }
11552             }
11553             catch(e) {
11554                 // ignore
11555             }
11556
11557             Roo.EventManager.removeListener(frame, 'load', cb, this);
11558
11559             this.fireEvent("requestcomplete", this, r, o);
11560             Roo.callback(o.success, o.scope, [r, o]);
11561             Roo.callback(o.callback, o.scope, [o, true, r]);
11562
11563             setTimeout(function(){document.body.removeChild(frame);}, 100);
11564         }
11565
11566         Roo.EventManager.on(frame, 'load', cb, this);
11567         form.submit();
11568
11569         if(hiddens){ // remove dynamic params
11570             for(var i = 0, len = hiddens.length; i < len; i++){
11571                 form.removeChild(hiddens[i]);
11572             }
11573         }
11574     }
11575 });
11576 /*
11577  * Based on:
11578  * Ext JS Library 1.1.1
11579  * Copyright(c) 2006-2007, Ext JS, LLC.
11580  *
11581  * Originally Released Under LGPL - original licence link has changed is not relivant.
11582  *
11583  * Fork - LGPL
11584  * <script type="text/javascript">
11585  */
11586  
11587 /**
11588  * Global Ajax request class.
11589  * 
11590  * @class Roo.Ajax
11591  * @extends Roo.data.Connection
11592  * @static
11593  * 
11594  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11595  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11596  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11597  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11598  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11599  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11600  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11601  */
11602 Roo.Ajax = new Roo.data.Connection({
11603     // fix up the docs
11604     /**
11605      * @scope Roo.Ajax
11606      * @type {Boolear} 
11607      */
11608     autoAbort : false,
11609
11610     /**
11611      * Serialize the passed form into a url encoded string
11612      * @scope Roo.Ajax
11613      * @param {String/HTMLElement} form
11614      * @return {String}
11615      */
11616     serializeForm : function(form){
11617         return Roo.lib.Ajax.serializeForm(form);
11618     }
11619 });/*
11620  * Based on:
11621  * Ext JS Library 1.1.1
11622  * Copyright(c) 2006-2007, Ext JS, LLC.
11623  *
11624  * Originally Released Under LGPL - original licence link has changed is not relivant.
11625  *
11626  * Fork - LGPL
11627  * <script type="text/javascript">
11628  */
11629
11630  
11631 /**
11632  * @class Roo.UpdateManager
11633  * @extends Roo.util.Observable
11634  * Provides AJAX-style update for Element object.<br><br>
11635  * Usage:<br>
11636  * <pre><code>
11637  * // Get it from a Roo.Element object
11638  * var el = Roo.get("foo");
11639  * var mgr = el.getUpdateManager();
11640  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11641  * ...
11642  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11643  * <br>
11644  * // or directly (returns the same UpdateManager instance)
11645  * var mgr = new Roo.UpdateManager("myElementId");
11646  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11647  * mgr.on("update", myFcnNeedsToKnow);
11648  * <br>
11649    // short handed call directly from the element object
11650    Roo.get("foo").load({
11651         url: "bar.php",
11652         scripts:true,
11653         params: "for=bar",
11654         text: "Loading Foo..."
11655    });
11656  * </code></pre>
11657  * @constructor
11658  * Create new UpdateManager directly.
11659  * @param {String/HTMLElement/Roo.Element} el The element to update
11660  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11661  */
11662 Roo.UpdateManager = function(el, forceNew){
11663     el = Roo.get(el);
11664     if(!forceNew && el.updateManager){
11665         return el.updateManager;
11666     }
11667     /**
11668      * The Element object
11669      * @type Roo.Element
11670      */
11671     this.el = el;
11672     /**
11673      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11674      * @type String
11675      */
11676     this.defaultUrl = null;
11677
11678     this.addEvents({
11679         /**
11680          * @event beforeupdate
11681          * Fired before an update is made, return false from your handler and the update is cancelled.
11682          * @param {Roo.Element} el
11683          * @param {String/Object/Function} url
11684          * @param {String/Object} params
11685          */
11686         "beforeupdate": true,
11687         /**
11688          * @event update
11689          * Fired after successful update is made.
11690          * @param {Roo.Element} el
11691          * @param {Object} oResponseObject The response Object
11692          */
11693         "update": true,
11694         /**
11695          * @event failure
11696          * Fired on update failure.
11697          * @param {Roo.Element} el
11698          * @param {Object} oResponseObject The response Object
11699          */
11700         "failure": true
11701     });
11702     var d = Roo.UpdateManager.defaults;
11703     /**
11704      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11705      * @type String
11706      */
11707     this.sslBlankUrl = d.sslBlankUrl;
11708     /**
11709      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11710      * @type Boolean
11711      */
11712     this.disableCaching = d.disableCaching;
11713     /**
11714      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11715      * @type String
11716      */
11717     this.indicatorText = d.indicatorText;
11718     /**
11719      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11720      * @type String
11721      */
11722     this.showLoadIndicator = d.showLoadIndicator;
11723     /**
11724      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11725      * @type Number
11726      */
11727     this.timeout = d.timeout;
11728
11729     /**
11730      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11731      * @type Boolean
11732      */
11733     this.loadScripts = d.loadScripts;
11734
11735     /**
11736      * Transaction object of current executing transaction
11737      */
11738     this.transaction = null;
11739
11740     /**
11741      * @private
11742      */
11743     this.autoRefreshProcId = null;
11744     /**
11745      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.refreshDelegate = this.refresh.createDelegate(this);
11749     /**
11750      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11751      * @type Function
11752      */
11753     this.updateDelegate = this.update.createDelegate(this);
11754     /**
11755      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11756      * @type Function
11757      */
11758     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11759     /**
11760      * @private
11761      */
11762     this.successDelegate = this.processSuccess.createDelegate(this);
11763     /**
11764      * @private
11765      */
11766     this.failureDelegate = this.processFailure.createDelegate(this);
11767
11768     if(!this.renderer){
11769      /**
11770       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11771       */
11772     this.renderer = new Roo.UpdateManager.BasicRenderer();
11773     }
11774     
11775     Roo.UpdateManager.superclass.constructor.call(this);
11776 };
11777
11778 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11779     /**
11780      * Get the Element this UpdateManager is bound to
11781      * @return {Roo.Element} The element
11782      */
11783     getEl : function(){
11784         return this.el;
11785     },
11786     /**
11787      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11788      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11789 <pre><code>
11790 um.update({<br/>
11791     url: "your-url.php",<br/>
11792     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11793     callback: yourFunction,<br/>
11794     scope: yourObject, //(optional scope)  <br/>
11795     discardUrl: false, <br/>
11796     nocache: false,<br/>
11797     text: "Loading...",<br/>
11798     timeout: 30,<br/>
11799     scripts: false<br/>
11800 });
11801 </code></pre>
11802      * The only required property is url. The optional properties nocache, text and scripts
11803      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11804      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11805      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11806      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11807      */
11808     update : function(url, params, callback, discardUrl){
11809         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11810             var method = this.method,
11811                 cfg;
11812             if(typeof url == "object"){ // must be config object
11813                 cfg = url;
11814                 url = cfg.url;
11815                 params = params || cfg.params;
11816                 callback = callback || cfg.callback;
11817                 discardUrl = discardUrl || cfg.discardUrl;
11818                 if(callback && cfg.scope){
11819                     callback = callback.createDelegate(cfg.scope);
11820                 }
11821                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11822                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11823                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11824                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11825                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11826             }
11827             this.showLoading();
11828             if(!discardUrl){
11829                 this.defaultUrl = url;
11830             }
11831             if(typeof url == "function"){
11832                 url = url.call(this);
11833             }
11834
11835             method = method || (params ? "POST" : "GET");
11836             if(method == "GET"){
11837                 url = this.prepareUrl(url);
11838             }
11839
11840             var o = Roo.apply(cfg ||{}, {
11841                 url : url,
11842                 params: params,
11843                 success: this.successDelegate,
11844                 failure: this.failureDelegate,
11845                 callback: undefined,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11848             });
11849             Roo.log("updated manager called with timeout of " + o.timeout);
11850             this.transaction = Roo.Ajax.request(o);
11851         }
11852     },
11853
11854     /**
11855      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11856      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11857      * @param {String/HTMLElement} form The form Id or form element
11858      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11859      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11860      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11861      */
11862     formUpdate : function(form, url, reset, callback){
11863         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11864             if(typeof url == "function"){
11865                 url = url.call(this);
11866             }
11867             form = Roo.getDom(form);
11868             this.transaction = Roo.Ajax.request({
11869                 form: form,
11870                 url:url,
11871                 success: this.successDelegate,
11872                 failure: this.failureDelegate,
11873                 timeout: (this.timeout*1000),
11874                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11875             });
11876             this.showLoading.defer(1, this);
11877         }
11878     },
11879
11880     /**
11881      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11882      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11883      */
11884     refresh : function(callback){
11885         if(this.defaultUrl == null){
11886             return;
11887         }
11888         this.update(this.defaultUrl, null, callback, true);
11889     },
11890
11891     /**
11892      * Set this element to auto refresh.
11893      * @param {Number} interval How often to update (in seconds).
11894      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11895      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11896      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11897      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11898      */
11899     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11900         if(refreshNow){
11901             this.update(url || this.defaultUrl, params, callback, true);
11902         }
11903         if(this.autoRefreshProcId){
11904             clearInterval(this.autoRefreshProcId);
11905         }
11906         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11907     },
11908
11909     /**
11910      * Stop auto refresh on this element.
11911      */
11912      stopAutoRefresh : function(){
11913         if(this.autoRefreshProcId){
11914             clearInterval(this.autoRefreshProcId);
11915             delete this.autoRefreshProcId;
11916         }
11917     },
11918
11919     isAutoRefreshing : function(){
11920        return this.autoRefreshProcId ? true : false;
11921     },
11922     /**
11923      * Called to update the element to "Loading" state. Override to perform custom action.
11924      */
11925     showLoading : function(){
11926         if(this.showLoadIndicator){
11927             this.el.update(this.indicatorText);
11928         }
11929     },
11930
11931     /**
11932      * Adds unique parameter to query string if disableCaching = true
11933      * @private
11934      */
11935     prepareUrl : function(url){
11936         if(this.disableCaching){
11937             var append = "_dc=" + (new Date().getTime());
11938             if(url.indexOf("?") !== -1){
11939                 url += "&" + append;
11940             }else{
11941                 url += "?" + append;
11942             }
11943         }
11944         return url;
11945     },
11946
11947     /**
11948      * @private
11949      */
11950     processSuccess : function(response){
11951         this.transaction = null;
11952         if(response.argument.form && response.argument.reset){
11953             try{ // put in try/catch since some older FF releases had problems with this
11954                 response.argument.form.reset();
11955             }catch(e){}
11956         }
11957         if(this.loadScripts){
11958             this.renderer.render(this.el, response, this,
11959                 this.updateComplete.createDelegate(this, [response]));
11960         }else{
11961             this.renderer.render(this.el, response, this);
11962             this.updateComplete(response);
11963         }
11964     },
11965
11966     updateComplete : function(response){
11967         this.fireEvent("update", this.el, response);
11968         if(typeof response.argument.callback == "function"){
11969             response.argument.callback(this.el, true, response);
11970         }
11971     },
11972
11973     /**
11974      * @private
11975      */
11976     processFailure : function(response){
11977         this.transaction = null;
11978         this.fireEvent("failure", this.el, response);
11979         if(typeof response.argument.callback == "function"){
11980             response.argument.callback(this.el, false, response);
11981         }
11982     },
11983
11984     /**
11985      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11986      * @param {Object} renderer The object implementing the render() method
11987      */
11988     setRenderer : function(renderer){
11989         this.renderer = renderer;
11990     },
11991
11992     getRenderer : function(){
11993        return this.renderer;
11994     },
11995
11996     /**
11997      * Set the defaultUrl used for updates
11998      * @param {String/Function} defaultUrl The url or a function to call to get the url
11999      */
12000     setDefaultUrl : function(defaultUrl){
12001         this.defaultUrl = defaultUrl;
12002     },
12003
12004     /**
12005      * Aborts the executing transaction
12006      */
12007     abort : function(){
12008         if(this.transaction){
12009             Roo.Ajax.abort(this.transaction);
12010         }
12011     },
12012
12013     /**
12014      * Returns true if an update is in progress
12015      * @return {Boolean}
12016      */
12017     isUpdating : function(){
12018         if(this.transaction){
12019             return Roo.Ajax.isLoading(this.transaction);
12020         }
12021         return false;
12022     }
12023 });
12024
12025 /**
12026  * @class Roo.UpdateManager.defaults
12027  * @static (not really - but it helps the doc tool)
12028  * The defaults collection enables customizing the default properties of UpdateManager
12029  */
12030    Roo.UpdateManager.defaults = {
12031        /**
12032          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12033          * @type Number
12034          */
12035          timeout : 30,
12036
12037          /**
12038          * True to process scripts by default (Defaults to false).
12039          * @type Boolean
12040          */
12041         loadScripts : false,
12042
12043         /**
12044         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12045         * @type String
12046         */
12047         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12048         /**
12049          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12050          * @type Boolean
12051          */
12052         disableCaching : false,
12053         /**
12054          * Whether to show indicatorText when loading (Defaults to true).
12055          * @type Boolean
12056          */
12057         showLoadIndicator : true,
12058         /**
12059          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12060          * @type String
12061          */
12062         indicatorText : '<div class="loading-indicator">Loading...</div>'
12063    };
12064
12065 /**
12066  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12067  *Usage:
12068  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12069  * @param {String/HTMLElement/Roo.Element} el The element to update
12070  * @param {String} url The url
12071  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12072  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12073  * @static
12074  * @deprecated
12075  * @member Roo.UpdateManager
12076  */
12077 Roo.UpdateManager.updateElement = function(el, url, params, options){
12078     var um = Roo.get(el, true).getUpdateManager();
12079     Roo.apply(um, options);
12080     um.update(url, params, options ? options.callback : null);
12081 };
12082 // alias for backwards compat
12083 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12084 /**
12085  * @class Roo.UpdateManager.BasicRenderer
12086  * Default Content renderer. Updates the elements innerHTML with the responseText.
12087  */
12088 Roo.UpdateManager.BasicRenderer = function(){};
12089
12090 Roo.UpdateManager.BasicRenderer.prototype = {
12091     /**
12092      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12093      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12094      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12095      * @param {Roo.Element} el The element being rendered
12096      * @param {Object} response The YUI Connect response object
12097      * @param {UpdateManager} updateManager The calling update manager
12098      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12099      */
12100      render : function(el, response, updateManager, callback){
12101         el.update(response.responseText, updateManager.loadScripts, callback);
12102     }
12103 };
12104 /*
12105  * Based on:
12106  * Roo JS
12107  * (c)) Alan Knowles
12108  * Licence : LGPL
12109  */
12110
12111
12112 /**
12113  * @class Roo.DomTemplate
12114  * @extends Roo.Template
12115  * An effort at a dom based template engine..
12116  *
12117  * Similar to XTemplate, except it uses dom parsing to create the template..
12118  *
12119  * Supported features:
12120  *
12121  *  Tags:
12122
12123 <pre><code>
12124       {a_variable} - output encoded.
12125       {a_variable.format:("Y-m-d")} - call a method on the variable
12126       {a_variable:raw} - unencoded output
12127       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12128       {a_variable:this.method_on_template(...)} - call a method on the template object.
12129  
12130 </code></pre>
12131  *  The tpl tag:
12132 <pre><code>
12133         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12134         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12135         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12136         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12137   
12138 </code></pre>
12139  *      
12140  */
12141 Roo.DomTemplate = function()
12142 {
12143      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12144      if (this.html) {
12145         this.compile();
12146      }
12147 };
12148
12149
12150 Roo.extend(Roo.DomTemplate, Roo.Template, {
12151     /**
12152      * id counter for sub templates.
12153      */
12154     id : 0,
12155     /**
12156      * flag to indicate if dom parser is inside a pre,
12157      * it will strip whitespace if not.
12158      */
12159     inPre : false,
12160     
12161     /**
12162      * The various sub templates
12163      */
12164     tpls : false,
12165     
12166     
12167     
12168     /**
12169      *
12170      * basic tag replacing syntax
12171      * WORD:WORD()
12172      *
12173      * // you can fake an object call by doing this
12174      *  x.t:(test,tesT) 
12175      * 
12176      */
12177     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12178     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12179     
12180     iterChild : function (node, method) {
12181         
12182         var oldPre = this.inPre;
12183         if (node.tagName == 'PRE') {
12184             this.inPre = true;
12185         }
12186         for( var i = 0; i < node.childNodes.length; i++) {
12187             method.call(this, node.childNodes[i]);
12188         }
12189         this.inPre = oldPre;
12190     },
12191     
12192     
12193     
12194     /**
12195      * compile the template
12196      *
12197      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12198      *
12199      */
12200     compile: function()
12201     {
12202         var s = this.html;
12203         
12204         // covert the html into DOM...
12205         var doc = false;
12206         var div =false;
12207         try {
12208             doc = document.implementation.createHTMLDocument("");
12209             doc.documentElement.innerHTML =   this.html  ;
12210             div = doc.documentElement;
12211         } catch (e) {
12212             // old IE... - nasty -- it causes all sorts of issues.. with
12213             // images getting pulled from server..
12214             div = document.createElement('div');
12215             div.innerHTML = this.html;
12216         }
12217         //doc.documentElement.innerHTML = htmlBody
12218          
12219         
12220         
12221         this.tpls = [];
12222         var _t = this;
12223         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12224         
12225         var tpls = this.tpls;
12226         
12227         // create a top level template from the snippet..
12228         
12229         //Roo.log(div.innerHTML);
12230         
12231         var tpl = {
12232             uid : 'master',
12233             id : this.id++,
12234             attr : false,
12235             value : false,
12236             body : div.innerHTML,
12237             
12238             forCall : false,
12239             execCall : false,
12240             dom : div,
12241             isTop : true
12242             
12243         };
12244         tpls.unshift(tpl);
12245         
12246         
12247         // compile them...
12248         this.tpls = [];
12249         Roo.each(tpls, function(tp){
12250             this.compileTpl(tp);
12251             this.tpls[tp.id] = tp;
12252         }, this);
12253         
12254         this.master = tpls[0];
12255         return this;
12256         
12257         
12258     },
12259     
12260     compileNode : function(node, istop) {
12261         // test for
12262         //Roo.log(node);
12263         
12264         
12265         // skip anything not a tag..
12266         if (node.nodeType != 1) {
12267             if (node.nodeType == 3 && !this.inPre) {
12268                 // reduce white space..
12269                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12270                 
12271             }
12272             return;
12273         }
12274         
12275         var tpl = {
12276             uid : false,
12277             id : false,
12278             attr : false,
12279             value : false,
12280             body : '',
12281             
12282             forCall : false,
12283             execCall : false,
12284             dom : false,
12285             isTop : istop
12286             
12287             
12288         };
12289         
12290         
12291         switch(true) {
12292             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12293             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12294             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12295             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12296             // no default..
12297         }
12298         
12299         
12300         if (!tpl.attr) {
12301             // just itterate children..
12302             this.iterChild(node,this.compileNode);
12303             return;
12304         }
12305         tpl.uid = this.id++;
12306         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12307         node.removeAttribute('roo-'+ tpl.attr);
12308         if (tpl.attr != 'name') {
12309             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12310             node.parentNode.replaceChild(placeholder,  node);
12311         } else {
12312             
12313             var placeholder =  document.createElement('span');
12314             placeholder.className = 'roo-tpl-' + tpl.value;
12315             node.parentNode.replaceChild(placeholder,  node);
12316         }
12317         
12318         // parent now sees '{domtplXXXX}
12319         this.iterChild(node,this.compileNode);
12320         
12321         // we should now have node body...
12322         var div = document.createElement('div');
12323         div.appendChild(node);
12324         tpl.dom = node;
12325         // this has the unfortunate side effect of converting tagged attributes
12326         // eg. href="{...}" into %7C...%7D
12327         // this has been fixed by searching for those combo's although it's a bit hacky..
12328         
12329         
12330         tpl.body = div.innerHTML;
12331         
12332         
12333          
12334         tpl.id = tpl.uid;
12335         switch(tpl.attr) {
12336             case 'for' :
12337                 switch (tpl.value) {
12338                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12339                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12340                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12341                 }
12342                 break;
12343             
12344             case 'exec':
12345                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12346                 break;
12347             
12348             case 'if':     
12349                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12350                 break;
12351             
12352             case 'name':
12353                 tpl.id  = tpl.value; // replace non characters???
12354                 break;
12355             
12356         }
12357         
12358         
12359         this.tpls.push(tpl);
12360         
12361         
12362         
12363     },
12364     
12365     
12366     
12367     
12368     /**
12369      * Compile a segment of the template into a 'sub-template'
12370      *
12371      * 
12372      * 
12373      *
12374      */
12375     compileTpl : function(tpl)
12376     {
12377         var fm = Roo.util.Format;
12378         var useF = this.disableFormats !== true;
12379         
12380         var sep = Roo.isGecko ? "+\n" : ",\n";
12381         
12382         var undef = function(str) {
12383             Roo.debug && Roo.log("Property not found :"  + str);
12384             return '';
12385         };
12386           
12387         //Roo.log(tpl.body);
12388         
12389         
12390         
12391         var fn = function(m, lbrace, name, format, args)
12392         {
12393             //Roo.log("ARGS");
12394             //Roo.log(arguments);
12395             args = args ? args.replace(/\\'/g,"'") : args;
12396             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12397             if (typeof(format) == 'undefined') {
12398                 format =  'htmlEncode'; 
12399             }
12400             if (format == 'raw' ) {
12401                 format = false;
12402             }
12403             
12404             if(name.substr(0, 6) == 'domtpl'){
12405                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12406             }
12407             
12408             // build an array of options to determine if value is undefined..
12409             
12410             // basically get 'xxxx.yyyy' then do
12411             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12412             //    (function () { Roo.log("Property not found"); return ''; })() :
12413             //    ......
12414             
12415             var udef_ar = [];
12416             var lookfor = '';
12417             Roo.each(name.split('.'), function(st) {
12418                 lookfor += (lookfor.length ? '.': '') + st;
12419                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12420             });
12421             
12422             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12423             
12424             
12425             if(format && useF){
12426                 
12427                 args = args ? ',' + args : "";
12428                  
12429                 if(format.substr(0, 5) != "this."){
12430                     format = "fm." + format + '(';
12431                 }else{
12432                     format = 'this.call("'+ format.substr(5) + '", ';
12433                     args = ", values";
12434                 }
12435                 
12436                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12437             }
12438              
12439             if (args && args.length) {
12440                 // called with xxyx.yuu:(test,test)
12441                 // change to ()
12442                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12443             }
12444             // raw.. - :raw modifier..
12445             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12446             
12447         };
12448         var body;
12449         // branched to use + in gecko and [].join() in others
12450         if(Roo.isGecko){
12451             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12452                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12453                     "';};};";
12454         }else{
12455             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12456             body.push(tpl.body.replace(/(\r\n|\n)/g,
12457                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12458             body.push("'].join('');};};");
12459             body = body.join('');
12460         }
12461         
12462         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12463        
12464         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12465         eval(body);
12466         
12467         return this;
12468     },
12469      
12470     /**
12471      * same as applyTemplate, except it's done to one of the subTemplates
12472      * when using named templates, you can do:
12473      *
12474      * var str = pl.applySubTemplate('your-name', values);
12475      *
12476      * 
12477      * @param {Number} id of the template
12478      * @param {Object} values to apply to template
12479      * @param {Object} parent (normaly the instance of this object)
12480      */
12481     applySubTemplate : function(id, values, parent)
12482     {
12483         
12484         
12485         var t = this.tpls[id];
12486         
12487         
12488         try { 
12489             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12490                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12491                 return '';
12492             }
12493         } catch(e) {
12494             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12495             Roo.log(values);
12496           
12497             return '';
12498         }
12499         try { 
12500             
12501             if(t.execCall && t.execCall.call(this, values, parent)){
12502                 return '';
12503             }
12504         } catch(e) {
12505             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12506             Roo.log(values);
12507             return '';
12508         }
12509         
12510         try {
12511             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12512             parent = t.target ? values : parent;
12513             if(t.forCall && vs instanceof Array){
12514                 var buf = [];
12515                 for(var i = 0, len = vs.length; i < len; i++){
12516                     try {
12517                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12518                     } catch (e) {
12519                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12520                         Roo.log(e.body);
12521                         //Roo.log(t.compiled);
12522                         Roo.log(vs[i]);
12523                     }   
12524                 }
12525                 return buf.join('');
12526             }
12527         } catch (e) {
12528             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12529             Roo.log(values);
12530             return '';
12531         }
12532         try {
12533             return t.compiled.call(this, vs, parent);
12534         } catch (e) {
12535             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12536             Roo.log(e.body);
12537             //Roo.log(t.compiled);
12538             Roo.log(values);
12539             return '';
12540         }
12541     },
12542
12543    
12544
12545     applyTemplate : function(values){
12546         return this.master.compiled.call(this, values, {});
12547         //var s = this.subs;
12548     },
12549
12550     apply : function(){
12551         return this.applyTemplate.apply(this, arguments);
12552     }
12553
12554  });
12555
12556 Roo.DomTemplate.from = function(el){
12557     el = Roo.getDom(el);
12558     return new Roo.Domtemplate(el.value || el.innerHTML);
12559 };/*
12560  * Based on:
12561  * Ext JS Library 1.1.1
12562  * Copyright(c) 2006-2007, Ext JS, LLC.
12563  *
12564  * Originally Released Under LGPL - original licence link has changed is not relivant.
12565  *
12566  * Fork - LGPL
12567  * <script type="text/javascript">
12568  */
12569
12570 /**
12571  * @class Roo.util.DelayedTask
12572  * Provides a convenient method of performing setTimeout where a new
12573  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12574  * You can use this class to buffer
12575  * the keypress events for a certain number of milliseconds, and perform only if they stop
12576  * for that amount of time.
12577  * @constructor The parameters to this constructor serve as defaults and are not required.
12578  * @param {Function} fn (optional) The default function to timeout
12579  * @param {Object} scope (optional) The default scope of that timeout
12580  * @param {Array} args (optional) The default Array of arguments
12581  */
12582 Roo.util.DelayedTask = function(fn, scope, args){
12583     var id = null, d, t;
12584
12585     var call = function(){
12586         var now = new Date().getTime();
12587         if(now - t >= d){
12588             clearInterval(id);
12589             id = null;
12590             fn.apply(scope, args || []);
12591         }
12592     };
12593     /**
12594      * Cancels any pending timeout and queues a new one
12595      * @param {Number} delay The milliseconds to delay
12596      * @param {Function} newFn (optional) Overrides function passed to constructor
12597      * @param {Object} newScope (optional) Overrides scope passed to constructor
12598      * @param {Array} newArgs (optional) Overrides args passed to constructor
12599      */
12600     this.delay = function(delay, newFn, newScope, newArgs){
12601         if(id && delay != d){
12602             this.cancel();
12603         }
12604         d = delay;
12605         t = new Date().getTime();
12606         fn = newFn || fn;
12607         scope = newScope || scope;
12608         args = newArgs || args;
12609         if(!id){
12610             id = setInterval(call, d);
12611         }
12612     };
12613
12614     /**
12615      * Cancel the last queued timeout
12616      */
12617     this.cancel = function(){
12618         if(id){
12619             clearInterval(id);
12620             id = null;
12621         }
12622     };
12623 };/*
12624  * Based on:
12625  * Ext JS Library 1.1.1
12626  * Copyright(c) 2006-2007, Ext JS, LLC.
12627  *
12628  * Originally Released Under LGPL - original licence link has changed is not relivant.
12629  *
12630  * Fork - LGPL
12631  * <script type="text/javascript">
12632  */
12633  
12634  
12635 Roo.util.TaskRunner = function(interval){
12636     interval = interval || 10;
12637     var tasks = [], removeQueue = [];
12638     var id = 0;
12639     var running = false;
12640
12641     var stopThread = function(){
12642         running = false;
12643         clearInterval(id);
12644         id = 0;
12645     };
12646
12647     var startThread = function(){
12648         if(!running){
12649             running = true;
12650             id = setInterval(runTasks, interval);
12651         }
12652     };
12653
12654     var removeTask = function(task){
12655         removeQueue.push(task);
12656         if(task.onStop){
12657             task.onStop();
12658         }
12659     };
12660
12661     var runTasks = function(){
12662         if(removeQueue.length > 0){
12663             for(var i = 0, len = removeQueue.length; i < len; i++){
12664                 tasks.remove(removeQueue[i]);
12665             }
12666             removeQueue = [];
12667             if(tasks.length < 1){
12668                 stopThread();
12669                 return;
12670             }
12671         }
12672         var now = new Date().getTime();
12673         for(var i = 0, len = tasks.length; i < len; ++i){
12674             var t = tasks[i];
12675             var itime = now - t.taskRunTime;
12676             if(t.interval <= itime){
12677                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12678                 t.taskRunTime = now;
12679                 if(rt === false || t.taskRunCount === t.repeat){
12680                     removeTask(t);
12681                     return;
12682                 }
12683             }
12684             if(t.duration && t.duration <= (now - t.taskStartTime)){
12685                 removeTask(t);
12686             }
12687         }
12688     };
12689
12690     /**
12691      * Queues a new task.
12692      * @param {Object} task
12693      */
12694     this.start = function(task){
12695         tasks.push(task);
12696         task.taskStartTime = new Date().getTime();
12697         task.taskRunTime = 0;
12698         task.taskRunCount = 0;
12699         startThread();
12700         return task;
12701     };
12702
12703     this.stop = function(task){
12704         removeTask(task);
12705         return task;
12706     };
12707
12708     this.stopAll = function(){
12709         stopThread();
12710         for(var i = 0, len = tasks.length; i < len; i++){
12711             if(tasks[i].onStop){
12712                 tasks[i].onStop();
12713             }
12714         }
12715         tasks = [];
12716         removeQueue = [];
12717     };
12718 };
12719
12720 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12721  * Based on:
12722  * Ext JS Library 1.1.1
12723  * Copyright(c) 2006-2007, Ext JS, LLC.
12724  *
12725  * Originally Released Under LGPL - original licence link has changed is not relivant.
12726  *
12727  * Fork - LGPL
12728  * <script type="text/javascript">
12729  */
12730
12731  
12732 /**
12733  * @class Roo.util.MixedCollection
12734  * @extends Roo.util.Observable
12735  * A Collection class that maintains both numeric indexes and keys and exposes events.
12736  * @constructor
12737  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12738  * collection (defaults to false)
12739  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12740  * and return the key value for that item.  This is used when available to look up the key on items that
12741  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12742  * equivalent to providing an implementation for the {@link #getKey} method.
12743  */
12744 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12745     this.items = [];
12746     this.map = {};
12747     this.keys = [];
12748     this.length = 0;
12749     this.addEvents({
12750         /**
12751          * @event clear
12752          * Fires when the collection is cleared.
12753          */
12754         "clear" : true,
12755         /**
12756          * @event add
12757          * Fires when an item is added to the collection.
12758          * @param {Number} index The index at which the item was added.
12759          * @param {Object} o The item added.
12760          * @param {String} key The key associated with the added item.
12761          */
12762         "add" : true,
12763         /**
12764          * @event replace
12765          * Fires when an item is replaced in the collection.
12766          * @param {String} key he key associated with the new added.
12767          * @param {Object} old The item being replaced.
12768          * @param {Object} new The new item.
12769          */
12770         "replace" : true,
12771         /**
12772          * @event remove
12773          * Fires when an item is removed from the collection.
12774          * @param {Object} o The item being removed.
12775          * @param {String} key (optional) The key associated with the removed item.
12776          */
12777         "remove" : true,
12778         "sort" : true
12779     });
12780     this.allowFunctions = allowFunctions === true;
12781     if(keyFn){
12782         this.getKey = keyFn;
12783     }
12784     Roo.util.MixedCollection.superclass.constructor.call(this);
12785 };
12786
12787 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12788     allowFunctions : false,
12789     
12790 /**
12791  * Adds an item to the collection.
12792  * @param {String} key The key to associate with the item
12793  * @param {Object} o The item to add.
12794  * @return {Object} The item added.
12795  */
12796     add : function(key, o){
12797         if(arguments.length == 1){
12798             o = arguments[0];
12799             key = this.getKey(o);
12800         }
12801         if(typeof key == "undefined" || key === null){
12802             this.length++;
12803             this.items.push(o);
12804             this.keys.push(null);
12805         }else{
12806             var old = this.map[key];
12807             if(old){
12808                 return this.replace(key, o);
12809             }
12810             this.length++;
12811             this.items.push(o);
12812             this.map[key] = o;
12813             this.keys.push(key);
12814         }
12815         this.fireEvent("add", this.length-1, o, key);
12816         return o;
12817     },
12818        
12819 /**
12820   * MixedCollection has a generic way to fetch keys if you implement getKey.
12821 <pre><code>
12822 // normal way
12823 var mc = new Roo.util.MixedCollection();
12824 mc.add(someEl.dom.id, someEl);
12825 mc.add(otherEl.dom.id, otherEl);
12826 //and so on
12827
12828 // using getKey
12829 var mc = new Roo.util.MixedCollection();
12830 mc.getKey = function(el){
12831    return el.dom.id;
12832 };
12833 mc.add(someEl);
12834 mc.add(otherEl);
12835
12836 // or via the constructor
12837 var mc = new Roo.util.MixedCollection(false, function(el){
12838    return el.dom.id;
12839 });
12840 mc.add(someEl);
12841 mc.add(otherEl);
12842 </code></pre>
12843  * @param o {Object} The item for which to find the key.
12844  * @return {Object} The key for the passed item.
12845  */
12846     getKey : function(o){
12847          return o.id; 
12848     },
12849    
12850 /**
12851  * Replaces an item in the collection.
12852  * @param {String} key The key associated with the item to replace, or the item to replace.
12853  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12854  * @return {Object}  The new item.
12855  */
12856     replace : function(key, o){
12857         if(arguments.length == 1){
12858             o = arguments[0];
12859             key = this.getKey(o);
12860         }
12861         var old = this.item(key);
12862         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12863              return this.add(key, o);
12864         }
12865         var index = this.indexOfKey(key);
12866         this.items[index] = o;
12867         this.map[key] = o;
12868         this.fireEvent("replace", key, old, o);
12869         return o;
12870     },
12871    
12872 /**
12873  * Adds all elements of an Array or an Object to the collection.
12874  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12875  * an Array of values, each of which are added to the collection.
12876  */
12877     addAll : function(objs){
12878         if(arguments.length > 1 || objs instanceof Array){
12879             var args = arguments.length > 1 ? arguments : objs;
12880             for(var i = 0, len = args.length; i < len; i++){
12881                 this.add(args[i]);
12882             }
12883         }else{
12884             for(var key in objs){
12885                 if(this.allowFunctions || typeof objs[key] != "function"){
12886                     this.add(key, objs[key]);
12887                 }
12888             }
12889         }
12890     },
12891    
12892 /**
12893  * Executes the specified function once for every item in the collection, passing each
12894  * item as the first and only parameter. returning false from the function will stop the iteration.
12895  * @param {Function} fn The function to execute for each item.
12896  * @param {Object} scope (optional) The scope in which to execute the function.
12897  */
12898     each : function(fn, scope){
12899         var items = [].concat(this.items); // each safe for removal
12900         for(var i = 0, len = items.length; i < len; i++){
12901             if(fn.call(scope || items[i], items[i], i, len) === false){
12902                 break;
12903             }
12904         }
12905     },
12906    
12907 /**
12908  * Executes the specified function once for every key in the collection, passing each
12909  * key, and its associated item as the first two parameters.
12910  * @param {Function} fn The function to execute for each item.
12911  * @param {Object} scope (optional) The scope in which to execute the function.
12912  */
12913     eachKey : function(fn, scope){
12914         for(var i = 0, len = this.keys.length; i < len; i++){
12915             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12916         }
12917     },
12918    
12919 /**
12920  * Returns the first item in the collection which elicits a true return value from the
12921  * passed selection function.
12922  * @param {Function} fn The selection function to execute for each item.
12923  * @param {Object} scope (optional) The scope in which to execute the function.
12924  * @return {Object} The first item in the collection which returned true from the selection function.
12925  */
12926     find : function(fn, scope){
12927         for(var i = 0, len = this.items.length; i < len; i++){
12928             if(fn.call(scope || window, this.items[i], this.keys[i])){
12929                 return this.items[i];
12930             }
12931         }
12932         return null;
12933     },
12934    
12935 /**
12936  * Inserts an item at the specified index in the collection.
12937  * @param {Number} index The index to insert the item at.
12938  * @param {String} key The key to associate with the new item, or the item itself.
12939  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12940  * @return {Object} The item inserted.
12941  */
12942     insert : function(index, key, o){
12943         if(arguments.length == 2){
12944             o = arguments[1];
12945             key = this.getKey(o);
12946         }
12947         if(index >= this.length){
12948             return this.add(key, o);
12949         }
12950         this.length++;
12951         this.items.splice(index, 0, o);
12952         if(typeof key != "undefined" && key != null){
12953             this.map[key] = o;
12954         }
12955         this.keys.splice(index, 0, key);
12956         this.fireEvent("add", index, o, key);
12957         return o;
12958     },
12959    
12960 /**
12961  * Removed an item from the collection.
12962  * @param {Object} o The item to remove.
12963  * @return {Object} The item removed.
12964  */
12965     remove : function(o){
12966         return this.removeAt(this.indexOf(o));
12967     },
12968    
12969 /**
12970  * Remove an item from a specified index in the collection.
12971  * @param {Number} index The index within the collection of the item to remove.
12972  */
12973     removeAt : function(index){
12974         if(index < this.length && index >= 0){
12975             this.length--;
12976             var o = this.items[index];
12977             this.items.splice(index, 1);
12978             var key = this.keys[index];
12979             if(typeof key != "undefined"){
12980                 delete this.map[key];
12981             }
12982             this.keys.splice(index, 1);
12983             this.fireEvent("remove", o, key);
12984         }
12985     },
12986    
12987 /**
12988  * Removed an item associated with the passed key fom the collection.
12989  * @param {String} key The key of the item to remove.
12990  */
12991     removeKey : function(key){
12992         return this.removeAt(this.indexOfKey(key));
12993     },
12994    
12995 /**
12996  * Returns the number of items in the collection.
12997  * @return {Number} the number of items in the collection.
12998  */
12999     getCount : function(){
13000         return this.length; 
13001     },
13002    
13003 /**
13004  * Returns index within the collection of the passed Object.
13005  * @param {Object} o The item to find the index of.
13006  * @return {Number} index of the item.
13007  */
13008     indexOf : function(o){
13009         if(!this.items.indexOf){
13010             for(var i = 0, len = this.items.length; i < len; i++){
13011                 if(this.items[i] == o) return i;
13012             }
13013             return -1;
13014         }else{
13015             return this.items.indexOf(o);
13016         }
13017     },
13018    
13019 /**
13020  * Returns index within the collection of the passed key.
13021  * @param {String} key The key to find the index of.
13022  * @return {Number} index of the key.
13023  */
13024     indexOfKey : function(key){
13025         if(!this.keys.indexOf){
13026             for(var i = 0, len = this.keys.length; i < len; i++){
13027                 if(this.keys[i] == key) return i;
13028             }
13029             return -1;
13030         }else{
13031             return this.keys.indexOf(key);
13032         }
13033     },
13034    
13035 /**
13036  * Returns the item associated with the passed key OR index. Key has priority over index.
13037  * @param {String/Number} key The key or index of the item.
13038  * @return {Object} The item associated with the passed key.
13039  */
13040     item : function(key){
13041         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13042         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13043     },
13044     
13045 /**
13046  * Returns the item at the specified index.
13047  * @param {Number} index The index of the item.
13048  * @return {Object}
13049  */
13050     itemAt : function(index){
13051         return this.items[index];
13052     },
13053     
13054 /**
13055  * Returns the item associated with the passed key.
13056  * @param {String/Number} key The key of the item.
13057  * @return {Object} The item associated with the passed key.
13058  */
13059     key : function(key){
13060         return this.map[key];
13061     },
13062    
13063 /**
13064  * Returns true if the collection contains the passed Object as an item.
13065  * @param {Object} o  The Object to look for in the collection.
13066  * @return {Boolean} True if the collection contains the Object as an item.
13067  */
13068     contains : function(o){
13069         return this.indexOf(o) != -1;
13070     },
13071    
13072 /**
13073  * Returns true if the collection contains the passed Object as a key.
13074  * @param {String} key The key to look for in the collection.
13075  * @return {Boolean} True if the collection contains the Object as a key.
13076  */
13077     containsKey : function(key){
13078         return typeof this.map[key] != "undefined";
13079     },
13080    
13081 /**
13082  * Removes all items from the collection.
13083  */
13084     clear : function(){
13085         this.length = 0;
13086         this.items = [];
13087         this.keys = [];
13088         this.map = {};
13089         this.fireEvent("clear");
13090     },
13091    
13092 /**
13093  * Returns the first item in the collection.
13094  * @return {Object} the first item in the collection..
13095  */
13096     first : function(){
13097         return this.items[0]; 
13098     },
13099    
13100 /**
13101  * Returns the last item in the collection.
13102  * @return {Object} the last item in the collection..
13103  */
13104     last : function(){
13105         return this.items[this.length-1];   
13106     },
13107     
13108     _sort : function(property, dir, fn){
13109         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13110         fn = fn || function(a, b){
13111             return a-b;
13112         };
13113         var c = [], k = this.keys, items = this.items;
13114         for(var i = 0, len = items.length; i < len; i++){
13115             c[c.length] = {key: k[i], value: items[i], index: i};
13116         }
13117         c.sort(function(a, b){
13118             var v = fn(a[property], b[property]) * dsc;
13119             if(v == 0){
13120                 v = (a.index < b.index ? -1 : 1);
13121             }
13122             return v;
13123         });
13124         for(var i = 0, len = c.length; i < len; i++){
13125             items[i] = c[i].value;
13126             k[i] = c[i].key;
13127         }
13128         this.fireEvent("sort", this);
13129     },
13130     
13131     /**
13132      * Sorts this collection with the passed comparison function
13133      * @param {String} direction (optional) "ASC" or "DESC"
13134      * @param {Function} fn (optional) comparison function
13135      */
13136     sort : function(dir, fn){
13137         this._sort("value", dir, fn);
13138     },
13139     
13140     /**
13141      * Sorts this collection by keys
13142      * @param {String} direction (optional) "ASC" or "DESC"
13143      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13144      */
13145     keySort : function(dir, fn){
13146         this._sort("key", dir, fn || function(a, b){
13147             return String(a).toUpperCase()-String(b).toUpperCase();
13148         });
13149     },
13150     
13151     /**
13152      * Returns a range of items in this collection
13153      * @param {Number} startIndex (optional) defaults to 0
13154      * @param {Number} endIndex (optional) default to the last item
13155      * @return {Array} An array of items
13156      */
13157     getRange : function(start, end){
13158         var items = this.items;
13159         if(items.length < 1){
13160             return [];
13161         }
13162         start = start || 0;
13163         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13164         var r = [];
13165         if(start <= end){
13166             for(var i = start; i <= end; i++) {
13167                     r[r.length] = items[i];
13168             }
13169         }else{
13170             for(var i = start; i >= end; i--) {
13171                     r[r.length] = items[i];
13172             }
13173         }
13174         return r;
13175     },
13176         
13177     /**
13178      * Filter the <i>objects</i> in this collection by a specific property. 
13179      * Returns a new collection that has been filtered.
13180      * @param {String} property A property on your objects
13181      * @param {String/RegExp} value Either string that the property values 
13182      * should start with or a RegExp to test against the property
13183      * @return {MixedCollection} The new filtered collection
13184      */
13185     filter : function(property, value){
13186         if(!value.exec){ // not a regex
13187             value = String(value);
13188             if(value.length == 0){
13189                 return this.clone();
13190             }
13191             value = new RegExp("^" + Roo.escapeRe(value), "i");
13192         }
13193         return this.filterBy(function(o){
13194             return o && value.test(o[property]);
13195         });
13196         },
13197     
13198     /**
13199      * Filter by a function. * Returns a new collection that has been filtered.
13200      * The passed function will be called with each 
13201      * object in the collection. If the function returns true, the value is included 
13202      * otherwise it is filtered.
13203      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13204      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13205      * @return {MixedCollection} The new filtered collection
13206      */
13207     filterBy : function(fn, scope){
13208         var r = new Roo.util.MixedCollection();
13209         r.getKey = this.getKey;
13210         var k = this.keys, it = this.items;
13211         for(var i = 0, len = it.length; i < len; i++){
13212             if(fn.call(scope||this, it[i], k[i])){
13213                                 r.add(k[i], it[i]);
13214                         }
13215         }
13216         return r;
13217     },
13218     
13219     /**
13220      * Creates a duplicate of this collection
13221      * @return {MixedCollection}
13222      */
13223     clone : function(){
13224         var r = new Roo.util.MixedCollection();
13225         var k = this.keys, it = this.items;
13226         for(var i = 0, len = it.length; i < len; i++){
13227             r.add(k[i], it[i]);
13228         }
13229         r.getKey = this.getKey;
13230         return r;
13231     }
13232 });
13233 /**
13234  * Returns the item associated with the passed key or index.
13235  * @method
13236  * @param {String/Number} key The key or index of the item.
13237  * @return {Object} The item associated with the passed key.
13238  */
13239 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13240  * Based on:
13241  * Ext JS Library 1.1.1
13242  * Copyright(c) 2006-2007, Ext JS, LLC.
13243  *
13244  * Originally Released Under LGPL - original licence link has changed is not relivant.
13245  *
13246  * Fork - LGPL
13247  * <script type="text/javascript">
13248  */
13249 /**
13250  * @class Roo.util.JSON
13251  * Modified version of Douglas Crockford"s json.js that doesn"t
13252  * mess with the Object prototype 
13253  * http://www.json.org/js.html
13254  * @singleton
13255  */
13256 Roo.util.JSON = new (function(){
13257     var useHasOwn = {}.hasOwnProperty ? true : false;
13258     
13259     // crashes Safari in some instances
13260     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13261     
13262     var pad = function(n) {
13263         return n < 10 ? "0" + n : n;
13264     };
13265     
13266     var m = {
13267         "\b": '\\b',
13268         "\t": '\\t',
13269         "\n": '\\n',
13270         "\f": '\\f',
13271         "\r": '\\r',
13272         '"' : '\\"',
13273         "\\": '\\\\'
13274     };
13275
13276     var encodeString = function(s){
13277         if (/["\\\x00-\x1f]/.test(s)) {
13278             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13279                 var c = m[b];
13280                 if(c){
13281                     return c;
13282                 }
13283                 c = b.charCodeAt();
13284                 return "\\u00" +
13285                     Math.floor(c / 16).toString(16) +
13286                     (c % 16).toString(16);
13287             }) + '"';
13288         }
13289         return '"' + s + '"';
13290     };
13291     
13292     var encodeArray = function(o){
13293         var a = ["["], b, i, l = o.length, v;
13294             for (i = 0; i < l; i += 1) {
13295                 v = o[i];
13296                 switch (typeof v) {
13297                     case "undefined":
13298                     case "function":
13299                     case "unknown":
13300                         break;
13301                     default:
13302                         if (b) {
13303                             a.push(',');
13304                         }
13305                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13306                         b = true;
13307                 }
13308             }
13309             a.push("]");
13310             return a.join("");
13311     };
13312     
13313     var encodeDate = function(o){
13314         return '"' + o.getFullYear() + "-" +
13315                 pad(o.getMonth() + 1) + "-" +
13316                 pad(o.getDate()) + "T" +
13317                 pad(o.getHours()) + ":" +
13318                 pad(o.getMinutes()) + ":" +
13319                 pad(o.getSeconds()) + '"';
13320     };
13321     
13322     /**
13323      * Encodes an Object, Array or other value
13324      * @param {Mixed} o The variable to encode
13325      * @return {String} The JSON string
13326      */
13327     this.encode = function(o)
13328     {
13329         // should this be extended to fully wrap stringify..
13330         
13331         if(typeof o == "undefined" || o === null){
13332             return "null";
13333         }else if(o instanceof Array){
13334             return encodeArray(o);
13335         }else if(o instanceof Date){
13336             return encodeDate(o);
13337         }else if(typeof o == "string"){
13338             return encodeString(o);
13339         }else if(typeof o == "number"){
13340             return isFinite(o) ? String(o) : "null";
13341         }else if(typeof o == "boolean"){
13342             return String(o);
13343         }else {
13344             var a = ["{"], b, i, v;
13345             for (i in o) {
13346                 if(!useHasOwn || o.hasOwnProperty(i)) {
13347                     v = o[i];
13348                     switch (typeof v) {
13349                     case "undefined":
13350                     case "function":
13351                     case "unknown":
13352                         break;
13353                     default:
13354                         if(b){
13355                             a.push(',');
13356                         }
13357                         a.push(this.encode(i), ":",
13358                                 v === null ? "null" : this.encode(v));
13359                         b = true;
13360                     }
13361                 }
13362             }
13363             a.push("}");
13364             return a.join("");
13365         }
13366     };
13367     
13368     /**
13369      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13370      * @param {String} json The JSON string
13371      * @return {Object} The resulting object
13372      */
13373     this.decode = function(json){
13374         
13375         return  /** eval:var:json */ eval("(" + json + ')');
13376     };
13377 })();
13378 /** 
13379  * Shorthand for {@link Roo.util.JSON#encode}
13380  * @member Roo encode 
13381  * @method */
13382 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13383 /** 
13384  * Shorthand for {@link Roo.util.JSON#decode}
13385  * @member Roo decode 
13386  * @method */
13387 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13388 /*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398  
13399 /**
13400  * @class Roo.util.Format
13401  * Reusable data formatting functions
13402  * @singleton
13403  */
13404 Roo.util.Format = function(){
13405     var trimRe = /^\s+|\s+$/g;
13406     return {
13407         /**
13408          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13409          * @param {String} value The string to truncate
13410          * @param {Number} length The maximum length to allow before truncating
13411          * @return {String} The converted text
13412          */
13413         ellipsis : function(value, len){
13414             if(value && value.length > len){
13415                 return value.substr(0, len-3)+"...";
13416             }
13417             return value;
13418         },
13419
13420         /**
13421          * Checks a reference and converts it to empty string if it is undefined
13422          * @param {Mixed} value Reference to check
13423          * @return {Mixed} Empty string if converted, otherwise the original value
13424          */
13425         undef : function(value){
13426             return typeof value != "undefined" ? value : "";
13427         },
13428
13429         /**
13430          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13431          * @param {String} value The string to encode
13432          * @return {String} The encoded text
13433          */
13434         htmlEncode : function(value){
13435             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13436         },
13437
13438         /**
13439          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13440          * @param {String} value The string to decode
13441          * @return {String} The decoded text
13442          */
13443         htmlDecode : function(value){
13444             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13445         },
13446
13447         /**
13448          * Trims any whitespace from either side of a string
13449          * @param {String} value The text to trim
13450          * @return {String} The trimmed text
13451          */
13452         trim : function(value){
13453             return String(value).replace(trimRe, "");
13454         },
13455
13456         /**
13457          * Returns a substring from within an original string
13458          * @param {String} value The original text
13459          * @param {Number} start The start index of the substring
13460          * @param {Number} length The length of the substring
13461          * @return {String} The substring
13462          */
13463         substr : function(value, start, length){
13464             return String(value).substr(start, length);
13465         },
13466
13467         /**
13468          * Converts a string to all lower case letters
13469          * @param {String} value The text to convert
13470          * @return {String} The converted text
13471          */
13472         lowercase : function(value){
13473             return String(value).toLowerCase();
13474         },
13475
13476         /**
13477          * Converts a string to all upper case letters
13478          * @param {String} value The text to convert
13479          * @return {String} The converted text
13480          */
13481         uppercase : function(value){
13482             return String(value).toUpperCase();
13483         },
13484
13485         /**
13486          * Converts the first character only of a string to upper case
13487          * @param {String} value The text to convert
13488          * @return {String} The converted text
13489          */
13490         capitalize : function(value){
13491             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13492         },
13493
13494         // private
13495         call : function(value, fn){
13496             if(arguments.length > 2){
13497                 var args = Array.prototype.slice.call(arguments, 2);
13498                 args.unshift(value);
13499                  
13500                 return /** eval:var:value */  eval(fn).apply(window, args);
13501             }else{
13502                 /** eval:var:value */
13503                 return /** eval:var:value */ eval(fn).call(window, value);
13504             }
13505         },
13506
13507        
13508         /**
13509          * safer version of Math.toFixed..??/
13510          * @param {Number/String} value The numeric value to format
13511          * @param {Number/String} value Decimal places 
13512          * @return {String} The formatted currency string
13513          */
13514         toFixed : function(v, n)
13515         {
13516             // why not use to fixed - precision is buggered???
13517             if (!n) {
13518                 return Math.round(v-0);
13519             }
13520             var fact = Math.pow(10,n+1);
13521             v = (Math.round((v-0)*fact))/fact;
13522             var z = (''+fact).substring(2);
13523             if (v == Math.floor(v)) {
13524                 return Math.floor(v) + '.' + z;
13525             }
13526             
13527             // now just padd decimals..
13528             var ps = String(v).split('.');
13529             var fd = (ps[1] + z);
13530             var r = fd.substring(0,n); 
13531             var rm = fd.substring(n); 
13532             if (rm < 5) {
13533                 return ps[0] + '.' + r;
13534             }
13535             r*=1; // turn it into a number;
13536             r++;
13537             if (String(r).length != n) {
13538                 ps[0]*=1;
13539                 ps[0]++;
13540                 r = String(r).substring(1); // chop the end off.
13541             }
13542             
13543             return ps[0] + '.' + r;
13544              
13545         },
13546         
13547         /**
13548          * Format a number as US currency
13549          * @param {Number/String} value The numeric value to format
13550          * @return {String} The formatted currency string
13551          */
13552         usMoney : function(v){
13553             return '$' + Roo.util.Format.number(v);
13554         },
13555         
13556         /**
13557          * Format a number
13558          * eventually this should probably emulate php's number_format
13559          * @param {Number/String} value The numeric value to format
13560          * @param {Number} decimals number of decimal places
13561          * @return {String} The formatted currency string
13562          */
13563         number : function(v,decimals)
13564         {
13565             // multiply and round.
13566             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13567             var mul = Math.pow(10, decimals);
13568             var zero = String(mul).substring(1);
13569             v = (Math.round((v-0)*mul))/mul;
13570             
13571             // if it's '0' number.. then
13572             
13573             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13574             v = String(v);
13575             var ps = v.split('.');
13576             var whole = ps[0];
13577             
13578             
13579             var r = /(\d+)(\d{3})/;
13580             // add comma's
13581             while (r.test(whole)) {
13582                 whole = whole.replace(r, '$1' + ',' + '$2');
13583             }
13584             
13585             
13586             var sub = ps[1] ?
13587                     // has decimals..
13588                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13589                     // does not have decimals
13590                     (decimals ? ('.' + zero) : '');
13591             
13592             
13593             return whole + sub ;
13594         },
13595         
13596         /**
13597          * Parse a value into a formatted date using the specified format pattern.
13598          * @param {Mixed} value The value to format
13599          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13600          * @return {String} The formatted date string
13601          */
13602         date : function(v, format){
13603             if(!v){
13604                 return "";
13605             }
13606             if(!(v instanceof Date)){
13607                 v = new Date(Date.parse(v));
13608             }
13609             return v.dateFormat(format || Roo.util.Format.defaults.date);
13610         },
13611
13612         /**
13613          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13614          * @param {String} format Any valid date format string
13615          * @return {Function} The date formatting function
13616          */
13617         dateRenderer : function(format){
13618             return function(v){
13619                 return Roo.util.Format.date(v, format);  
13620             };
13621         },
13622
13623         // private
13624         stripTagsRE : /<\/?[^>]+>/gi,
13625         
13626         /**
13627          * Strips all HTML tags
13628          * @param {Mixed} value The text from which to strip tags
13629          * @return {String} The stripped text
13630          */
13631         stripTags : function(v){
13632             return !v ? v : String(v).replace(this.stripTagsRE, "");
13633         }
13634     };
13635 }();
13636 Roo.util.Format.defaults = {
13637     date : 'd/M/Y'
13638 };/*
13639  * Based on:
13640  * Ext JS Library 1.1.1
13641  * Copyright(c) 2006-2007, Ext JS, LLC.
13642  *
13643  * Originally Released Under LGPL - original licence link has changed is not relivant.
13644  *
13645  * Fork - LGPL
13646  * <script type="text/javascript">
13647  */
13648
13649
13650  
13651
13652 /**
13653  * @class Roo.MasterTemplate
13654  * @extends Roo.Template
13655  * Provides a template that can have child templates. The syntax is:
13656 <pre><code>
13657 var t = new Roo.MasterTemplate(
13658         '&lt;select name="{name}"&gt;',
13659                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13660         '&lt;/select&gt;'
13661 );
13662 t.add('options', {value: 'foo', text: 'bar'});
13663 // or you can add multiple child elements in one shot
13664 t.addAll('options', [
13665     {value: 'foo', text: 'bar'},
13666     {value: 'foo2', text: 'bar2'},
13667     {value: 'foo3', text: 'bar3'}
13668 ]);
13669 // then append, applying the master template values
13670 t.append('my-form', {name: 'my-select'});
13671 </code></pre>
13672 * A name attribute for the child template is not required if you have only one child
13673 * template or you want to refer to them by index.
13674  */
13675 Roo.MasterTemplate = function(){
13676     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13677     this.originalHtml = this.html;
13678     var st = {};
13679     var m, re = this.subTemplateRe;
13680     re.lastIndex = 0;
13681     var subIndex = 0;
13682     while(m = re.exec(this.html)){
13683         var name = m[1], content = m[2];
13684         st[subIndex] = {
13685             name: name,
13686             index: subIndex,
13687             buffer: [],
13688             tpl : new Roo.Template(content)
13689         };
13690         if(name){
13691             st[name] = st[subIndex];
13692         }
13693         st[subIndex].tpl.compile();
13694         st[subIndex].tpl.call = this.call.createDelegate(this);
13695         subIndex++;
13696     }
13697     this.subCount = subIndex;
13698     this.subs = st;
13699 };
13700 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13701     /**
13702     * The regular expression used to match sub templates
13703     * @type RegExp
13704     * @property
13705     */
13706     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13707
13708     /**
13709      * Applies the passed values to a child template.
13710      * @param {String/Number} name (optional) The name or index of the child template
13711      * @param {Array/Object} values The values to be applied to the template
13712      * @return {MasterTemplate} this
13713      */
13714      add : function(name, values){
13715         if(arguments.length == 1){
13716             values = arguments[0];
13717             name = 0;
13718         }
13719         var s = this.subs[name];
13720         s.buffer[s.buffer.length] = s.tpl.apply(values);
13721         return this;
13722     },
13723
13724     /**
13725      * Applies all the passed values to a child template.
13726      * @param {String/Number} name (optional) The name or index of the child template
13727      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13728      * @param {Boolean} reset (optional) True to reset the template first
13729      * @return {MasterTemplate} this
13730      */
13731     fill : function(name, values, reset){
13732         var a = arguments;
13733         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13734             values = a[0];
13735             name = 0;
13736             reset = a[1];
13737         }
13738         if(reset){
13739             this.reset();
13740         }
13741         for(var i = 0, len = values.length; i < len; i++){
13742             this.add(name, values[i]);
13743         }
13744         return this;
13745     },
13746
13747     /**
13748      * Resets the template for reuse
13749      * @return {MasterTemplate} this
13750      */
13751      reset : function(){
13752         var s = this.subs;
13753         for(var i = 0; i < this.subCount; i++){
13754             s[i].buffer = [];
13755         }
13756         return this;
13757     },
13758
13759     applyTemplate : function(values){
13760         var s = this.subs;
13761         var replaceIndex = -1;
13762         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13763             return s[++replaceIndex].buffer.join("");
13764         });
13765         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13766     },
13767
13768     apply : function(){
13769         return this.applyTemplate.apply(this, arguments);
13770     },
13771
13772     compile : function(){return this;}
13773 });
13774
13775 /**
13776  * Alias for fill().
13777  * @method
13778  */
13779 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13780  /**
13781  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13782  * var tpl = Roo.MasterTemplate.from('element-id');
13783  * @param {String/HTMLElement} el
13784  * @param {Object} config
13785  * @static
13786  */
13787 Roo.MasterTemplate.from = function(el, config){
13788     el = Roo.getDom(el);
13789     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13790 };/*
13791  * Based on:
13792  * Ext JS Library 1.1.1
13793  * Copyright(c) 2006-2007, Ext JS, LLC.
13794  *
13795  * Originally Released Under LGPL - original licence link has changed is not relivant.
13796  *
13797  * Fork - LGPL
13798  * <script type="text/javascript">
13799  */
13800
13801  
13802 /**
13803  * @class Roo.util.CSS
13804  * Utility class for manipulating CSS rules
13805  * @singleton
13806  */
13807 Roo.util.CSS = function(){
13808         var rules = null;
13809         var doc = document;
13810
13811     var camelRe = /(-[a-z])/gi;
13812     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13813
13814    return {
13815    /**
13816     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13817     * tag and appended to the HEAD of the document.
13818     * @param {String|Object} cssText The text containing the css rules
13819     * @param {String} id An id to add to the stylesheet for later removal
13820     * @return {StyleSheet}
13821     */
13822     createStyleSheet : function(cssText, id){
13823         var ss;
13824         var head = doc.getElementsByTagName("head")[0];
13825         var nrules = doc.createElement("style");
13826         nrules.setAttribute("type", "text/css");
13827         if(id){
13828             nrules.setAttribute("id", id);
13829         }
13830         if (typeof(cssText) != 'string') {
13831             // support object maps..
13832             // not sure if this a good idea.. 
13833             // perhaps it should be merged with the general css handling
13834             // and handle js style props.
13835             var cssTextNew = [];
13836             for(var n in cssText) {
13837                 var citems = [];
13838                 for(var k in cssText[n]) {
13839                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13840                 }
13841                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13842                 
13843             }
13844             cssText = cssTextNew.join("\n");
13845             
13846         }
13847        
13848        
13849        if(Roo.isIE){
13850            head.appendChild(nrules);
13851            ss = nrules.styleSheet;
13852            ss.cssText = cssText;
13853        }else{
13854            try{
13855                 nrules.appendChild(doc.createTextNode(cssText));
13856            }catch(e){
13857                nrules.cssText = cssText; 
13858            }
13859            head.appendChild(nrules);
13860            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13861        }
13862        this.cacheStyleSheet(ss);
13863        return ss;
13864    },
13865
13866    /**
13867     * Removes a style or link tag by id
13868     * @param {String} id The id of the tag
13869     */
13870    removeStyleSheet : function(id){
13871        var existing = doc.getElementById(id);
13872        if(existing){
13873            existing.parentNode.removeChild(existing);
13874        }
13875    },
13876
13877    /**
13878     * Dynamically swaps an existing stylesheet reference for a new one
13879     * @param {String} id The id of an existing link tag to remove
13880     * @param {String} url The href of the new stylesheet to include
13881     */
13882    swapStyleSheet : function(id, url){
13883        this.removeStyleSheet(id);
13884        var ss = doc.createElement("link");
13885        ss.setAttribute("rel", "stylesheet");
13886        ss.setAttribute("type", "text/css");
13887        ss.setAttribute("id", id);
13888        ss.setAttribute("href", url);
13889        doc.getElementsByTagName("head")[0].appendChild(ss);
13890    },
13891    
13892    /**
13893     * Refresh the rule cache if you have dynamically added stylesheets
13894     * @return {Object} An object (hash) of rules indexed by selector
13895     */
13896    refreshCache : function(){
13897        return this.getRules(true);
13898    },
13899
13900    // private
13901    cacheStyleSheet : function(stylesheet){
13902        if(!rules){
13903            rules = {};
13904        }
13905        try{// try catch for cross domain access issue
13906            var ssRules = stylesheet.cssRules || stylesheet.rules;
13907            for(var j = ssRules.length-1; j >= 0; --j){
13908                rules[ssRules[j].selectorText] = ssRules[j];
13909            }
13910        }catch(e){}
13911    },
13912    
13913    /**
13914     * Gets all css rules for the document
13915     * @param {Boolean} refreshCache true to refresh the internal cache
13916     * @return {Object} An object (hash) of rules indexed by selector
13917     */
13918    getRules : function(refreshCache){
13919                 if(rules == null || refreshCache){
13920                         rules = {};
13921                         var ds = doc.styleSheets;
13922                         for(var i =0, len = ds.length; i < len; i++){
13923                             try{
13924                         this.cacheStyleSheet(ds[i]);
13925                     }catch(e){} 
13926                 }
13927                 }
13928                 return rules;
13929         },
13930         
13931         /**
13932     * Gets an an individual CSS rule by selector(s)
13933     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13934     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13935     * @return {CSSRule} The CSS rule or null if one is not found
13936     */
13937    getRule : function(selector, refreshCache){
13938                 var rs = this.getRules(refreshCache);
13939                 if(!(selector instanceof Array)){
13940                     return rs[selector];
13941                 }
13942                 for(var i = 0; i < selector.length; i++){
13943                         if(rs[selector[i]]){
13944                                 return rs[selector[i]];
13945                         }
13946                 }
13947                 return null;
13948         },
13949         
13950         
13951         /**
13952     * Updates a rule property
13953     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13954     * @param {String} property The css property
13955     * @param {String} value The new value for the property
13956     * @return {Boolean} true If a rule was found and updated
13957     */
13958    updateRule : function(selector, property, value){
13959                 if(!(selector instanceof Array)){
13960                         var rule = this.getRule(selector);
13961                         if(rule){
13962                                 rule.style[property.replace(camelRe, camelFn)] = value;
13963                                 return true;
13964                         }
13965                 }else{
13966                         for(var i = 0; i < selector.length; i++){
13967                                 if(this.updateRule(selector[i], property, value)){
13968                                         return true;
13969                                 }
13970                         }
13971                 }
13972                 return false;
13973         }
13974    };   
13975 }();/*
13976  * Based on:
13977  * Ext JS Library 1.1.1
13978  * Copyright(c) 2006-2007, Ext JS, LLC.
13979  *
13980  * Originally Released Under LGPL - original licence link has changed is not relivant.
13981  *
13982  * Fork - LGPL
13983  * <script type="text/javascript">
13984  */
13985
13986  
13987
13988 /**
13989  * @class Roo.util.ClickRepeater
13990  * @extends Roo.util.Observable
13991  * 
13992  * A wrapper class which can be applied to any element. Fires a "click" event while the
13993  * mouse is pressed. The interval between firings may be specified in the config but
13994  * defaults to 10 milliseconds.
13995  * 
13996  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13997  * 
13998  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13999  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14000  * Similar to an autorepeat key delay.
14001  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14002  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14003  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14004  *           "interval" and "delay" are ignored. "immediate" is honored.
14005  * @cfg {Boolean} preventDefault True to prevent the default click event
14006  * @cfg {Boolean} stopDefault True to stop the default click event
14007  * 
14008  * @history
14009  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14010  *     2007-02-02 jvs Renamed to ClickRepeater
14011  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14012  *
14013  *  @constructor
14014  * @param {String/HTMLElement/Element} el The element to listen on
14015  * @param {Object} config
14016  **/
14017 Roo.util.ClickRepeater = function(el, config)
14018 {
14019     this.el = Roo.get(el);
14020     this.el.unselectable();
14021
14022     Roo.apply(this, config);
14023
14024     this.addEvents({
14025     /**
14026      * @event mousedown
14027      * Fires when the mouse button is depressed.
14028      * @param {Roo.util.ClickRepeater} this
14029      */
14030         "mousedown" : true,
14031     /**
14032      * @event click
14033      * Fires on a specified interval during the time the element is pressed.
14034      * @param {Roo.util.ClickRepeater} this
14035      */
14036         "click" : true,
14037     /**
14038      * @event mouseup
14039      * Fires when the mouse key is released.
14040      * @param {Roo.util.ClickRepeater} this
14041      */
14042         "mouseup" : true
14043     });
14044
14045     this.el.on("mousedown", this.handleMouseDown, this);
14046     if(this.preventDefault || this.stopDefault){
14047         this.el.on("click", function(e){
14048             if(this.preventDefault){
14049                 e.preventDefault();
14050             }
14051             if(this.stopDefault){
14052                 e.stopEvent();
14053             }
14054         }, this);
14055     }
14056
14057     // allow inline handler
14058     if(this.handler){
14059         this.on("click", this.handler,  this.scope || this);
14060     }
14061
14062     Roo.util.ClickRepeater.superclass.constructor.call(this);
14063 };
14064
14065 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14066     interval : 20,
14067     delay: 250,
14068     preventDefault : true,
14069     stopDefault : false,
14070     timer : 0,
14071
14072     // private
14073     handleMouseDown : function(){
14074         clearTimeout(this.timer);
14075         this.el.blur();
14076         if(this.pressClass){
14077             this.el.addClass(this.pressClass);
14078         }
14079         this.mousedownTime = new Date();
14080
14081         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14082         this.el.on("mouseout", this.handleMouseOut, this);
14083
14084         this.fireEvent("mousedown", this);
14085         this.fireEvent("click", this);
14086         
14087         this.timer = this.click.defer(this.delay || this.interval, this);
14088     },
14089
14090     // private
14091     click : function(){
14092         this.fireEvent("click", this);
14093         this.timer = this.click.defer(this.getInterval(), this);
14094     },
14095
14096     // private
14097     getInterval: function(){
14098         if(!this.accelerate){
14099             return this.interval;
14100         }
14101         var pressTime = this.mousedownTime.getElapsed();
14102         if(pressTime < 500){
14103             return 400;
14104         }else if(pressTime < 1700){
14105             return 320;
14106         }else if(pressTime < 2600){
14107             return 250;
14108         }else if(pressTime < 3500){
14109             return 180;
14110         }else if(pressTime < 4400){
14111             return 140;
14112         }else if(pressTime < 5300){
14113             return 80;
14114         }else if(pressTime < 6200){
14115             return 50;
14116         }else{
14117             return 10;
14118         }
14119     },
14120
14121     // private
14122     handleMouseOut : function(){
14123         clearTimeout(this.timer);
14124         if(this.pressClass){
14125             this.el.removeClass(this.pressClass);
14126         }
14127         this.el.on("mouseover", this.handleMouseReturn, this);
14128     },
14129
14130     // private
14131     handleMouseReturn : function(){
14132         this.el.un("mouseover", this.handleMouseReturn);
14133         if(this.pressClass){
14134             this.el.addClass(this.pressClass);
14135         }
14136         this.click();
14137     },
14138
14139     // private
14140     handleMouseUp : function(){
14141         clearTimeout(this.timer);
14142         this.el.un("mouseover", this.handleMouseReturn);
14143         this.el.un("mouseout", this.handleMouseOut);
14144         Roo.get(document).un("mouseup", this.handleMouseUp);
14145         this.el.removeClass(this.pressClass);
14146         this.fireEvent("mouseup", this);
14147     }
14148 });/*
14149  * Based on:
14150  * Ext JS Library 1.1.1
14151  * Copyright(c) 2006-2007, Ext JS, LLC.
14152  *
14153  * Originally Released Under LGPL - original licence link has changed is not relivant.
14154  *
14155  * Fork - LGPL
14156  * <script type="text/javascript">
14157  */
14158
14159  
14160 /**
14161  * @class Roo.KeyNav
14162  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14163  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14164  * way to implement custom navigation schemes for any UI component.</p>
14165  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14166  * pageUp, pageDown, del, home, end.  Usage:</p>
14167  <pre><code>
14168 var nav = new Roo.KeyNav("my-element", {
14169     "left" : function(e){
14170         this.moveLeft(e.ctrlKey);
14171     },
14172     "right" : function(e){
14173         this.moveRight(e.ctrlKey);
14174     },
14175     "enter" : function(e){
14176         this.save();
14177     },
14178     scope : this
14179 });
14180 </code></pre>
14181  * @constructor
14182  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14183  * @param {Object} config The config
14184  */
14185 Roo.KeyNav = function(el, config){
14186     this.el = Roo.get(el);
14187     Roo.apply(this, config);
14188     if(!this.disabled){
14189         this.disabled = true;
14190         this.enable();
14191     }
14192 };
14193
14194 Roo.KeyNav.prototype = {
14195     /**
14196      * @cfg {Boolean} disabled
14197      * True to disable this KeyNav instance (defaults to false)
14198      */
14199     disabled : false,
14200     /**
14201      * @cfg {String} defaultEventAction
14202      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14203      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14204      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14205      */
14206     defaultEventAction: "stopEvent",
14207     /**
14208      * @cfg {Boolean} forceKeyDown
14209      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14210      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14211      * handle keydown instead of keypress.
14212      */
14213     forceKeyDown : false,
14214
14215     // private
14216     prepareEvent : function(e){
14217         var k = e.getKey();
14218         var h = this.keyToHandler[k];
14219         //if(h && this[h]){
14220         //    e.stopPropagation();
14221         //}
14222         if(Roo.isSafari && h && k >= 37 && k <= 40){
14223             e.stopEvent();
14224         }
14225     },
14226
14227     // private
14228     relay : function(e){
14229         var k = e.getKey();
14230         var h = this.keyToHandler[k];
14231         if(h && this[h]){
14232             if(this.doRelay(e, this[h], h) !== true){
14233                 e[this.defaultEventAction]();
14234             }
14235         }
14236     },
14237
14238     // private
14239     doRelay : function(e, h, hname){
14240         return h.call(this.scope || this, e);
14241     },
14242
14243     // possible handlers
14244     enter : false,
14245     left : false,
14246     right : false,
14247     up : false,
14248     down : false,
14249     tab : false,
14250     esc : false,
14251     pageUp : false,
14252     pageDown : false,
14253     del : false,
14254     home : false,
14255     end : false,
14256
14257     // quick lookup hash
14258     keyToHandler : {
14259         37 : "left",
14260         39 : "right",
14261         38 : "up",
14262         40 : "down",
14263         33 : "pageUp",
14264         34 : "pageDown",
14265         46 : "del",
14266         36 : "home",
14267         35 : "end",
14268         13 : "enter",
14269         27 : "esc",
14270         9  : "tab"
14271     },
14272
14273         /**
14274          * Enable this KeyNav
14275          */
14276         enable: function(){
14277                 if(this.disabled){
14278             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14279             // the EventObject will normalize Safari automatically
14280             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14281                 this.el.on("keydown", this.relay,  this);
14282             }else{
14283                 this.el.on("keydown", this.prepareEvent,  this);
14284                 this.el.on("keypress", this.relay,  this);
14285             }
14286                     this.disabled = false;
14287                 }
14288         },
14289
14290         /**
14291          * Disable this KeyNav
14292          */
14293         disable: function(){
14294                 if(!this.disabled){
14295                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14296                 this.el.un("keydown", this.relay);
14297             }else{
14298                 this.el.un("keydown", this.prepareEvent);
14299                 this.el.un("keypress", this.relay);
14300             }
14301                     this.disabled = true;
14302                 }
14303         }
14304 };/*
14305  * Based on:
14306  * Ext JS Library 1.1.1
14307  * Copyright(c) 2006-2007, Ext JS, LLC.
14308  *
14309  * Originally Released Under LGPL - original licence link has changed is not relivant.
14310  *
14311  * Fork - LGPL
14312  * <script type="text/javascript">
14313  */
14314
14315  
14316 /**
14317  * @class Roo.KeyMap
14318  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14319  * The constructor accepts the same config object as defined by {@link #addBinding}.
14320  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14321  * combination it will call the function with this signature (if the match is a multi-key
14322  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14323  * A KeyMap can also handle a string representation of keys.<br />
14324  * Usage:
14325  <pre><code>
14326 // map one key by key code
14327 var map = new Roo.KeyMap("my-element", {
14328     key: 13, // or Roo.EventObject.ENTER
14329     fn: myHandler,
14330     scope: myObject
14331 });
14332
14333 // map multiple keys to one action by string
14334 var map = new Roo.KeyMap("my-element", {
14335     key: "a\r\n\t",
14336     fn: myHandler,
14337     scope: myObject
14338 });
14339
14340 // map multiple keys to multiple actions by strings and array of codes
14341 var map = new Roo.KeyMap("my-element", [
14342     {
14343         key: [10,13],
14344         fn: function(){ alert("Return was pressed"); }
14345     }, {
14346         key: "abc",
14347         fn: function(){ alert('a, b or c was pressed'); }
14348     }, {
14349         key: "\t",
14350         ctrl:true,
14351         shift:true,
14352         fn: function(){ alert('Control + shift + tab was pressed.'); }
14353     }
14354 ]);
14355 </code></pre>
14356  * <b>Note: A KeyMap starts enabled</b>
14357  * @constructor
14358  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14359  * @param {Object} config The config (see {@link #addBinding})
14360  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14361  */
14362 Roo.KeyMap = function(el, config, eventName){
14363     this.el  = Roo.get(el);
14364     this.eventName = eventName || "keydown";
14365     this.bindings = [];
14366     if(config){
14367         this.addBinding(config);
14368     }
14369     this.enable();
14370 };
14371
14372 Roo.KeyMap.prototype = {
14373     /**
14374      * True to stop the event from bubbling and prevent the default browser action if the
14375      * key was handled by the KeyMap (defaults to false)
14376      * @type Boolean
14377      */
14378     stopEvent : false,
14379
14380     /**
14381      * Add a new binding to this KeyMap. The following config object properties are supported:
14382      * <pre>
14383 Property    Type             Description
14384 ----------  ---------------  ----------------------------------------------------------------------
14385 key         String/Array     A single keycode or an array of keycodes to handle
14386 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14387 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14388 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14389 fn          Function         The function to call when KeyMap finds the expected key combination
14390 scope       Object           The scope of the callback function
14391 </pre>
14392      *
14393      * Usage:
14394      * <pre><code>
14395 // Create a KeyMap
14396 var map = new Roo.KeyMap(document, {
14397     key: Roo.EventObject.ENTER,
14398     fn: handleKey,
14399     scope: this
14400 });
14401
14402 //Add a new binding to the existing KeyMap later
14403 map.addBinding({
14404     key: 'abc',
14405     shift: true,
14406     fn: handleKey,
14407     scope: this
14408 });
14409 </code></pre>
14410      * @param {Object/Array} config A single KeyMap config or an array of configs
14411      */
14412         addBinding : function(config){
14413         if(config instanceof Array){
14414             for(var i = 0, len = config.length; i < len; i++){
14415                 this.addBinding(config[i]);
14416             }
14417             return;
14418         }
14419         var keyCode = config.key,
14420             shift = config.shift, 
14421             ctrl = config.ctrl, 
14422             alt = config.alt,
14423             fn = config.fn,
14424             scope = config.scope;
14425         if(typeof keyCode == "string"){
14426             var ks = [];
14427             var keyString = keyCode.toUpperCase();
14428             for(var j = 0, len = keyString.length; j < len; j++){
14429                 ks.push(keyString.charCodeAt(j));
14430             }
14431             keyCode = ks;
14432         }
14433         var keyArray = keyCode instanceof Array;
14434         var handler = function(e){
14435             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14436                 var k = e.getKey();
14437                 if(keyArray){
14438                     for(var i = 0, len = keyCode.length; i < len; i++){
14439                         if(keyCode[i] == k){
14440                           if(this.stopEvent){
14441                               e.stopEvent();
14442                           }
14443                           fn.call(scope || window, k, e);
14444                           return;
14445                         }
14446                     }
14447                 }else{
14448                     if(k == keyCode){
14449                         if(this.stopEvent){
14450                            e.stopEvent();
14451                         }
14452                         fn.call(scope || window, k, e);
14453                     }
14454                 }
14455             }
14456         };
14457         this.bindings.push(handler);  
14458         },
14459
14460     /**
14461      * Shorthand for adding a single key listener
14462      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14463      * following options:
14464      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14465      * @param {Function} fn The function to call
14466      * @param {Object} scope (optional) The scope of the function
14467      */
14468     on : function(key, fn, scope){
14469         var keyCode, shift, ctrl, alt;
14470         if(typeof key == "object" && !(key instanceof Array)){
14471             keyCode = key.key;
14472             shift = key.shift;
14473             ctrl = key.ctrl;
14474             alt = key.alt;
14475         }else{
14476             keyCode = key;
14477         }
14478         this.addBinding({
14479             key: keyCode,
14480             shift: shift,
14481             ctrl: ctrl,
14482             alt: alt,
14483             fn: fn,
14484             scope: scope
14485         })
14486     },
14487
14488     // private
14489     handleKeyDown : function(e){
14490             if(this.enabled){ //just in case
14491             var b = this.bindings;
14492             for(var i = 0, len = b.length; i < len; i++){
14493                 b[i].call(this, e);
14494             }
14495             }
14496         },
14497         
14498         /**
14499          * Returns true if this KeyMap is enabled
14500          * @return {Boolean} 
14501          */
14502         isEnabled : function(){
14503             return this.enabled;  
14504         },
14505         
14506         /**
14507          * Enables this KeyMap
14508          */
14509         enable: function(){
14510                 if(!this.enabled){
14511                     this.el.on(this.eventName, this.handleKeyDown, this);
14512                     this.enabled = true;
14513                 }
14514         },
14515
14516         /**
14517          * Disable this KeyMap
14518          */
14519         disable: function(){
14520                 if(this.enabled){
14521                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14522                     this.enabled = false;
14523                 }
14524         }
14525 };/*
14526  * Based on:
14527  * Ext JS Library 1.1.1
14528  * Copyright(c) 2006-2007, Ext JS, LLC.
14529  *
14530  * Originally Released Under LGPL - original licence link has changed is not relivant.
14531  *
14532  * Fork - LGPL
14533  * <script type="text/javascript">
14534  */
14535
14536  
14537 /**
14538  * @class Roo.util.TextMetrics
14539  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14540  * wide, in pixels, a given block of text will be.
14541  * @singleton
14542  */
14543 Roo.util.TextMetrics = function(){
14544     var shared;
14545     return {
14546         /**
14547          * Measures the size of the specified text
14548          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14549          * that can affect the size of the rendered text
14550          * @param {String} text The text to measure
14551          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14552          * in order to accurately measure the text height
14553          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14554          */
14555         measure : function(el, text, fixedWidth){
14556             if(!shared){
14557                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14558             }
14559             shared.bind(el);
14560             shared.setFixedWidth(fixedWidth || 'auto');
14561             return shared.getSize(text);
14562         },
14563
14564         /**
14565          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14566          * the overhead of multiple calls to initialize the style properties on each measurement.
14567          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14568          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14569          * in order to accurately measure the text height
14570          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14571          */
14572         createInstance : function(el, fixedWidth){
14573             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14574         }
14575     };
14576 }();
14577
14578  
14579
14580 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14581     var ml = new Roo.Element(document.createElement('div'));
14582     document.body.appendChild(ml.dom);
14583     ml.position('absolute');
14584     ml.setLeftTop(-1000, -1000);
14585     ml.hide();
14586
14587     if(fixedWidth){
14588         ml.setWidth(fixedWidth);
14589     }
14590      
14591     var instance = {
14592         /**
14593          * Returns the size of the specified text based on the internal element's style and width properties
14594          * @memberOf Roo.util.TextMetrics.Instance#
14595          * @param {String} text The text to measure
14596          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14597          */
14598         getSize : function(text){
14599             ml.update(text);
14600             var s = ml.getSize();
14601             ml.update('');
14602             return s;
14603         },
14604
14605         /**
14606          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14607          * that can affect the size of the rendered text
14608          * @memberOf Roo.util.TextMetrics.Instance#
14609          * @param {String/HTMLElement} el The element, dom node or id
14610          */
14611         bind : function(el){
14612             ml.setStyle(
14613                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14614             );
14615         },
14616
14617         /**
14618          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14619          * to set a fixed width in order to accurately measure the text height.
14620          * @memberOf Roo.util.TextMetrics.Instance#
14621          * @param {Number} width The width to set on the element
14622          */
14623         setFixedWidth : function(width){
14624             ml.setWidth(width);
14625         },
14626
14627         /**
14628          * Returns the measured width of the specified text
14629          * @memberOf Roo.util.TextMetrics.Instance#
14630          * @param {String} text The text to measure
14631          * @return {Number} width The width in pixels
14632          */
14633         getWidth : function(text){
14634             ml.dom.style.width = 'auto';
14635             return this.getSize(text).width;
14636         },
14637
14638         /**
14639          * Returns the measured height of the specified text.  For multiline text, be sure to call
14640          * {@link #setFixedWidth} if necessary.
14641          * @memberOf Roo.util.TextMetrics.Instance#
14642          * @param {String} text The text to measure
14643          * @return {Number} height The height in pixels
14644          */
14645         getHeight : function(text){
14646             return this.getSize(text).height;
14647         }
14648     };
14649
14650     instance.bind(bindTo);
14651
14652     return instance;
14653 };
14654
14655 // backwards compat
14656 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14657  * Based on:
14658  * Ext JS Library 1.1.1
14659  * Copyright(c) 2006-2007, Ext JS, LLC.
14660  *
14661  * Originally Released Under LGPL - original licence link has changed is not relivant.
14662  *
14663  * Fork - LGPL
14664  * <script type="text/javascript">
14665  */
14666
14667 /**
14668  * @class Roo.state.Provider
14669  * Abstract base class for state provider implementations. This class provides methods
14670  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14671  * Provider interface.
14672  */
14673 Roo.state.Provider = function(){
14674     /**
14675      * @event statechange
14676      * Fires when a state change occurs.
14677      * @param {Provider} this This state provider
14678      * @param {String} key The state key which was changed
14679      * @param {String} value The encoded value for the state
14680      */
14681     this.addEvents({
14682         "statechange": true
14683     });
14684     this.state = {};
14685     Roo.state.Provider.superclass.constructor.call(this);
14686 };
14687 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14688     /**
14689      * Returns the current value for a key
14690      * @param {String} name The key name
14691      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14692      * @return {Mixed} The state data
14693      */
14694     get : function(name, defaultValue){
14695         return typeof this.state[name] == "undefined" ?
14696             defaultValue : this.state[name];
14697     },
14698     
14699     /**
14700      * Clears a value from the state
14701      * @param {String} name The key name
14702      */
14703     clear : function(name){
14704         delete this.state[name];
14705         this.fireEvent("statechange", this, name, null);
14706     },
14707     
14708     /**
14709      * Sets the value for a key
14710      * @param {String} name The key name
14711      * @param {Mixed} value The value to set
14712      */
14713     set : function(name, value){
14714         this.state[name] = value;
14715         this.fireEvent("statechange", this, name, value);
14716     },
14717     
14718     /**
14719      * Decodes a string previously encoded with {@link #encodeValue}.
14720      * @param {String} value The value to decode
14721      * @return {Mixed} The decoded value
14722      */
14723     decodeValue : function(cookie){
14724         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14725         var matches = re.exec(unescape(cookie));
14726         if(!matches || !matches[1]) return; // non state cookie
14727         var type = matches[1];
14728         var v = matches[2];
14729         switch(type){
14730             case "n":
14731                 return parseFloat(v);
14732             case "d":
14733                 return new Date(Date.parse(v));
14734             case "b":
14735                 return (v == "1");
14736             case "a":
14737                 var all = [];
14738                 var values = v.split("^");
14739                 for(var i = 0, len = values.length; i < len; i++){
14740                     all.push(this.decodeValue(values[i]));
14741                 }
14742                 return all;
14743            case "o":
14744                 var all = {};
14745                 var values = v.split("^");
14746                 for(var i = 0, len = values.length; i < len; i++){
14747                     var kv = values[i].split("=");
14748                     all[kv[0]] = this.decodeValue(kv[1]);
14749                 }
14750                 return all;
14751            default:
14752                 return v;
14753         }
14754     },
14755     
14756     /**
14757      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14758      * @param {Mixed} value The value to encode
14759      * @return {String} The encoded value
14760      */
14761     encodeValue : function(v){
14762         var enc;
14763         if(typeof v == "number"){
14764             enc = "n:" + v;
14765         }else if(typeof v == "boolean"){
14766             enc = "b:" + (v ? "1" : "0");
14767         }else if(v instanceof Date){
14768             enc = "d:" + v.toGMTString();
14769         }else if(v instanceof Array){
14770             var flat = "";
14771             for(var i = 0, len = v.length; i < len; i++){
14772                 flat += this.encodeValue(v[i]);
14773                 if(i != len-1) flat += "^";
14774             }
14775             enc = "a:" + flat;
14776         }else if(typeof v == "object"){
14777             var flat = "";
14778             for(var key in v){
14779                 if(typeof v[key] != "function"){
14780                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14781                 }
14782             }
14783             enc = "o:" + flat.substring(0, flat.length-1);
14784         }else{
14785             enc = "s:" + v;
14786         }
14787         return escape(enc);        
14788     }
14789 });
14790
14791 /*
14792  * Based on:
14793  * Ext JS Library 1.1.1
14794  * Copyright(c) 2006-2007, Ext JS, LLC.
14795  *
14796  * Originally Released Under LGPL - original licence link has changed is not relivant.
14797  *
14798  * Fork - LGPL
14799  * <script type="text/javascript">
14800  */
14801 /**
14802  * @class Roo.state.Manager
14803  * This is the global state manager. By default all components that are "state aware" check this class
14804  * for state information if you don't pass them a custom state provider. In order for this class
14805  * to be useful, it must be initialized with a provider when your application initializes.
14806  <pre><code>
14807 // in your initialization function
14808 init : function(){
14809    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14810    ...
14811    // supposed you have a {@link Roo.BorderLayout}
14812    var layout = new Roo.BorderLayout(...);
14813    layout.restoreState();
14814    // or a {Roo.BasicDialog}
14815    var dialog = new Roo.BasicDialog(...);
14816    dialog.restoreState();
14817  </code></pre>
14818  * @singleton
14819  */
14820 Roo.state.Manager = function(){
14821     var provider = new Roo.state.Provider();
14822     
14823     return {
14824         /**
14825          * Configures the default state provider for your application
14826          * @param {Provider} stateProvider The state provider to set
14827          */
14828         setProvider : function(stateProvider){
14829             provider = stateProvider;
14830         },
14831         
14832         /**
14833          * Returns the current value for a key
14834          * @param {String} name The key name
14835          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14836          * @return {Mixed} The state data
14837          */
14838         get : function(key, defaultValue){
14839             return provider.get(key, defaultValue);
14840         },
14841         
14842         /**
14843          * Sets the value for a key
14844          * @param {String} name The key name
14845          * @param {Mixed} value The state data
14846          */
14847          set : function(key, value){
14848             provider.set(key, value);
14849         },
14850         
14851         /**
14852          * Clears a value from the state
14853          * @param {String} name The key name
14854          */
14855         clear : function(key){
14856             provider.clear(key);
14857         },
14858         
14859         /**
14860          * Gets the currently configured state provider
14861          * @return {Provider} The state provider
14862          */
14863         getProvider : function(){
14864             return provider;
14865         }
14866     };
14867 }();
14868 /*
14869  * Based on:
14870  * Ext JS Library 1.1.1
14871  * Copyright(c) 2006-2007, Ext JS, LLC.
14872  *
14873  * Originally Released Under LGPL - original licence link has changed is not relivant.
14874  *
14875  * Fork - LGPL
14876  * <script type="text/javascript">
14877  */
14878 /**
14879  * @class Roo.state.CookieProvider
14880  * @extends Roo.state.Provider
14881  * The default Provider implementation which saves state via cookies.
14882  * <br />Usage:
14883  <pre><code>
14884    var cp = new Roo.state.CookieProvider({
14885        path: "/cgi-bin/",
14886        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14887        domain: "roojs.com"
14888    })
14889    Roo.state.Manager.setProvider(cp);
14890  </code></pre>
14891  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14892  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14893  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14894  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14895  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14896  * domain the page is running on including the 'www' like 'www.roojs.com')
14897  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14898  * @constructor
14899  * Create a new CookieProvider
14900  * @param {Object} config The configuration object
14901  */
14902 Roo.state.CookieProvider = function(config){
14903     Roo.state.CookieProvider.superclass.constructor.call(this);
14904     this.path = "/";
14905     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14906     this.domain = null;
14907     this.secure = false;
14908     Roo.apply(this, config);
14909     this.state = this.readCookies();
14910 };
14911
14912 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14913     // private
14914     set : function(name, value){
14915         if(typeof value == "undefined" || value === null){
14916             this.clear(name);
14917             return;
14918         }
14919         this.setCookie(name, value);
14920         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14921     },
14922
14923     // private
14924     clear : function(name){
14925         this.clearCookie(name);
14926         Roo.state.CookieProvider.superclass.clear.call(this, name);
14927     },
14928
14929     // private
14930     readCookies : function(){
14931         var cookies = {};
14932         var c = document.cookie + ";";
14933         var re = /\s?(.*?)=(.*?);/g;
14934         var matches;
14935         while((matches = re.exec(c)) != null){
14936             var name = matches[1];
14937             var value = matches[2];
14938             if(name && name.substring(0,3) == "ys-"){
14939                 cookies[name.substr(3)] = this.decodeValue(value);
14940             }
14941         }
14942         return cookies;
14943     },
14944
14945     // private
14946     setCookie : function(name, value){
14947         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14948            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14949            ((this.path == null) ? "" : ("; path=" + this.path)) +
14950            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14951            ((this.secure == true) ? "; secure" : "");
14952     },
14953
14954     // private
14955     clearCookie : function(name){
14956         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14957            ((this.path == null) ? "" : ("; path=" + this.path)) +
14958            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14959            ((this.secure == true) ? "; secure" : "");
14960     }
14961 });/*
14962  * Based on:
14963  * Ext JS Library 1.1.1
14964  * Copyright(c) 2006-2007, Ext JS, LLC.
14965  *
14966  * Originally Released Under LGPL - original licence link has changed is not relivant.
14967  *
14968  * Fork - LGPL
14969  * <script type="text/javascript">
14970  */
14971  
14972
14973 /**
14974  * @class Roo.ComponentMgr
14975  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14976  * @singleton
14977  */
14978 Roo.ComponentMgr = function(){
14979     var all = new Roo.util.MixedCollection();
14980
14981     return {
14982         /**
14983          * Registers a component.
14984          * @param {Roo.Component} c The component
14985          */
14986         register : function(c){
14987             all.add(c);
14988         },
14989
14990         /**
14991          * Unregisters a component.
14992          * @param {Roo.Component} c The component
14993          */
14994         unregister : function(c){
14995             all.remove(c);
14996         },
14997
14998         /**
14999          * Returns a component by id
15000          * @param {String} id The component id
15001          */
15002         get : function(id){
15003             return all.get(id);
15004         },
15005
15006         /**
15007          * Registers a function that will be called when a specified component is added to ComponentMgr
15008          * @param {String} id The component id
15009          * @param {Funtction} fn The callback function
15010          * @param {Object} scope The scope of the callback
15011          */
15012         onAvailable : function(id, fn, scope){
15013             all.on("add", function(index, o){
15014                 if(o.id == id){
15015                     fn.call(scope || o, o);
15016                     all.un("add", fn, scope);
15017                 }
15018             });
15019         }
15020     };
15021 }();/*
15022  * Based on:
15023  * Ext JS Library 1.1.1
15024  * Copyright(c) 2006-2007, Ext JS, LLC.
15025  *
15026  * Originally Released Under LGPL - original licence link has changed is not relivant.
15027  *
15028  * Fork - LGPL
15029  * <script type="text/javascript">
15030  */
15031  
15032 /**
15033  * @class Roo.Component
15034  * @extends Roo.util.Observable
15035  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15036  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15037  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15038  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15039  * All visual components (widgets) that require rendering into a layout should subclass Component.
15040  * @constructor
15041  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15042  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15043  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15044  */
15045 Roo.Component = function(config){
15046     config = config || {};
15047     if(config.tagName || config.dom || typeof config == "string"){ // element object
15048         config = {el: config, id: config.id || config};
15049     }
15050     this.initialConfig = config;
15051
15052     Roo.apply(this, config);
15053     this.addEvents({
15054         /**
15055          * @event disable
15056          * Fires after the component is disabled.
15057              * @param {Roo.Component} this
15058              */
15059         disable : true,
15060         /**
15061          * @event enable
15062          * Fires after the component is enabled.
15063              * @param {Roo.Component} this
15064              */
15065         enable : true,
15066         /**
15067          * @event beforeshow
15068          * Fires before the component is shown.  Return false to stop the show.
15069              * @param {Roo.Component} this
15070              */
15071         beforeshow : true,
15072         /**
15073          * @event show
15074          * Fires after the component is shown.
15075              * @param {Roo.Component} this
15076              */
15077         show : true,
15078         /**
15079          * @event beforehide
15080          * Fires before the component is hidden. Return false to stop the hide.
15081              * @param {Roo.Component} this
15082              */
15083         beforehide : true,
15084         /**
15085          * @event hide
15086          * Fires after the component is hidden.
15087              * @param {Roo.Component} this
15088              */
15089         hide : true,
15090         /**
15091          * @event beforerender
15092          * Fires before the component is rendered. Return false to stop the render.
15093              * @param {Roo.Component} this
15094              */
15095         beforerender : true,
15096         /**
15097          * @event render
15098          * Fires after the component is rendered.
15099              * @param {Roo.Component} this
15100              */
15101         render : true,
15102         /**
15103          * @event beforedestroy
15104          * Fires before the component is destroyed. Return false to stop the destroy.
15105              * @param {Roo.Component} this
15106              */
15107         beforedestroy : true,
15108         /**
15109          * @event destroy
15110          * Fires after the component is destroyed.
15111              * @param {Roo.Component} this
15112              */
15113         destroy : true
15114     });
15115     if(!this.id){
15116         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15117     }
15118     Roo.ComponentMgr.register(this);
15119     Roo.Component.superclass.constructor.call(this);
15120     this.initComponent();
15121     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15122         this.render(this.renderTo);
15123         delete this.renderTo;
15124     }
15125 };
15126
15127 /** @private */
15128 Roo.Component.AUTO_ID = 1000;
15129
15130 Roo.extend(Roo.Component, Roo.util.Observable, {
15131     /**
15132      * @scope Roo.Component.prototype
15133      * @type {Boolean}
15134      * true if this component is hidden. Read-only.
15135      */
15136     hidden : false,
15137     /**
15138      * @type {Boolean}
15139      * true if this component is disabled. Read-only.
15140      */
15141     disabled : false,
15142     /**
15143      * @type {Boolean}
15144      * true if this component has been rendered. Read-only.
15145      */
15146     rendered : false,
15147     
15148     /** @cfg {String} disableClass
15149      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15150      */
15151     disabledClass : "x-item-disabled",
15152         /** @cfg {Boolean} allowDomMove
15153          * Whether the component can move the Dom node when rendering (defaults to true).
15154          */
15155     allowDomMove : true,
15156     /** @cfg {String} hideMode
15157      * How this component should hidden. Supported values are
15158      * "visibility" (css visibility), "offsets" (negative offset position) and
15159      * "display" (css display) - defaults to "display".
15160      */
15161     hideMode: 'display',
15162
15163     /** @private */
15164     ctype : "Roo.Component",
15165
15166     /**
15167      * @cfg {String} actionMode 
15168      * which property holds the element that used for  hide() / show() / disable() / enable()
15169      * default is 'el' 
15170      */
15171     actionMode : "el",
15172
15173     /** @private */
15174     getActionEl : function(){
15175         return this[this.actionMode];
15176     },
15177
15178     initComponent : Roo.emptyFn,
15179     /**
15180      * If this is a lazy rendering component, render it to its container element.
15181      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15182      */
15183     render : function(container, position){
15184         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15185             if(!container && this.el){
15186                 this.el = Roo.get(this.el);
15187                 container = this.el.dom.parentNode;
15188                 this.allowDomMove = false;
15189             }
15190             this.container = Roo.get(container);
15191             this.rendered = true;
15192             if(position !== undefined){
15193                 if(typeof position == 'number'){
15194                     position = this.container.dom.childNodes[position];
15195                 }else{
15196                     position = Roo.getDom(position);
15197                 }
15198             }
15199             this.onRender(this.container, position || null);
15200             if(this.cls){
15201                 this.el.addClass(this.cls);
15202                 delete this.cls;
15203             }
15204             if(this.style){
15205                 this.el.applyStyles(this.style);
15206                 delete this.style;
15207             }
15208             this.fireEvent("render", this);
15209             this.afterRender(this.container);
15210             if(this.hidden){
15211                 this.hide();
15212             }
15213             if(this.disabled){
15214                 this.disable();
15215             }
15216         }
15217         return this;
15218     },
15219
15220     /** @private */
15221     // default function is not really useful
15222     onRender : function(ct, position){
15223         if(this.el){
15224             this.el = Roo.get(this.el);
15225             if(this.allowDomMove !== false){
15226                 ct.dom.insertBefore(this.el.dom, position);
15227             }
15228         }
15229     },
15230
15231     /** @private */
15232     getAutoCreate : function(){
15233         var cfg = typeof this.autoCreate == "object" ?
15234                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15235         if(this.id && !cfg.id){
15236             cfg.id = this.id;
15237         }
15238         return cfg;
15239     },
15240
15241     /** @private */
15242     afterRender : Roo.emptyFn,
15243
15244     /**
15245      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15246      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15247      */
15248     destroy : function(){
15249         if(this.fireEvent("beforedestroy", this) !== false){
15250             this.purgeListeners();
15251             this.beforeDestroy();
15252             if(this.rendered){
15253                 this.el.removeAllListeners();
15254                 this.el.remove();
15255                 if(this.actionMode == "container"){
15256                     this.container.remove();
15257                 }
15258             }
15259             this.onDestroy();
15260             Roo.ComponentMgr.unregister(this);
15261             this.fireEvent("destroy", this);
15262         }
15263     },
15264
15265         /** @private */
15266     beforeDestroy : function(){
15267
15268     },
15269
15270         /** @private */
15271         onDestroy : function(){
15272
15273     },
15274
15275     /**
15276      * Returns the underlying {@link Roo.Element}.
15277      * @return {Roo.Element} The element
15278      */
15279     getEl : function(){
15280         return this.el;
15281     },
15282
15283     /**
15284      * Returns the id of this component.
15285      * @return {String}
15286      */
15287     getId : function(){
15288         return this.id;
15289     },
15290
15291     /**
15292      * Try to focus this component.
15293      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15294      * @return {Roo.Component} this
15295      */
15296     focus : function(selectText){
15297         if(this.rendered){
15298             this.el.focus();
15299             if(selectText === true){
15300                 this.el.dom.select();
15301             }
15302         }
15303         return this;
15304     },
15305
15306     /** @private */
15307     blur : function(){
15308         if(this.rendered){
15309             this.el.blur();
15310         }
15311         return this;
15312     },
15313
15314     /**
15315      * Disable this component.
15316      * @return {Roo.Component} this
15317      */
15318     disable : function(){
15319         if(this.rendered){
15320             this.onDisable();
15321         }
15322         this.disabled = true;
15323         this.fireEvent("disable", this);
15324         return this;
15325     },
15326
15327         // private
15328     onDisable : function(){
15329         this.getActionEl().addClass(this.disabledClass);
15330         this.el.dom.disabled = true;
15331     },
15332
15333     /**
15334      * Enable this component.
15335      * @return {Roo.Component} this
15336      */
15337     enable : function(){
15338         if(this.rendered){
15339             this.onEnable();
15340         }
15341         this.disabled = false;
15342         this.fireEvent("enable", this);
15343         return this;
15344     },
15345
15346         // private
15347     onEnable : function(){
15348         this.getActionEl().removeClass(this.disabledClass);
15349         this.el.dom.disabled = false;
15350     },
15351
15352     /**
15353      * Convenience function for setting disabled/enabled by boolean.
15354      * @param {Boolean} disabled
15355      */
15356     setDisabled : function(disabled){
15357         this[disabled ? "disable" : "enable"]();
15358     },
15359
15360     /**
15361      * Show this component.
15362      * @return {Roo.Component} this
15363      */
15364     show: function(){
15365         if(this.fireEvent("beforeshow", this) !== false){
15366             this.hidden = false;
15367             if(this.rendered){
15368                 this.onShow();
15369             }
15370             this.fireEvent("show", this);
15371         }
15372         return this;
15373     },
15374
15375     // private
15376     onShow : function(){
15377         var ae = this.getActionEl();
15378         if(this.hideMode == 'visibility'){
15379             ae.dom.style.visibility = "visible";
15380         }else if(this.hideMode == 'offsets'){
15381             ae.removeClass('x-hidden');
15382         }else{
15383             ae.dom.style.display = "";
15384         }
15385     },
15386
15387     /**
15388      * Hide this component.
15389      * @return {Roo.Component} this
15390      */
15391     hide: function(){
15392         if(this.fireEvent("beforehide", this) !== false){
15393             this.hidden = true;
15394             if(this.rendered){
15395                 this.onHide();
15396             }
15397             this.fireEvent("hide", this);
15398         }
15399         return this;
15400     },
15401
15402     // private
15403     onHide : function(){
15404         var ae = this.getActionEl();
15405         if(this.hideMode == 'visibility'){
15406             ae.dom.style.visibility = "hidden";
15407         }else if(this.hideMode == 'offsets'){
15408             ae.addClass('x-hidden');
15409         }else{
15410             ae.dom.style.display = "none";
15411         }
15412     },
15413
15414     /**
15415      * Convenience function to hide or show this component by boolean.
15416      * @param {Boolean} visible True to show, false to hide
15417      * @return {Roo.Component} this
15418      */
15419     setVisible: function(visible){
15420         if(visible) {
15421             this.show();
15422         }else{
15423             this.hide();
15424         }
15425         return this;
15426     },
15427
15428     /**
15429      * Returns true if this component is visible.
15430      */
15431     isVisible : function(){
15432         return this.getActionEl().isVisible();
15433     },
15434
15435     cloneConfig : function(overrides){
15436         overrides = overrides || {};
15437         var id = overrides.id || Roo.id();
15438         var cfg = Roo.applyIf(overrides, this.initialConfig);
15439         cfg.id = id; // prevent dup id
15440         return new this.constructor(cfg);
15441     }
15442 });/*
15443  * Based on:
15444  * Ext JS Library 1.1.1
15445  * Copyright(c) 2006-2007, Ext JS, LLC.
15446  *
15447  * Originally Released Under LGPL - original licence link has changed is not relivant.
15448  *
15449  * Fork - LGPL
15450  * <script type="text/javascript">
15451  */
15452
15453 /**
15454  * @class Roo.BoxComponent
15455  * @extends Roo.Component
15456  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15457  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15458  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15459  * layout containers.
15460  * @constructor
15461  * @param {Roo.Element/String/Object} config The configuration options.
15462  */
15463 Roo.BoxComponent = function(config){
15464     Roo.Component.call(this, config);
15465     this.addEvents({
15466         /**
15467          * @event resize
15468          * Fires after the component is resized.
15469              * @param {Roo.Component} this
15470              * @param {Number} adjWidth The box-adjusted width that was set
15471              * @param {Number} adjHeight The box-adjusted height that was set
15472              * @param {Number} rawWidth The width that was originally specified
15473              * @param {Number} rawHeight The height that was originally specified
15474              */
15475         resize : true,
15476         /**
15477          * @event move
15478          * Fires after the component is moved.
15479              * @param {Roo.Component} this
15480              * @param {Number} x The new x position
15481              * @param {Number} y The new y position
15482              */
15483         move : true
15484     });
15485 };
15486
15487 Roo.extend(Roo.BoxComponent, Roo.Component, {
15488     // private, set in afterRender to signify that the component has been rendered
15489     boxReady : false,
15490     // private, used to defer height settings to subclasses
15491     deferHeight: false,
15492     /** @cfg {Number} width
15493      * width (optional) size of component
15494      */
15495      /** @cfg {Number} height
15496      * height (optional) size of component
15497      */
15498      
15499     /**
15500      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15501      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15502      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15503      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15504      * @return {Roo.BoxComponent} this
15505      */
15506     setSize : function(w, h){
15507         // support for standard size objects
15508         if(typeof w == 'object'){
15509             h = w.height;
15510             w = w.width;
15511         }
15512         // not rendered
15513         if(!this.boxReady){
15514             this.width = w;
15515             this.height = h;
15516             return this;
15517         }
15518
15519         // prevent recalcs when not needed
15520         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15521             return this;
15522         }
15523         this.lastSize = {width: w, height: h};
15524
15525         var adj = this.adjustSize(w, h);
15526         var aw = adj.width, ah = adj.height;
15527         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15528             var rz = this.getResizeEl();
15529             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15530                 rz.setSize(aw, ah);
15531             }else if(!this.deferHeight && ah !== undefined){
15532                 rz.setHeight(ah);
15533             }else if(aw !== undefined){
15534                 rz.setWidth(aw);
15535             }
15536             this.onResize(aw, ah, w, h);
15537             this.fireEvent('resize', this, aw, ah, w, h);
15538         }
15539         return this;
15540     },
15541
15542     /**
15543      * Gets the current size of the component's underlying element.
15544      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15545      */
15546     getSize : function(){
15547         return this.el.getSize();
15548     },
15549
15550     /**
15551      * Gets the current XY position of the component's underlying element.
15552      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15553      * @return {Array} The XY position of the element (e.g., [100, 200])
15554      */
15555     getPosition : function(local){
15556         if(local === true){
15557             return [this.el.getLeft(true), this.el.getTop(true)];
15558         }
15559         return this.xy || this.el.getXY();
15560     },
15561
15562     /**
15563      * Gets the current box measurements of the component's underlying element.
15564      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15565      * @returns {Object} box An object in the format {x, y, width, height}
15566      */
15567     getBox : function(local){
15568         var s = this.el.getSize();
15569         if(local){
15570             s.x = this.el.getLeft(true);
15571             s.y = this.el.getTop(true);
15572         }else{
15573             var xy = this.xy || this.el.getXY();
15574             s.x = xy[0];
15575             s.y = xy[1];
15576         }
15577         return s;
15578     },
15579
15580     /**
15581      * Sets the current box measurements of the component's underlying element.
15582      * @param {Object} box An object in the format {x, y, width, height}
15583      * @returns {Roo.BoxComponent} this
15584      */
15585     updateBox : function(box){
15586         this.setSize(box.width, box.height);
15587         this.setPagePosition(box.x, box.y);
15588         return this;
15589     },
15590
15591     // protected
15592     getResizeEl : function(){
15593         return this.resizeEl || this.el;
15594     },
15595
15596     // protected
15597     getPositionEl : function(){
15598         return this.positionEl || this.el;
15599     },
15600
15601     /**
15602      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15603      * This method fires the move event.
15604      * @param {Number} left The new left
15605      * @param {Number} top The new top
15606      * @returns {Roo.BoxComponent} this
15607      */
15608     setPosition : function(x, y){
15609         this.x = x;
15610         this.y = y;
15611         if(!this.boxReady){
15612             return this;
15613         }
15614         var adj = this.adjustPosition(x, y);
15615         var ax = adj.x, ay = adj.y;
15616
15617         var el = this.getPositionEl();
15618         if(ax !== undefined || ay !== undefined){
15619             if(ax !== undefined && ay !== undefined){
15620                 el.setLeftTop(ax, ay);
15621             }else if(ax !== undefined){
15622                 el.setLeft(ax);
15623             }else if(ay !== undefined){
15624                 el.setTop(ay);
15625             }
15626             this.onPosition(ax, ay);
15627             this.fireEvent('move', this, ax, ay);
15628         }
15629         return this;
15630     },
15631
15632     /**
15633      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15634      * This method fires the move event.
15635      * @param {Number} x The new x position
15636      * @param {Number} y The new y position
15637      * @returns {Roo.BoxComponent} this
15638      */
15639     setPagePosition : function(x, y){
15640         this.pageX = x;
15641         this.pageY = y;
15642         if(!this.boxReady){
15643             return;
15644         }
15645         if(x === undefined || y === undefined){ // cannot translate undefined points
15646             return;
15647         }
15648         var p = this.el.translatePoints(x, y);
15649         this.setPosition(p.left, p.top);
15650         return this;
15651     },
15652
15653     // private
15654     onRender : function(ct, position){
15655         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15656         if(this.resizeEl){
15657             this.resizeEl = Roo.get(this.resizeEl);
15658         }
15659         if(this.positionEl){
15660             this.positionEl = Roo.get(this.positionEl);
15661         }
15662     },
15663
15664     // private
15665     afterRender : function(){
15666         Roo.BoxComponent.superclass.afterRender.call(this);
15667         this.boxReady = true;
15668         this.setSize(this.width, this.height);
15669         if(this.x || this.y){
15670             this.setPosition(this.x, this.y);
15671         }
15672         if(this.pageX || this.pageY){
15673             this.setPagePosition(this.pageX, this.pageY);
15674         }
15675     },
15676
15677     /**
15678      * Force the component's size to recalculate based on the underlying element's current height and width.
15679      * @returns {Roo.BoxComponent} this
15680      */
15681     syncSize : function(){
15682         delete this.lastSize;
15683         this.setSize(this.el.getWidth(), this.el.getHeight());
15684         return this;
15685     },
15686
15687     /**
15688      * Called after the component is resized, this method is empty by default but can be implemented by any
15689      * subclass that needs to perform custom logic after a resize occurs.
15690      * @param {Number} adjWidth The box-adjusted width that was set
15691      * @param {Number} adjHeight The box-adjusted height that was set
15692      * @param {Number} rawWidth The width that was originally specified
15693      * @param {Number} rawHeight The height that was originally specified
15694      */
15695     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15696
15697     },
15698
15699     /**
15700      * Called after the component is moved, this method is empty by default but can be implemented by any
15701      * subclass that needs to perform custom logic after a move occurs.
15702      * @param {Number} x The new x position
15703      * @param {Number} y The new y position
15704      */
15705     onPosition : function(x, y){
15706
15707     },
15708
15709     // private
15710     adjustSize : function(w, h){
15711         if(this.autoWidth){
15712             w = 'auto';
15713         }
15714         if(this.autoHeight){
15715             h = 'auto';
15716         }
15717         return {width : w, height: h};
15718     },
15719
15720     // private
15721     adjustPosition : function(x, y){
15722         return {x : x, y: y};
15723     }
15724 });/*
15725  * Original code for Roojs - LGPL
15726  * <script type="text/javascript">
15727  */
15728  
15729 /**
15730  * @class Roo.XComponent
15731  * A delayed Element creator...
15732  * Or a way to group chunks of interface together.
15733  * 
15734  * Mypart.xyx = new Roo.XComponent({
15735
15736     parent : 'Mypart.xyz', // empty == document.element.!!
15737     order : '001',
15738     name : 'xxxx'
15739     region : 'xxxx'
15740     disabled : function() {} 
15741      
15742     tree : function() { // return an tree of xtype declared components
15743         var MODULE = this;
15744         return 
15745         {
15746             xtype : 'NestedLayoutPanel',
15747             // technicall
15748         }
15749      ]
15750  *})
15751  *
15752  *
15753  * It can be used to build a big heiracy, with parent etc.
15754  * or you can just use this to render a single compoent to a dom element
15755  * MYPART.render(Roo.Element | String(id) | dom_element )
15756  * 
15757  * @extends Roo.util.Observable
15758  * @constructor
15759  * @param cfg {Object} configuration of component
15760  * 
15761  */
15762 Roo.XComponent = function(cfg) {
15763     Roo.apply(this, cfg);
15764     this.addEvents({ 
15765         /**
15766              * @event built
15767              * Fires when this the componnt is built
15768              * @param {Roo.XComponent} c the component
15769              */
15770         'built' : true
15771         
15772     });
15773     this.region = this.region || 'center'; // default..
15774     Roo.XComponent.register(this);
15775     this.modules = false;
15776     this.el = false; // where the layout goes..
15777     
15778     
15779 }
15780 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15781     /**
15782      * @property el
15783      * The created element (with Roo.factory())
15784      * @type {Roo.Layout}
15785      */
15786     el  : false,
15787     
15788     /**
15789      * @property el
15790      * for BC  - use el in new code
15791      * @type {Roo.Layout}
15792      */
15793     panel : false,
15794     
15795     /**
15796      * @property layout
15797      * for BC  - use el in new code
15798      * @type {Roo.Layout}
15799      */
15800     layout : false,
15801     
15802      /**
15803      * @cfg {Function|boolean} disabled
15804      * If this module is disabled by some rule, return true from the funtion
15805      */
15806     disabled : false,
15807     
15808     /**
15809      * @cfg {String} parent 
15810      * Name of parent element which it get xtype added to..
15811      */
15812     parent: false,
15813     
15814     /**
15815      * @cfg {String} order
15816      * Used to set the order in which elements are created (usefull for multiple tabs)
15817      */
15818     
15819     order : false,
15820     /**
15821      * @cfg {String} name
15822      * String to display while loading.
15823      */
15824     name : false,
15825     /**
15826      * @cfg {String} region
15827      * Region to render component to (defaults to center)
15828      */
15829     region : 'center',
15830     
15831     /**
15832      * @cfg {Array} items
15833      * A single item array - the first element is the root of the tree..
15834      * It's done this way to stay compatible with the Xtype system...
15835      */
15836     items : false,
15837     
15838     /**
15839      * @property _tree
15840      * The method that retuns the tree of parts that make up this compoennt 
15841      * @type {function}
15842      */
15843     _tree  : false,
15844     
15845      /**
15846      * render
15847      * render element to dom or tree
15848      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15849      */
15850     
15851     render : function(el)
15852     {
15853         
15854         el = el || false;
15855         var hp = this.parent ? 1 : 0;
15856         
15857         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15858             // if parent is a '#.....' string, then let's use that..
15859             var ename = this.parent.substr(1)
15860             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15861             el = Roo.get(ename);
15862             if (!el && !this.parent) {
15863                 Roo.log("Warning - element can not be found :#" + ename );
15864                 return;
15865             }
15866         }
15867         var tree = this._tree ? this._tree() : this.tree();
15868
15869         
15870         if (!this.parent && typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) {
15871             //el = Roo.get(document.body);
15872             this.parent = { el : true };
15873         }
15874             
15875             
15876         
15877         if (!this.parent) {
15878             
15879             Roo.log("no parent - creating one");
15880             
15881             el = el ? Roo.get(el) : false;      
15882             
15883             // it's a top level one..
15884             this.parent =  {
15885                 el : new Roo.BorderLayout(el || document.body, {
15886                 
15887                      center: {
15888                          titlebar: false,
15889                          autoScroll:false,
15890                          closeOnTab: true,
15891                          tabPosition: 'top',
15892                           //resizeTabs: true,
15893                          alwaysShowTabs: el && hp? false :  true,
15894                          hideTabs: el || !hp ? true :  false,
15895                          minTabWidth: 140
15896                      }
15897                  })
15898             }
15899         }
15900         
15901                 if (!this.parent.el) {
15902                         // probably an old style ctor, which has been disabled.
15903                         return;
15904                         
15905                 }
15906                 // The 'tree' method is  '_tree now' 
15907             
15908         tree.region = tree.region || this.region;
15909         
15910         if (this.parent.el === true) {
15911             // bootstrap... - body..
15912             this.parent.el = Roo.factory(tree);
15913         }
15914         
15915         this.el = this.parent.el.addxtype(tree);
15916         this.fireEvent('built', this);
15917         
15918         this.panel = this.el;
15919         this.layout = this.panel.layout;
15920                 this.parentLayout = this.parent.layout  || false;  
15921          
15922     }
15923     
15924 });
15925
15926 Roo.apply(Roo.XComponent, {
15927     /**
15928      * @property  hideProgress
15929      * true to disable the building progress bar.. usefull on single page renders.
15930      * @type Boolean
15931      */
15932     hideProgress : false,
15933     /**
15934      * @property  buildCompleted
15935      * True when the builder has completed building the interface.
15936      * @type Boolean
15937      */
15938     buildCompleted : false,
15939      
15940     /**
15941      * @property  topModule
15942      * the upper most module - uses document.element as it's constructor.
15943      * @type Object
15944      */
15945      
15946     topModule  : false,
15947       
15948     /**
15949      * @property  modules
15950      * array of modules to be created by registration system.
15951      * @type {Array} of Roo.XComponent
15952      */
15953     
15954     modules : [],
15955     /**
15956      * @property  elmodules
15957      * array of modules to be created by which use #ID 
15958      * @type {Array} of Roo.XComponent
15959      */
15960      
15961     elmodules : [],
15962
15963      /**
15964      * @property  build_from_html
15965      * Build elements from html - used by bootstrap HTML stuff 
15966      *    - this is cleared after build is completed
15967      * @type {boolean} true  (default false)
15968      */
15969      
15970     build_from_html : false,
15971
15972     /**
15973      * Register components to be built later.
15974      *
15975      * This solves the following issues
15976      * - Building is not done on page load, but after an authentication process has occured.
15977      * - Interface elements are registered on page load
15978      * - Parent Interface elements may not be loaded before child, so this handles that..
15979      * 
15980      *
15981      * example:
15982      * 
15983      * MyApp.register({
15984           order : '000001',
15985           module : 'Pman.Tab.projectMgr',
15986           region : 'center',
15987           parent : 'Pman.layout',
15988           disabled : false,  // or use a function..
15989         })
15990      
15991      * * @param {Object} details about module
15992      */
15993     register : function(obj) {
15994                 
15995         Roo.XComponent.event.fireEvent('register', obj);
15996         switch(typeof(obj.disabled) ) {
15997                 
15998             case 'undefined':
15999                 break;
16000             
16001             case 'function':
16002                 if ( obj.disabled() ) {
16003                         return;
16004                 }
16005                 break;
16006             
16007             default:
16008                 if (obj.disabled) {
16009                         return;
16010                 }
16011                 break;
16012         }
16013                 
16014         this.modules.push(obj);
16015          
16016     },
16017     /**
16018      * convert a string to an object..
16019      * eg. 'AAA.BBB' -> finds AAA.BBB
16020
16021      */
16022     
16023     toObject : function(str)
16024     {
16025         if (!str || typeof(str) == 'object') {
16026             return str;
16027         }
16028         if (str.substring(0,1) == '#') {
16029             return str;
16030         }
16031
16032         var ar = str.split('.');
16033         var rt, o;
16034         rt = ar.shift();
16035             /** eval:var:o */
16036         try {
16037             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16038         } catch (e) {
16039             throw "Module not found : " + str;
16040         }
16041         
16042         if (o === false) {
16043             throw "Module not found : " + str;
16044         }
16045         Roo.each(ar, function(e) {
16046             if (typeof(o[e]) == 'undefined') {
16047                 throw "Module not found : " + str;
16048             }
16049             o = o[e];
16050         });
16051         
16052         return o;
16053         
16054     },
16055     
16056     
16057     /**
16058      * move modules into their correct place in the tree..
16059      * 
16060      */
16061     preBuild : function ()
16062     {
16063         var _t = this;
16064         Roo.each(this.modules , function (obj)
16065         {
16066             Roo.XComponent.event.fireEvent('beforebuild', obj);
16067             
16068             var opar = obj.parent;
16069             try { 
16070                 obj.parent = this.toObject(opar);
16071             } catch(e) {
16072                 Roo.log("parent:toObject failed: " + e.toString());
16073                 return;
16074             }
16075             
16076             if (!obj.parent) {
16077                 Roo.debug && Roo.log("GOT top level module");
16078                 Roo.debug && Roo.log(obj);
16079                 obj.modules = new Roo.util.MixedCollection(false, 
16080                     function(o) { return o.order + '' }
16081                 );
16082                 this.topModule = obj;
16083                 return;
16084             }
16085                         // parent is a string (usually a dom element name..)
16086             if (typeof(obj.parent) == 'string') {
16087                 this.elmodules.push(obj);
16088                 return;
16089             }
16090             if (obj.parent.constructor != Roo.XComponent) {
16091                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16092             }
16093             if (!obj.parent.modules) {
16094                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16095                     function(o) { return o.order + '' }
16096                 );
16097             }
16098             if (obj.parent.disabled) {
16099                 obj.disabled = true;
16100             }
16101             obj.parent.modules.add(obj);
16102         }, this);
16103     },
16104     
16105      /**
16106      * make a list of modules to build.
16107      * @return {Array} list of modules. 
16108      */ 
16109     
16110     buildOrder : function()
16111     {
16112         var _this = this;
16113         var cmp = function(a,b) {   
16114             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16115         };
16116         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16117             throw "No top level modules to build";
16118         }
16119         
16120         // make a flat list in order of modules to build.
16121         var mods = this.topModule ? [ this.topModule ] : [];
16122                 
16123         
16124         // elmodules (is a list of DOM based modules )
16125         Roo.each(this.elmodules, function(e) {
16126             mods.push(e);
16127             if (!this.topModule &&
16128                 typeof(e.parent) == 'string' &&
16129                 e.parent.substring(0,1) == '#' &&
16130                 Roo.get(e.parent.substr(1))
16131                ) {
16132                 
16133                 _this.topModule = e;
16134             }
16135             
16136         });
16137
16138         
16139         // add modules to their parents..
16140         var addMod = function(m) {
16141             Roo.debug && Roo.log("build Order: add: " + m.name);
16142                 
16143             mods.push(m);
16144             if (m.modules && !m.disabled) {
16145                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16146                 m.modules.keySort('ASC',  cmp );
16147                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16148     
16149                 m.modules.each(addMod);
16150             } else {
16151                 Roo.debug && Roo.log("build Order: no child modules");
16152             }
16153             // not sure if this is used any more..
16154             if (m.finalize) {
16155                 m.finalize.name = m.name + " (clean up) ";
16156                 mods.push(m.finalize);
16157             }
16158             
16159         }
16160         if (this.topModule && this.topModule.modules) { 
16161             this.topModule.modules.keySort('ASC',  cmp );
16162             this.topModule.modules.each(addMod);
16163         } 
16164         return mods;
16165     },
16166     
16167      /**
16168      * Build the registered modules.
16169      * @param {Object} parent element.
16170      * @param {Function} optional method to call after module has been added.
16171      * 
16172      */ 
16173    
16174     build : function(opts) 
16175     {
16176         
16177         if (typeof(opts) != 'undefined') {
16178             Roo.apply(this,opts);
16179         }
16180         
16181         this.preBuild();
16182         var mods = this.buildOrder();
16183       
16184         //this.allmods = mods;
16185         //Roo.debug && Roo.log(mods);
16186         //return;
16187         if (!mods.length) { // should not happen
16188             throw "NO modules!!!";
16189         }
16190         
16191         
16192         var msg = "Building Interface...";
16193         // flash it up as modal - so we store the mask!?
16194         if (!this.hideProgress && Roo.MessageBox) {
16195             Roo.MessageBox.show({ title: 'loading' });
16196             Roo.MessageBox.show({
16197                title: "Please wait...",
16198                msg: msg,
16199                width:450,
16200                progress:true,
16201                closable:false,
16202                modal: false
16203               
16204             });
16205         }
16206         var total = mods.length;
16207         
16208         var _this = this;
16209         var progressRun = function() {
16210             if (!mods.length) {
16211                 Roo.debug && Roo.log('hide?');
16212                 if (!this.hideProgress && Roo.MessageBox) {
16213                     Roo.MessageBox.hide();
16214                 }
16215                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16216                 
16217                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16218                 
16219                 // THE END...
16220                 return false;   
16221             }
16222             
16223             var m = mods.shift();
16224             
16225             
16226             Roo.debug && Roo.log(m);
16227             // not sure if this is supported any more.. - modules that are are just function
16228             if (typeof(m) == 'function') { 
16229                 m.call(this);
16230                 return progressRun.defer(10, _this);
16231             } 
16232             
16233             
16234             msg = "Building Interface " + (total  - mods.length) + 
16235                     " of " + total + 
16236                     (m.name ? (' - ' + m.name) : '');
16237                         Roo.debug && Roo.log(msg);
16238             if (!this.hideProgress &&  Roo.MessageBox) { 
16239                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16240             }
16241             
16242          
16243             // is the module disabled?
16244             var disabled = (typeof(m.disabled) == 'function') ?
16245                 m.disabled.call(m.module.disabled) : m.disabled;    
16246             
16247             
16248             if (disabled) {
16249                 return progressRun(); // we do not update the display!
16250             }
16251             
16252             // now build 
16253             
16254                         
16255                         
16256             m.render();
16257             // it's 10 on top level, and 1 on others??? why...
16258             return progressRun.defer(10, _this);
16259              
16260         }
16261         progressRun.defer(1, _this);
16262      
16263         
16264         
16265     },
16266         
16267         
16268         /**
16269          * Event Object.
16270          *
16271          *
16272          */
16273         event: false, 
16274     /**
16275          * wrapper for event.on - aliased later..  
16276          * Typically use to register a event handler for register:
16277          *
16278          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16279          *
16280          */
16281     on : false
16282    
16283     
16284     
16285 });
16286
16287 Roo.XComponent.event = new Roo.util.Observable({
16288                 events : { 
16289                         /**
16290                          * @event register
16291                          * Fires when an Component is registered,
16292                          * set the disable property on the Component to stop registration.
16293                          * @param {Roo.XComponent} c the component being registerd.
16294                          * 
16295                          */
16296                         'register' : true,
16297             /**
16298                          * @event beforebuild
16299                          * Fires before each Component is built
16300                          * can be used to apply permissions.
16301                          * @param {Roo.XComponent} c the component being registerd.
16302                          * 
16303                          */
16304                         'beforebuild' : true,
16305                         /**
16306                          * @event buildcomplete
16307                          * Fires on the top level element when all elements have been built
16308                          * @param {Roo.XComponent} the top level component.
16309                          */
16310                         'buildcomplete' : true
16311                         
16312                 }
16313 });
16314
16315 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16316  /*
16317  * Based on:
16318  * Ext JS Library 1.1.1
16319  * Copyright(c) 2006-2007, Ext JS, LLC.
16320  *
16321  * Originally Released Under LGPL - original licence link has changed is not relivant.
16322  *
16323  * Fork - LGPL
16324  * <script type="text/javascript">
16325  */
16326
16327
16328
16329 /*
16330  * These classes are derivatives of the similarly named classes in the YUI Library.
16331  * The original license:
16332  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16333  * Code licensed under the BSD License:
16334  * http://developer.yahoo.net/yui/license.txt
16335  */
16336
16337 (function() {
16338
16339 var Event=Roo.EventManager;
16340 var Dom=Roo.lib.Dom;
16341
16342 /**
16343  * @class Roo.dd.DragDrop
16344  * @extends Roo.util.Observable
16345  * Defines the interface and base operation of items that that can be
16346  * dragged or can be drop targets.  It was designed to be extended, overriding
16347  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16348  * Up to three html elements can be associated with a DragDrop instance:
16349  * <ul>
16350  * <li>linked element: the element that is passed into the constructor.
16351  * This is the element which defines the boundaries for interaction with
16352  * other DragDrop objects.</li>
16353  * <li>handle element(s): The drag operation only occurs if the element that
16354  * was clicked matches a handle element.  By default this is the linked
16355  * element, but there are times that you will want only a portion of the
16356  * linked element to initiate the drag operation, and the setHandleElId()
16357  * method provides a way to define this.</li>
16358  * <li>drag element: this represents the element that would be moved along
16359  * with the cursor during a drag operation.  By default, this is the linked
16360  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16361  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16362  * </li>
16363  * </ul>
16364  * This class should not be instantiated until the onload event to ensure that
16365  * the associated elements are available.
16366  * The following would define a DragDrop obj that would interact with any
16367  * other DragDrop obj in the "group1" group:
16368  * <pre>
16369  *  dd = new Roo.dd.DragDrop("div1", "group1");
16370  * </pre>
16371  * Since none of the event handlers have been implemented, nothing would
16372  * actually happen if you were to run the code above.  Normally you would
16373  * override this class or one of the default implementations, but you can
16374  * also override the methods you want on an instance of the class...
16375  * <pre>
16376  *  dd.onDragDrop = function(e, id) {
16377  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16378  *  }
16379  * </pre>
16380  * @constructor
16381  * @param {String} id of the element that is linked to this instance
16382  * @param {String} sGroup the group of related DragDrop objects
16383  * @param {object} config an object containing configurable attributes
16384  *                Valid properties for DragDrop:
16385  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16386  */
16387 Roo.dd.DragDrop = function(id, sGroup, config) {
16388     if (id) {
16389         this.init(id, sGroup, config);
16390     }
16391     
16392 };
16393
16394 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16395
16396     /**
16397      * The id of the element associated with this object.  This is what we
16398      * refer to as the "linked element" because the size and position of
16399      * this element is used to determine when the drag and drop objects have
16400      * interacted.
16401      * @property id
16402      * @type String
16403      */
16404     id: null,
16405
16406     /**
16407      * Configuration attributes passed into the constructor
16408      * @property config
16409      * @type object
16410      */
16411     config: null,
16412
16413     /**
16414      * The id of the element that will be dragged.  By default this is same
16415      * as the linked element , but could be changed to another element. Ex:
16416      * Roo.dd.DDProxy
16417      * @property dragElId
16418      * @type String
16419      * @private
16420      */
16421     dragElId: null,
16422
16423     /**
16424      * the id of the element that initiates the drag operation.  By default
16425      * this is the linked element, but could be changed to be a child of this
16426      * element.  This lets us do things like only starting the drag when the
16427      * header element within the linked html element is clicked.
16428      * @property handleElId
16429      * @type String
16430      * @private
16431      */
16432     handleElId: null,
16433
16434     /**
16435      * An associative array of HTML tags that will be ignored if clicked.
16436      * @property invalidHandleTypes
16437      * @type {string: string}
16438      */
16439     invalidHandleTypes: null,
16440
16441     /**
16442      * An associative array of ids for elements that will be ignored if clicked
16443      * @property invalidHandleIds
16444      * @type {string: string}
16445      */
16446     invalidHandleIds: null,
16447
16448     /**
16449      * An indexted array of css class names for elements that will be ignored
16450      * if clicked.
16451      * @property invalidHandleClasses
16452      * @type string[]
16453      */
16454     invalidHandleClasses: null,
16455
16456     /**
16457      * The linked element's absolute X position at the time the drag was
16458      * started
16459      * @property startPageX
16460      * @type int
16461      * @private
16462      */
16463     startPageX: 0,
16464
16465     /**
16466      * The linked element's absolute X position at the time the drag was
16467      * started
16468      * @property startPageY
16469      * @type int
16470      * @private
16471      */
16472     startPageY: 0,
16473
16474     /**
16475      * The group defines a logical collection of DragDrop objects that are
16476      * related.  Instances only get events when interacting with other
16477      * DragDrop object in the same group.  This lets us define multiple
16478      * groups using a single DragDrop subclass if we want.
16479      * @property groups
16480      * @type {string: string}
16481      */
16482     groups: null,
16483
16484     /**
16485      * Individual drag/drop instances can be locked.  This will prevent
16486      * onmousedown start drag.
16487      * @property locked
16488      * @type boolean
16489      * @private
16490      */
16491     locked: false,
16492
16493     /**
16494      * Lock this instance
16495      * @method lock
16496      */
16497     lock: function() { this.locked = true; },
16498
16499     /**
16500      * Unlock this instace
16501      * @method unlock
16502      */
16503     unlock: function() { this.locked = false; },
16504
16505     /**
16506      * By default, all insances can be a drop target.  This can be disabled by
16507      * setting isTarget to false.
16508      * @method isTarget
16509      * @type boolean
16510      */
16511     isTarget: true,
16512
16513     /**
16514      * The padding configured for this drag and drop object for calculating
16515      * the drop zone intersection with this object.
16516      * @method padding
16517      * @type int[]
16518      */
16519     padding: null,
16520
16521     /**
16522      * Cached reference to the linked element
16523      * @property _domRef
16524      * @private
16525      */
16526     _domRef: null,
16527
16528     /**
16529      * Internal typeof flag
16530      * @property __ygDragDrop
16531      * @private
16532      */
16533     __ygDragDrop: true,
16534
16535     /**
16536      * Set to true when horizontal contraints are applied
16537      * @property constrainX
16538      * @type boolean
16539      * @private
16540      */
16541     constrainX: false,
16542
16543     /**
16544      * Set to true when vertical contraints are applied
16545      * @property constrainY
16546      * @type boolean
16547      * @private
16548      */
16549     constrainY: false,
16550
16551     /**
16552      * The left constraint
16553      * @property minX
16554      * @type int
16555      * @private
16556      */
16557     minX: 0,
16558
16559     /**
16560      * The right constraint
16561      * @property maxX
16562      * @type int
16563      * @private
16564      */
16565     maxX: 0,
16566
16567     /**
16568      * The up constraint
16569      * @property minY
16570      * @type int
16571      * @type int
16572      * @private
16573      */
16574     minY: 0,
16575
16576     /**
16577      * The down constraint
16578      * @property maxY
16579      * @type int
16580      * @private
16581      */
16582     maxY: 0,
16583
16584     /**
16585      * Maintain offsets when we resetconstraints.  Set to true when you want
16586      * the position of the element relative to its parent to stay the same
16587      * when the page changes
16588      *
16589      * @property maintainOffset
16590      * @type boolean
16591      */
16592     maintainOffset: false,
16593
16594     /**
16595      * Array of pixel locations the element will snap to if we specified a
16596      * horizontal graduation/interval.  This array is generated automatically
16597      * when you define a tick interval.
16598      * @property xTicks
16599      * @type int[]
16600      */
16601     xTicks: null,
16602
16603     /**
16604      * Array of pixel locations the element will snap to if we specified a
16605      * vertical graduation/interval.  This array is generated automatically
16606      * when you define a tick interval.
16607      * @property yTicks
16608      * @type int[]
16609      */
16610     yTicks: null,
16611
16612     /**
16613      * By default the drag and drop instance will only respond to the primary
16614      * button click (left button for a right-handed mouse).  Set to true to
16615      * allow drag and drop to start with any mouse click that is propogated
16616      * by the browser
16617      * @property primaryButtonOnly
16618      * @type boolean
16619      */
16620     primaryButtonOnly: true,
16621
16622     /**
16623      * The availabe property is false until the linked dom element is accessible.
16624      * @property available
16625      * @type boolean
16626      */
16627     available: false,
16628
16629     /**
16630      * By default, drags can only be initiated if the mousedown occurs in the
16631      * region the linked element is.  This is done in part to work around a
16632      * bug in some browsers that mis-report the mousedown if the previous
16633      * mouseup happened outside of the window.  This property is set to true
16634      * if outer handles are defined.
16635      *
16636      * @property hasOuterHandles
16637      * @type boolean
16638      * @default false
16639      */
16640     hasOuterHandles: false,
16641
16642     /**
16643      * Code that executes immediately before the startDrag event
16644      * @method b4StartDrag
16645      * @private
16646      */
16647     b4StartDrag: function(x, y) { },
16648
16649     /**
16650      * Abstract method called after a drag/drop object is clicked
16651      * and the drag or mousedown time thresholds have beeen met.
16652      * @method startDrag
16653      * @param {int} X click location
16654      * @param {int} Y click location
16655      */
16656     startDrag: function(x, y) { /* override this */ },
16657
16658     /**
16659      * Code that executes immediately before the onDrag event
16660      * @method b4Drag
16661      * @private
16662      */
16663     b4Drag: function(e) { },
16664
16665     /**
16666      * Abstract method called during the onMouseMove event while dragging an
16667      * object.
16668      * @method onDrag
16669      * @param {Event} e the mousemove event
16670      */
16671     onDrag: function(e) { /* override this */ },
16672
16673     /**
16674      * Abstract method called when this element fist begins hovering over
16675      * another DragDrop obj
16676      * @method onDragEnter
16677      * @param {Event} e the mousemove event
16678      * @param {String|DragDrop[]} id In POINT mode, the element
16679      * id this is hovering over.  In INTERSECT mode, an array of one or more
16680      * dragdrop items being hovered over.
16681      */
16682     onDragEnter: function(e, id) { /* override this */ },
16683
16684     /**
16685      * Code that executes immediately before the onDragOver event
16686      * @method b4DragOver
16687      * @private
16688      */
16689     b4DragOver: function(e) { },
16690
16691     /**
16692      * Abstract method called when this element is hovering over another
16693      * DragDrop obj
16694      * @method onDragOver
16695      * @param {Event} e the mousemove event
16696      * @param {String|DragDrop[]} id In POINT mode, the element
16697      * id this is hovering over.  In INTERSECT mode, an array of dd items
16698      * being hovered over.
16699      */
16700     onDragOver: function(e, id) { /* override this */ },
16701
16702     /**
16703      * Code that executes immediately before the onDragOut event
16704      * @method b4DragOut
16705      * @private
16706      */
16707     b4DragOut: function(e) { },
16708
16709     /**
16710      * Abstract method called when we are no longer hovering over an element
16711      * @method onDragOut
16712      * @param {Event} e the mousemove event
16713      * @param {String|DragDrop[]} id In POINT mode, the element
16714      * id this was hovering over.  In INTERSECT mode, an array of dd items
16715      * that the mouse is no longer over.
16716      */
16717     onDragOut: function(e, id) { /* override this */ },
16718
16719     /**
16720      * Code that executes immediately before the onDragDrop event
16721      * @method b4DragDrop
16722      * @private
16723      */
16724     b4DragDrop: function(e) { },
16725
16726     /**
16727      * Abstract method called when this item is dropped on another DragDrop
16728      * obj
16729      * @method onDragDrop
16730      * @param {Event} e the mouseup event
16731      * @param {String|DragDrop[]} id In POINT mode, the element
16732      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16733      * was dropped on.
16734      */
16735     onDragDrop: function(e, id) { /* override this */ },
16736
16737     /**
16738      * Abstract method called when this item is dropped on an area with no
16739      * drop target
16740      * @method onInvalidDrop
16741      * @param {Event} e the mouseup event
16742      */
16743     onInvalidDrop: function(e) { /* override this */ },
16744
16745     /**
16746      * Code that executes immediately before the endDrag event
16747      * @method b4EndDrag
16748      * @private
16749      */
16750     b4EndDrag: function(e) { },
16751
16752     /**
16753      * Fired when we are done dragging the object
16754      * @method endDrag
16755      * @param {Event} e the mouseup event
16756      */
16757     endDrag: function(e) { /* override this */ },
16758
16759     /**
16760      * Code executed immediately before the onMouseDown event
16761      * @method b4MouseDown
16762      * @param {Event} e the mousedown event
16763      * @private
16764      */
16765     b4MouseDown: function(e) {  },
16766
16767     /**
16768      * Event handler that fires when a drag/drop obj gets a mousedown
16769      * @method onMouseDown
16770      * @param {Event} e the mousedown event
16771      */
16772     onMouseDown: function(e) { /* override this */ },
16773
16774     /**
16775      * Event handler that fires when a drag/drop obj gets a mouseup
16776      * @method onMouseUp
16777      * @param {Event} e the mouseup event
16778      */
16779     onMouseUp: function(e) { /* override this */ },
16780
16781     /**
16782      * Override the onAvailable method to do what is needed after the initial
16783      * position was determined.
16784      * @method onAvailable
16785      */
16786     onAvailable: function () {
16787     },
16788
16789     /*
16790      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16791      * @type Object
16792      */
16793     defaultPadding : {left:0, right:0, top:0, bottom:0},
16794
16795     /*
16796      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16797  *
16798  * Usage:
16799  <pre><code>
16800  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16801                 { dragElId: "existingProxyDiv" });
16802  dd.startDrag = function(){
16803      this.constrainTo("parent-id");
16804  };
16805  </code></pre>
16806  * Or you can initalize it using the {@link Roo.Element} object:
16807  <pre><code>
16808  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16809      startDrag : function(){
16810          this.constrainTo("parent-id");
16811      }
16812  });
16813  </code></pre>
16814      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16815      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16816      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16817      * an object containing the sides to pad. For example: {right:10, bottom:10}
16818      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16819      */
16820     constrainTo : function(constrainTo, pad, inContent){
16821         if(typeof pad == "number"){
16822             pad = {left: pad, right:pad, top:pad, bottom:pad};
16823         }
16824         pad = pad || this.defaultPadding;
16825         var b = Roo.get(this.getEl()).getBox();
16826         var ce = Roo.get(constrainTo);
16827         var s = ce.getScroll();
16828         var c, cd = ce.dom;
16829         if(cd == document.body){
16830             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16831         }else{
16832             xy = ce.getXY();
16833             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16834         }
16835
16836
16837         var topSpace = b.y - c.y;
16838         var leftSpace = b.x - c.x;
16839
16840         this.resetConstraints();
16841         this.setXConstraint(leftSpace - (pad.left||0), // left
16842                 c.width - leftSpace - b.width - (pad.right||0) //right
16843         );
16844         this.setYConstraint(topSpace - (pad.top||0), //top
16845                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16846         );
16847     },
16848
16849     /**
16850      * Returns a reference to the linked element
16851      * @method getEl
16852      * @return {HTMLElement} the html element
16853      */
16854     getEl: function() {
16855         if (!this._domRef) {
16856             this._domRef = Roo.getDom(this.id);
16857         }
16858
16859         return this._domRef;
16860     },
16861
16862     /**
16863      * Returns a reference to the actual element to drag.  By default this is
16864      * the same as the html element, but it can be assigned to another
16865      * element. An example of this can be found in Roo.dd.DDProxy
16866      * @method getDragEl
16867      * @return {HTMLElement} the html element
16868      */
16869     getDragEl: function() {
16870         return Roo.getDom(this.dragElId);
16871     },
16872
16873     /**
16874      * Sets up the DragDrop object.  Must be called in the constructor of any
16875      * Roo.dd.DragDrop subclass
16876      * @method init
16877      * @param id the id of the linked element
16878      * @param {String} sGroup the group of related items
16879      * @param {object} config configuration attributes
16880      */
16881     init: function(id, sGroup, config) {
16882         this.initTarget(id, sGroup, config);
16883         if (!Roo.isTouch) {
16884             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16885         }
16886         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16887         // Event.on(this.id, "selectstart", Event.preventDefault);
16888     },
16889
16890     /**
16891      * Initializes Targeting functionality only... the object does not
16892      * get a mousedown handler.
16893      * @method initTarget
16894      * @param id the id of the linked element
16895      * @param {String} sGroup the group of related items
16896      * @param {object} config configuration attributes
16897      */
16898     initTarget: function(id, sGroup, config) {
16899
16900         // configuration attributes
16901         this.config = config || {};
16902
16903         // create a local reference to the drag and drop manager
16904         this.DDM = Roo.dd.DDM;
16905         // initialize the groups array
16906         this.groups = {};
16907
16908         // assume that we have an element reference instead of an id if the
16909         // parameter is not a string
16910         if (typeof id !== "string") {
16911             id = Roo.id(id);
16912         }
16913
16914         // set the id
16915         this.id = id;
16916
16917         // add to an interaction group
16918         this.addToGroup((sGroup) ? sGroup : "default");
16919
16920         // We don't want to register this as the handle with the manager
16921         // so we just set the id rather than calling the setter.
16922         this.handleElId = id;
16923
16924         // the linked element is the element that gets dragged by default
16925         this.setDragElId(id);
16926
16927         // by default, clicked anchors will not start drag operations.
16928         this.invalidHandleTypes = { A: "A" };
16929         this.invalidHandleIds = {};
16930         this.invalidHandleClasses = [];
16931
16932         this.applyConfig();
16933
16934         this.handleOnAvailable();
16935     },
16936
16937     /**
16938      * Applies the configuration parameters that were passed into the constructor.
16939      * This is supposed to happen at each level through the inheritance chain.  So
16940      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16941      * DragDrop in order to get all of the parameters that are available in
16942      * each object.
16943      * @method applyConfig
16944      */
16945     applyConfig: function() {
16946
16947         // configurable properties:
16948         //    padding, isTarget, maintainOffset, primaryButtonOnly
16949         this.padding           = this.config.padding || [0, 0, 0, 0];
16950         this.isTarget          = (this.config.isTarget !== false);
16951         this.maintainOffset    = (this.config.maintainOffset);
16952         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16953
16954     },
16955
16956     /**
16957      * Executed when the linked element is available
16958      * @method handleOnAvailable
16959      * @private
16960      */
16961     handleOnAvailable: function() {
16962         this.available = true;
16963         this.resetConstraints();
16964         this.onAvailable();
16965     },
16966
16967      /**
16968      * Configures the padding for the target zone in px.  Effectively expands
16969      * (or reduces) the virtual object size for targeting calculations.
16970      * Supports css-style shorthand; if only one parameter is passed, all sides
16971      * will have that padding, and if only two are passed, the top and bottom
16972      * will have the first param, the left and right the second.
16973      * @method setPadding
16974      * @param {int} iTop    Top pad
16975      * @param {int} iRight  Right pad
16976      * @param {int} iBot    Bot pad
16977      * @param {int} iLeft   Left pad
16978      */
16979     setPadding: function(iTop, iRight, iBot, iLeft) {
16980         // this.padding = [iLeft, iRight, iTop, iBot];
16981         if (!iRight && 0 !== iRight) {
16982             this.padding = [iTop, iTop, iTop, iTop];
16983         } else if (!iBot && 0 !== iBot) {
16984             this.padding = [iTop, iRight, iTop, iRight];
16985         } else {
16986             this.padding = [iTop, iRight, iBot, iLeft];
16987         }
16988     },
16989
16990     /**
16991      * Stores the initial placement of the linked element.
16992      * @method setInitialPosition
16993      * @param {int} diffX   the X offset, default 0
16994      * @param {int} diffY   the Y offset, default 0
16995      */
16996     setInitPosition: function(diffX, diffY) {
16997         var el = this.getEl();
16998
16999         if (!this.DDM.verifyEl(el)) {
17000             return;
17001         }
17002
17003         var dx = diffX || 0;
17004         var dy = diffY || 0;
17005
17006         var p = Dom.getXY( el );
17007
17008         this.initPageX = p[0] - dx;
17009         this.initPageY = p[1] - dy;
17010
17011         this.lastPageX = p[0];
17012         this.lastPageY = p[1];
17013
17014
17015         this.setStartPosition(p);
17016     },
17017
17018     /**
17019      * Sets the start position of the element.  This is set when the obj
17020      * is initialized, the reset when a drag is started.
17021      * @method setStartPosition
17022      * @param pos current position (from previous lookup)
17023      * @private
17024      */
17025     setStartPosition: function(pos) {
17026         var p = pos || Dom.getXY( this.getEl() );
17027         this.deltaSetXY = null;
17028
17029         this.startPageX = p[0];
17030         this.startPageY = p[1];
17031     },
17032
17033     /**
17034      * Add this instance to a group of related drag/drop objects.  All
17035      * instances belong to at least one group, and can belong to as many
17036      * groups as needed.
17037      * @method addToGroup
17038      * @param sGroup {string} the name of the group
17039      */
17040     addToGroup: function(sGroup) {
17041         this.groups[sGroup] = true;
17042         this.DDM.regDragDrop(this, sGroup);
17043     },
17044
17045     /**
17046      * Remove's this instance from the supplied interaction group
17047      * @method removeFromGroup
17048      * @param {string}  sGroup  The group to drop
17049      */
17050     removeFromGroup: function(sGroup) {
17051         if (this.groups[sGroup]) {
17052             delete this.groups[sGroup];
17053         }
17054
17055         this.DDM.removeDDFromGroup(this, sGroup);
17056     },
17057
17058     /**
17059      * Allows you to specify that an element other than the linked element
17060      * will be moved with the cursor during a drag
17061      * @method setDragElId
17062      * @param id {string} the id of the element that will be used to initiate the drag
17063      */
17064     setDragElId: function(id) {
17065         this.dragElId = id;
17066     },
17067
17068     /**
17069      * Allows you to specify a child of the linked element that should be
17070      * used to initiate the drag operation.  An example of this would be if
17071      * you have a content div with text and links.  Clicking anywhere in the
17072      * content area would normally start the drag operation.  Use this method
17073      * to specify that an element inside of the content div is the element
17074      * that starts the drag operation.
17075      * @method setHandleElId
17076      * @param id {string} the id of the element that will be used to
17077      * initiate the drag.
17078      */
17079     setHandleElId: function(id) {
17080         if (typeof id !== "string") {
17081             id = Roo.id(id);
17082         }
17083         this.handleElId = id;
17084         this.DDM.regHandle(this.id, id);
17085     },
17086
17087     /**
17088      * Allows you to set an element outside of the linked element as a drag
17089      * handle
17090      * @method setOuterHandleElId
17091      * @param id the id of the element that will be used to initiate the drag
17092      */
17093     setOuterHandleElId: function(id) {
17094         if (typeof id !== "string") {
17095             id = Roo.id(id);
17096         }
17097         Event.on(id, "mousedown",
17098                 this.handleMouseDown, this);
17099         this.setHandleElId(id);
17100
17101         this.hasOuterHandles = true;
17102     },
17103
17104     /**
17105      * Remove all drag and drop hooks for this element
17106      * @method unreg
17107      */
17108     unreg: function() {
17109         Event.un(this.id, "mousedown",
17110                 this.handleMouseDown);
17111         Event.un(this.id, "touchstart",
17112                 this.handleMouseDown);
17113         this._domRef = null;
17114         this.DDM._remove(this);
17115     },
17116
17117     destroy : function(){
17118         this.unreg();
17119     },
17120
17121     /**
17122      * Returns true if this instance is locked, or the drag drop mgr is locked
17123      * (meaning that all drag/drop is disabled on the page.)
17124      * @method isLocked
17125      * @return {boolean} true if this obj or all drag/drop is locked, else
17126      * false
17127      */
17128     isLocked: function() {
17129         return (this.DDM.isLocked() || this.locked);
17130     },
17131
17132     /**
17133      * Fired when this object is clicked
17134      * @method handleMouseDown
17135      * @param {Event} e
17136      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17137      * @private
17138      */
17139     handleMouseDown: function(e, oDD){
17140      
17141         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17142             //Roo.log('not touch/ button !=0');
17143             return;
17144         }
17145         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17146             return; // double touch..
17147         }
17148         
17149
17150         if (this.isLocked()) {
17151             //Roo.log('locked');
17152             return;
17153         }
17154
17155         this.DDM.refreshCache(this.groups);
17156 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17157         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17158         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17159             //Roo.log('no outer handes or not over target');
17160                 // do nothing.
17161         } else {
17162 //            Roo.log('check validator');
17163             if (this.clickValidator(e)) {
17164 //                Roo.log('validate success');
17165                 // set the initial element position
17166                 this.setStartPosition();
17167
17168
17169                 this.b4MouseDown(e);
17170                 this.onMouseDown(e);
17171
17172                 this.DDM.handleMouseDown(e, this);
17173
17174                 this.DDM.stopEvent(e);
17175             } else {
17176
17177
17178             }
17179         }
17180     },
17181
17182     clickValidator: function(e) {
17183         var target = e.getTarget();
17184         return ( this.isValidHandleChild(target) &&
17185                     (this.id == this.handleElId ||
17186                         this.DDM.handleWasClicked(target, this.id)) );
17187     },
17188
17189     /**
17190      * Allows you to specify a tag name that should not start a drag operation
17191      * when clicked.  This is designed to facilitate embedding links within a
17192      * drag handle that do something other than start the drag.
17193      * @method addInvalidHandleType
17194      * @param {string} tagName the type of element to exclude
17195      */
17196     addInvalidHandleType: function(tagName) {
17197         var type = tagName.toUpperCase();
17198         this.invalidHandleTypes[type] = type;
17199     },
17200
17201     /**
17202      * Lets you to specify an element id for a child of a drag handle
17203      * that should not initiate a drag
17204      * @method addInvalidHandleId
17205      * @param {string} id the element id of the element you wish to ignore
17206      */
17207     addInvalidHandleId: function(id) {
17208         if (typeof id !== "string") {
17209             id = Roo.id(id);
17210         }
17211         this.invalidHandleIds[id] = id;
17212     },
17213
17214     /**
17215      * Lets you specify a css class of elements that will not initiate a drag
17216      * @method addInvalidHandleClass
17217      * @param {string} cssClass the class of the elements you wish to ignore
17218      */
17219     addInvalidHandleClass: function(cssClass) {
17220         this.invalidHandleClasses.push(cssClass);
17221     },
17222
17223     /**
17224      * Unsets an excluded tag name set by addInvalidHandleType
17225      * @method removeInvalidHandleType
17226      * @param {string} tagName the type of element to unexclude
17227      */
17228     removeInvalidHandleType: function(tagName) {
17229         var type = tagName.toUpperCase();
17230         // this.invalidHandleTypes[type] = null;
17231         delete this.invalidHandleTypes[type];
17232     },
17233
17234     /**
17235      * Unsets an invalid handle id
17236      * @method removeInvalidHandleId
17237      * @param {string} id the id of the element to re-enable
17238      */
17239     removeInvalidHandleId: function(id) {
17240         if (typeof id !== "string") {
17241             id = Roo.id(id);
17242         }
17243         delete this.invalidHandleIds[id];
17244     },
17245
17246     /**
17247      * Unsets an invalid css class
17248      * @method removeInvalidHandleClass
17249      * @param {string} cssClass the class of the element(s) you wish to
17250      * re-enable
17251      */
17252     removeInvalidHandleClass: function(cssClass) {
17253         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17254             if (this.invalidHandleClasses[i] == cssClass) {
17255                 delete this.invalidHandleClasses[i];
17256             }
17257         }
17258     },
17259
17260     /**
17261      * Checks the tag exclusion list to see if this click should be ignored
17262      * @method isValidHandleChild
17263      * @param {HTMLElement} node the HTMLElement to evaluate
17264      * @return {boolean} true if this is a valid tag type, false if not
17265      */
17266     isValidHandleChild: function(node) {
17267
17268         var valid = true;
17269         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17270         var nodeName;
17271         try {
17272             nodeName = node.nodeName.toUpperCase();
17273         } catch(e) {
17274             nodeName = node.nodeName;
17275         }
17276         valid = valid && !this.invalidHandleTypes[nodeName];
17277         valid = valid && !this.invalidHandleIds[node.id];
17278
17279         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17280             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17281         }
17282
17283
17284         return valid;
17285
17286     },
17287
17288     /**
17289      * Create the array of horizontal tick marks if an interval was specified
17290      * in setXConstraint().
17291      * @method setXTicks
17292      * @private
17293      */
17294     setXTicks: function(iStartX, iTickSize) {
17295         this.xTicks = [];
17296         this.xTickSize = iTickSize;
17297
17298         var tickMap = {};
17299
17300         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17301             if (!tickMap[i]) {
17302                 this.xTicks[this.xTicks.length] = i;
17303                 tickMap[i] = true;
17304             }
17305         }
17306
17307         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17308             if (!tickMap[i]) {
17309                 this.xTicks[this.xTicks.length] = i;
17310                 tickMap[i] = true;
17311             }
17312         }
17313
17314         this.xTicks.sort(this.DDM.numericSort) ;
17315     },
17316
17317     /**
17318      * Create the array of vertical tick marks if an interval was specified in
17319      * setYConstraint().
17320      * @method setYTicks
17321      * @private
17322      */
17323     setYTicks: function(iStartY, iTickSize) {
17324         this.yTicks = [];
17325         this.yTickSize = iTickSize;
17326
17327         var tickMap = {};
17328
17329         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17330             if (!tickMap[i]) {
17331                 this.yTicks[this.yTicks.length] = i;
17332                 tickMap[i] = true;
17333             }
17334         }
17335
17336         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17337             if (!tickMap[i]) {
17338                 this.yTicks[this.yTicks.length] = i;
17339                 tickMap[i] = true;
17340             }
17341         }
17342
17343         this.yTicks.sort(this.DDM.numericSort) ;
17344     },
17345
17346     /**
17347      * By default, the element can be dragged any place on the screen.  Use
17348      * this method to limit the horizontal travel of the element.  Pass in
17349      * 0,0 for the parameters if you want to lock the drag to the y axis.
17350      * @method setXConstraint
17351      * @param {int} iLeft the number of pixels the element can move to the left
17352      * @param {int} iRight the number of pixels the element can move to the
17353      * right
17354      * @param {int} iTickSize optional parameter for specifying that the
17355      * element
17356      * should move iTickSize pixels at a time.
17357      */
17358     setXConstraint: function(iLeft, iRight, iTickSize) {
17359         this.leftConstraint = iLeft;
17360         this.rightConstraint = iRight;
17361
17362         this.minX = this.initPageX - iLeft;
17363         this.maxX = this.initPageX + iRight;
17364         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17365
17366         this.constrainX = true;
17367     },
17368
17369     /**
17370      * Clears any constraints applied to this instance.  Also clears ticks
17371      * since they can't exist independent of a constraint at this time.
17372      * @method clearConstraints
17373      */
17374     clearConstraints: function() {
17375         this.constrainX = false;
17376         this.constrainY = false;
17377         this.clearTicks();
17378     },
17379
17380     /**
17381      * Clears any tick interval defined for this instance
17382      * @method clearTicks
17383      */
17384     clearTicks: function() {
17385         this.xTicks = null;
17386         this.yTicks = null;
17387         this.xTickSize = 0;
17388         this.yTickSize = 0;
17389     },
17390
17391     /**
17392      * By default, the element can be dragged any place on the screen.  Set
17393      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17394      * parameters if you want to lock the drag to the x axis.
17395      * @method setYConstraint
17396      * @param {int} iUp the number of pixels the element can move up
17397      * @param {int} iDown the number of pixels the element can move down
17398      * @param {int} iTickSize optional parameter for specifying that the
17399      * element should move iTickSize pixels at a time.
17400      */
17401     setYConstraint: function(iUp, iDown, iTickSize) {
17402         this.topConstraint = iUp;
17403         this.bottomConstraint = iDown;
17404
17405         this.minY = this.initPageY - iUp;
17406         this.maxY = this.initPageY + iDown;
17407         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17408
17409         this.constrainY = true;
17410
17411     },
17412
17413     /**
17414      * resetConstraints must be called if you manually reposition a dd element.
17415      * @method resetConstraints
17416      * @param {boolean} maintainOffset
17417      */
17418     resetConstraints: function() {
17419
17420
17421         // Maintain offsets if necessary
17422         if (this.initPageX || this.initPageX === 0) {
17423             // figure out how much this thing has moved
17424             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17425             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17426
17427             this.setInitPosition(dx, dy);
17428
17429         // This is the first time we have detected the element's position
17430         } else {
17431             this.setInitPosition();
17432         }
17433
17434         if (this.constrainX) {
17435             this.setXConstraint( this.leftConstraint,
17436                                  this.rightConstraint,
17437                                  this.xTickSize        );
17438         }
17439
17440         if (this.constrainY) {
17441             this.setYConstraint( this.topConstraint,
17442                                  this.bottomConstraint,
17443                                  this.yTickSize         );
17444         }
17445     },
17446
17447     /**
17448      * Normally the drag element is moved pixel by pixel, but we can specify
17449      * that it move a number of pixels at a time.  This method resolves the
17450      * location when we have it set up like this.
17451      * @method getTick
17452      * @param {int} val where we want to place the object
17453      * @param {int[]} tickArray sorted array of valid points
17454      * @return {int} the closest tick
17455      * @private
17456      */
17457     getTick: function(val, tickArray) {
17458
17459         if (!tickArray) {
17460             // If tick interval is not defined, it is effectively 1 pixel,
17461             // so we return the value passed to us.
17462             return val;
17463         } else if (tickArray[0] >= val) {
17464             // The value is lower than the first tick, so we return the first
17465             // tick.
17466             return tickArray[0];
17467         } else {
17468             for (var i=0, len=tickArray.length; i<len; ++i) {
17469                 var next = i + 1;
17470                 if (tickArray[next] && tickArray[next] >= val) {
17471                     var diff1 = val - tickArray[i];
17472                     var diff2 = tickArray[next] - val;
17473                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17474                 }
17475             }
17476
17477             // The value is larger than the last tick, so we return the last
17478             // tick.
17479             return tickArray[tickArray.length - 1];
17480         }
17481     },
17482
17483     /**
17484      * toString method
17485      * @method toString
17486      * @return {string} string representation of the dd obj
17487      */
17488     toString: function() {
17489         return ("DragDrop " + this.id);
17490     }
17491
17492 });
17493
17494 })();
17495 /*
17496  * Based on:
17497  * Ext JS Library 1.1.1
17498  * Copyright(c) 2006-2007, Ext JS, LLC.
17499  *
17500  * Originally Released Under LGPL - original licence link has changed is not relivant.
17501  *
17502  * Fork - LGPL
17503  * <script type="text/javascript">
17504  */
17505
17506
17507 /**
17508  * The drag and drop utility provides a framework for building drag and drop
17509  * applications.  In addition to enabling drag and drop for specific elements,
17510  * the drag and drop elements are tracked by the manager class, and the
17511  * interactions between the various elements are tracked during the drag and
17512  * the implementing code is notified about these important moments.
17513  */
17514
17515 // Only load the library once.  Rewriting the manager class would orphan
17516 // existing drag and drop instances.
17517 if (!Roo.dd.DragDropMgr) {
17518
17519 /**
17520  * @class Roo.dd.DragDropMgr
17521  * DragDropMgr is a singleton that tracks the element interaction for
17522  * all DragDrop items in the window.  Generally, you will not call
17523  * this class directly, but it does have helper methods that could
17524  * be useful in your DragDrop implementations.
17525  * @singleton
17526  */
17527 Roo.dd.DragDropMgr = function() {
17528
17529     var Event = Roo.EventManager;
17530
17531     return {
17532
17533         /**
17534          * Two dimensional Array of registered DragDrop objects.  The first
17535          * dimension is the DragDrop item group, the second the DragDrop
17536          * object.
17537          * @property ids
17538          * @type {string: string}
17539          * @private
17540          * @static
17541          */
17542         ids: {},
17543
17544         /**
17545          * Array of element ids defined as drag handles.  Used to determine
17546          * if the element that generated the mousedown event is actually the
17547          * handle and not the html element itself.
17548          * @property handleIds
17549          * @type {string: string}
17550          * @private
17551          * @static
17552          */
17553         handleIds: {},
17554
17555         /**
17556          * the DragDrop object that is currently being dragged
17557          * @property dragCurrent
17558          * @type DragDrop
17559          * @private
17560          * @static
17561          **/
17562         dragCurrent: null,
17563
17564         /**
17565          * the DragDrop object(s) that are being hovered over
17566          * @property dragOvers
17567          * @type Array
17568          * @private
17569          * @static
17570          */
17571         dragOvers: {},
17572
17573         /**
17574          * the X distance between the cursor and the object being dragged
17575          * @property deltaX
17576          * @type int
17577          * @private
17578          * @static
17579          */
17580         deltaX: 0,
17581
17582         /**
17583          * the Y distance between the cursor and the object being dragged
17584          * @property deltaY
17585          * @type int
17586          * @private
17587          * @static
17588          */
17589         deltaY: 0,
17590
17591         /**
17592          * Flag to determine if we should prevent the default behavior of the
17593          * events we define. By default this is true, but this can be set to
17594          * false if you need the default behavior (not recommended)
17595          * @property preventDefault
17596          * @type boolean
17597          * @static
17598          */
17599         preventDefault: true,
17600
17601         /**
17602          * Flag to determine if we should stop the propagation of the events
17603          * we generate. This is true by default but you may want to set it to
17604          * false if the html element contains other features that require the
17605          * mouse click.
17606          * @property stopPropagation
17607          * @type boolean
17608          * @static
17609          */
17610         stopPropagation: true,
17611
17612         /**
17613          * Internal flag that is set to true when drag and drop has been
17614          * intialized
17615          * @property initialized
17616          * @private
17617          * @static
17618          */
17619         initalized: false,
17620
17621         /**
17622          * All drag and drop can be disabled.
17623          * @property locked
17624          * @private
17625          * @static
17626          */
17627         locked: false,
17628
17629         /**
17630          * Called the first time an element is registered.
17631          * @method init
17632          * @private
17633          * @static
17634          */
17635         init: function() {
17636             this.initialized = true;
17637         },
17638
17639         /**
17640          * In point mode, drag and drop interaction is defined by the
17641          * location of the cursor during the drag/drop
17642          * @property POINT
17643          * @type int
17644          * @static
17645          */
17646         POINT: 0,
17647
17648         /**
17649          * In intersect mode, drag and drop interactio nis defined by the
17650          * overlap of two or more drag and drop objects.
17651          * @property INTERSECT
17652          * @type int
17653          * @static
17654          */
17655         INTERSECT: 1,
17656
17657         /**
17658          * The current drag and drop mode.  Default: POINT
17659          * @property mode
17660          * @type int
17661          * @static
17662          */
17663         mode: 0,
17664
17665         /**
17666          * Runs method on all drag and drop objects
17667          * @method _execOnAll
17668          * @private
17669          * @static
17670          */
17671         _execOnAll: function(sMethod, args) {
17672             for (var i in this.ids) {
17673                 for (var j in this.ids[i]) {
17674                     var oDD = this.ids[i][j];
17675                     if (! this.isTypeOfDD(oDD)) {
17676                         continue;
17677                     }
17678                     oDD[sMethod].apply(oDD, args);
17679                 }
17680             }
17681         },
17682
17683         /**
17684          * Drag and drop initialization.  Sets up the global event handlers
17685          * @method _onLoad
17686          * @private
17687          * @static
17688          */
17689         _onLoad: function() {
17690
17691             this.init();
17692
17693             if (!Roo.isTouch) {
17694                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17695                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17696             }
17697             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17698             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17699             
17700             Event.on(window,   "unload",    this._onUnload, this, true);
17701             Event.on(window,   "resize",    this._onResize, this, true);
17702             // Event.on(window,   "mouseout",    this._test);
17703
17704         },
17705
17706         /**
17707          * Reset constraints on all drag and drop objs
17708          * @method _onResize
17709          * @private
17710          * @static
17711          */
17712         _onResize: function(e) {
17713             this._execOnAll("resetConstraints", []);
17714         },
17715
17716         /**
17717          * Lock all drag and drop functionality
17718          * @method lock
17719          * @static
17720          */
17721         lock: function() { this.locked = true; },
17722
17723         /**
17724          * Unlock all drag and drop functionality
17725          * @method unlock
17726          * @static
17727          */
17728         unlock: function() { this.locked = false; },
17729
17730         /**
17731          * Is drag and drop locked?
17732          * @method isLocked
17733          * @return {boolean} True if drag and drop is locked, false otherwise.
17734          * @static
17735          */
17736         isLocked: function() { return this.locked; },
17737
17738         /**
17739          * Location cache that is set for all drag drop objects when a drag is
17740          * initiated, cleared when the drag is finished.
17741          * @property locationCache
17742          * @private
17743          * @static
17744          */
17745         locationCache: {},
17746
17747         /**
17748          * Set useCache to false if you want to force object the lookup of each
17749          * drag and drop linked element constantly during a drag.
17750          * @property useCache
17751          * @type boolean
17752          * @static
17753          */
17754         useCache: true,
17755
17756         /**
17757          * The number of pixels that the mouse needs to move after the
17758          * mousedown before the drag is initiated.  Default=3;
17759          * @property clickPixelThresh
17760          * @type int
17761          * @static
17762          */
17763         clickPixelThresh: 3,
17764
17765         /**
17766          * The number of milliseconds after the mousedown event to initiate the
17767          * drag if we don't get a mouseup event. Default=1000
17768          * @property clickTimeThresh
17769          * @type int
17770          * @static
17771          */
17772         clickTimeThresh: 350,
17773
17774         /**
17775          * Flag that indicates that either the drag pixel threshold or the
17776          * mousdown time threshold has been met
17777          * @property dragThreshMet
17778          * @type boolean
17779          * @private
17780          * @static
17781          */
17782         dragThreshMet: false,
17783
17784         /**
17785          * Timeout used for the click time threshold
17786          * @property clickTimeout
17787          * @type Object
17788          * @private
17789          * @static
17790          */
17791         clickTimeout: null,
17792
17793         /**
17794          * The X position of the mousedown event stored for later use when a
17795          * drag threshold is met.
17796          * @property startX
17797          * @type int
17798          * @private
17799          * @static
17800          */
17801         startX: 0,
17802
17803         /**
17804          * The Y position of the mousedown event stored for later use when a
17805          * drag threshold is met.
17806          * @property startY
17807          * @type int
17808          * @private
17809          * @static
17810          */
17811         startY: 0,
17812
17813         /**
17814          * Each DragDrop instance must be registered with the DragDropMgr.
17815          * This is executed in DragDrop.init()
17816          * @method regDragDrop
17817          * @param {DragDrop} oDD the DragDrop object to register
17818          * @param {String} sGroup the name of the group this element belongs to
17819          * @static
17820          */
17821         regDragDrop: function(oDD, sGroup) {
17822             if (!this.initialized) { this.init(); }
17823
17824             if (!this.ids[sGroup]) {
17825                 this.ids[sGroup] = {};
17826             }
17827             this.ids[sGroup][oDD.id] = oDD;
17828         },
17829
17830         /**
17831          * Removes the supplied dd instance from the supplied group. Executed
17832          * by DragDrop.removeFromGroup, so don't call this function directly.
17833          * @method removeDDFromGroup
17834          * @private
17835          * @static
17836          */
17837         removeDDFromGroup: function(oDD, sGroup) {
17838             if (!this.ids[sGroup]) {
17839                 this.ids[sGroup] = {};
17840             }
17841
17842             var obj = this.ids[sGroup];
17843             if (obj && obj[oDD.id]) {
17844                 delete obj[oDD.id];
17845             }
17846         },
17847
17848         /**
17849          * Unregisters a drag and drop item.  This is executed in
17850          * DragDrop.unreg, use that method instead of calling this directly.
17851          * @method _remove
17852          * @private
17853          * @static
17854          */
17855         _remove: function(oDD) {
17856             for (var g in oDD.groups) {
17857                 if (g && this.ids[g][oDD.id]) {
17858                     delete this.ids[g][oDD.id];
17859                 }
17860             }
17861             delete this.handleIds[oDD.id];
17862         },
17863
17864         /**
17865          * Each DragDrop handle element must be registered.  This is done
17866          * automatically when executing DragDrop.setHandleElId()
17867          * @method regHandle
17868          * @param {String} sDDId the DragDrop id this element is a handle for
17869          * @param {String} sHandleId the id of the element that is the drag
17870          * handle
17871          * @static
17872          */
17873         regHandle: function(sDDId, sHandleId) {
17874             if (!this.handleIds[sDDId]) {
17875                 this.handleIds[sDDId] = {};
17876             }
17877             this.handleIds[sDDId][sHandleId] = sHandleId;
17878         },
17879
17880         /**
17881          * Utility function to determine if a given element has been
17882          * registered as a drag drop item.
17883          * @method isDragDrop
17884          * @param {String} id the element id to check
17885          * @return {boolean} true if this element is a DragDrop item,
17886          * false otherwise
17887          * @static
17888          */
17889         isDragDrop: function(id) {
17890             return ( this.getDDById(id) ) ? true : false;
17891         },
17892
17893         /**
17894          * Returns the drag and drop instances that are in all groups the
17895          * passed in instance belongs to.
17896          * @method getRelated
17897          * @param {DragDrop} p_oDD the obj to get related data for
17898          * @param {boolean} bTargetsOnly if true, only return targetable objs
17899          * @return {DragDrop[]} the related instances
17900          * @static
17901          */
17902         getRelated: function(p_oDD, bTargetsOnly) {
17903             var oDDs = [];
17904             for (var i in p_oDD.groups) {
17905                 for (j in this.ids[i]) {
17906                     var dd = this.ids[i][j];
17907                     if (! this.isTypeOfDD(dd)) {
17908                         continue;
17909                     }
17910                     if (!bTargetsOnly || dd.isTarget) {
17911                         oDDs[oDDs.length] = dd;
17912                     }
17913                 }
17914             }
17915
17916             return oDDs;
17917         },
17918
17919         /**
17920          * Returns true if the specified dd target is a legal target for
17921          * the specifice drag obj
17922          * @method isLegalTarget
17923          * @param {DragDrop} the drag obj
17924          * @param {DragDrop} the target
17925          * @return {boolean} true if the target is a legal target for the
17926          * dd obj
17927          * @static
17928          */
17929         isLegalTarget: function (oDD, oTargetDD) {
17930             var targets = this.getRelated(oDD, true);
17931             for (var i=0, len=targets.length;i<len;++i) {
17932                 if (targets[i].id == oTargetDD.id) {
17933                     return true;
17934                 }
17935             }
17936
17937             return false;
17938         },
17939
17940         /**
17941          * My goal is to be able to transparently determine if an object is
17942          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17943          * returns "object", oDD.constructor.toString() always returns
17944          * "DragDrop" and not the name of the subclass.  So for now it just
17945          * evaluates a well-known variable in DragDrop.
17946          * @method isTypeOfDD
17947          * @param {Object} the object to evaluate
17948          * @return {boolean} true if typeof oDD = DragDrop
17949          * @static
17950          */
17951         isTypeOfDD: function (oDD) {
17952             return (oDD && oDD.__ygDragDrop);
17953         },
17954
17955         /**
17956          * Utility function to determine if a given element has been
17957          * registered as a drag drop handle for the given Drag Drop object.
17958          * @method isHandle
17959          * @param {String} id the element id to check
17960          * @return {boolean} true if this element is a DragDrop handle, false
17961          * otherwise
17962          * @static
17963          */
17964         isHandle: function(sDDId, sHandleId) {
17965             return ( this.handleIds[sDDId] &&
17966                             this.handleIds[sDDId][sHandleId] );
17967         },
17968
17969         /**
17970          * Returns the DragDrop instance for a given id
17971          * @method getDDById
17972          * @param {String} id the id of the DragDrop object
17973          * @return {DragDrop} the drag drop object, null if it is not found
17974          * @static
17975          */
17976         getDDById: function(id) {
17977             for (var i in this.ids) {
17978                 if (this.ids[i][id]) {
17979                     return this.ids[i][id];
17980                 }
17981             }
17982             return null;
17983         },
17984
17985         /**
17986          * Fired after a registered DragDrop object gets the mousedown event.
17987          * Sets up the events required to track the object being dragged
17988          * @method handleMouseDown
17989          * @param {Event} e the event
17990          * @param oDD the DragDrop object being dragged
17991          * @private
17992          * @static
17993          */
17994         handleMouseDown: function(e, oDD) {
17995             if(Roo.QuickTips){
17996                 Roo.QuickTips.disable();
17997             }
17998             this.currentTarget = e.getTarget();
17999
18000             this.dragCurrent = oDD;
18001
18002             var el = oDD.getEl();
18003
18004             // track start position
18005             this.startX = e.getPageX();
18006             this.startY = e.getPageY();
18007
18008             this.deltaX = this.startX - el.offsetLeft;
18009             this.deltaY = this.startY - el.offsetTop;
18010
18011             this.dragThreshMet = false;
18012
18013             this.clickTimeout = setTimeout(
18014                     function() {
18015                         var DDM = Roo.dd.DDM;
18016                         DDM.startDrag(DDM.startX, DDM.startY);
18017                     },
18018                     this.clickTimeThresh );
18019         },
18020
18021         /**
18022          * Fired when either the drag pixel threshol or the mousedown hold
18023          * time threshold has been met.
18024          * @method startDrag
18025          * @param x {int} the X position of the original mousedown
18026          * @param y {int} the Y position of the original mousedown
18027          * @static
18028          */
18029         startDrag: function(x, y) {
18030             clearTimeout(this.clickTimeout);
18031             if (this.dragCurrent) {
18032                 this.dragCurrent.b4StartDrag(x, y);
18033                 this.dragCurrent.startDrag(x, y);
18034             }
18035             this.dragThreshMet = true;
18036         },
18037
18038         /**
18039          * Internal function to handle the mouseup event.  Will be invoked
18040          * from the context of the document.
18041          * @method handleMouseUp
18042          * @param {Event} e the event
18043          * @private
18044          * @static
18045          */
18046         handleMouseUp: function(e) {
18047
18048             if(Roo.QuickTips){
18049                 Roo.QuickTips.enable();
18050             }
18051             if (! this.dragCurrent) {
18052                 return;
18053             }
18054
18055             clearTimeout(this.clickTimeout);
18056
18057             if (this.dragThreshMet) {
18058                 this.fireEvents(e, true);
18059             } else {
18060             }
18061
18062             this.stopDrag(e);
18063
18064             this.stopEvent(e);
18065         },
18066
18067         /**
18068          * Utility to stop event propagation and event default, if these
18069          * features are turned on.
18070          * @method stopEvent
18071          * @param {Event} e the event as returned by this.getEvent()
18072          * @static
18073          */
18074         stopEvent: function(e){
18075             if(this.stopPropagation) {
18076                 e.stopPropagation();
18077             }
18078
18079             if (this.preventDefault) {
18080                 e.preventDefault();
18081             }
18082         },
18083
18084         /**
18085          * Internal function to clean up event handlers after the drag
18086          * operation is complete
18087          * @method stopDrag
18088          * @param {Event} e the event
18089          * @private
18090          * @static
18091          */
18092         stopDrag: function(e) {
18093             // Fire the drag end event for the item that was dragged
18094             if (this.dragCurrent) {
18095                 if (this.dragThreshMet) {
18096                     this.dragCurrent.b4EndDrag(e);
18097                     this.dragCurrent.endDrag(e);
18098                 }
18099
18100                 this.dragCurrent.onMouseUp(e);
18101             }
18102
18103             this.dragCurrent = null;
18104             this.dragOvers = {};
18105         },
18106
18107         /**
18108          * Internal function to handle the mousemove event.  Will be invoked
18109          * from the context of the html element.
18110          *
18111          * @TODO figure out what we can do about mouse events lost when the
18112          * user drags objects beyond the window boundary.  Currently we can
18113          * detect this in internet explorer by verifying that the mouse is
18114          * down during the mousemove event.  Firefox doesn't give us the
18115          * button state on the mousemove event.
18116          * @method handleMouseMove
18117          * @param {Event} e the event
18118          * @private
18119          * @static
18120          */
18121         handleMouseMove: function(e) {
18122             if (! this.dragCurrent) {
18123                 return true;
18124             }
18125
18126             // var button = e.which || e.button;
18127
18128             // check for IE mouseup outside of page boundary
18129             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18130                 this.stopEvent(e);
18131                 return this.handleMouseUp(e);
18132             }
18133
18134             if (!this.dragThreshMet) {
18135                 var diffX = Math.abs(this.startX - e.getPageX());
18136                 var diffY = Math.abs(this.startY - e.getPageY());
18137                 if (diffX > this.clickPixelThresh ||
18138                             diffY > this.clickPixelThresh) {
18139                     this.startDrag(this.startX, this.startY);
18140                 }
18141             }
18142
18143             if (this.dragThreshMet) {
18144                 this.dragCurrent.b4Drag(e);
18145                 this.dragCurrent.onDrag(e);
18146                 if(!this.dragCurrent.moveOnly){
18147                     this.fireEvents(e, false);
18148                 }
18149             }
18150
18151             this.stopEvent(e);
18152
18153             return true;
18154         },
18155
18156         /**
18157          * Iterates over all of the DragDrop elements to find ones we are
18158          * hovering over or dropping on
18159          * @method fireEvents
18160          * @param {Event} e the event
18161          * @param {boolean} isDrop is this a drop op or a mouseover op?
18162          * @private
18163          * @static
18164          */
18165         fireEvents: function(e, isDrop) {
18166             var dc = this.dragCurrent;
18167
18168             // If the user did the mouse up outside of the window, we could
18169             // get here even though we have ended the drag.
18170             if (!dc || dc.isLocked()) {
18171                 return;
18172             }
18173
18174             var pt = e.getPoint();
18175
18176             // cache the previous dragOver array
18177             var oldOvers = [];
18178
18179             var outEvts   = [];
18180             var overEvts  = [];
18181             var dropEvts  = [];
18182             var enterEvts = [];
18183
18184             // Check to see if the object(s) we were hovering over is no longer
18185             // being hovered over so we can fire the onDragOut event
18186             for (var i in this.dragOvers) {
18187
18188                 var ddo = this.dragOvers[i];
18189
18190                 if (! this.isTypeOfDD(ddo)) {
18191                     continue;
18192                 }
18193
18194                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18195                     outEvts.push( ddo );
18196                 }
18197
18198                 oldOvers[i] = true;
18199                 delete this.dragOvers[i];
18200             }
18201
18202             for (var sGroup in dc.groups) {
18203
18204                 if ("string" != typeof sGroup) {
18205                     continue;
18206                 }
18207
18208                 for (i in this.ids[sGroup]) {
18209                     var oDD = this.ids[sGroup][i];
18210                     if (! this.isTypeOfDD(oDD)) {
18211                         continue;
18212                     }
18213
18214                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18215                         if (this.isOverTarget(pt, oDD, this.mode)) {
18216                             // look for drop interactions
18217                             if (isDrop) {
18218                                 dropEvts.push( oDD );
18219                             // look for drag enter and drag over interactions
18220                             } else {
18221
18222                                 // initial drag over: dragEnter fires
18223                                 if (!oldOvers[oDD.id]) {
18224                                     enterEvts.push( oDD );
18225                                 // subsequent drag overs: dragOver fires
18226                                 } else {
18227                                     overEvts.push( oDD );
18228                                 }
18229
18230                                 this.dragOvers[oDD.id] = oDD;
18231                             }
18232                         }
18233                     }
18234                 }
18235             }
18236
18237             if (this.mode) {
18238                 if (outEvts.length) {
18239                     dc.b4DragOut(e, outEvts);
18240                     dc.onDragOut(e, outEvts);
18241                 }
18242
18243                 if (enterEvts.length) {
18244                     dc.onDragEnter(e, enterEvts);
18245                 }
18246
18247                 if (overEvts.length) {
18248                     dc.b4DragOver(e, overEvts);
18249                     dc.onDragOver(e, overEvts);
18250                 }
18251
18252                 if (dropEvts.length) {
18253                     dc.b4DragDrop(e, dropEvts);
18254                     dc.onDragDrop(e, dropEvts);
18255                 }
18256
18257             } else {
18258                 // fire dragout events
18259                 var len = 0;
18260                 for (i=0, len=outEvts.length; i<len; ++i) {
18261                     dc.b4DragOut(e, outEvts[i].id);
18262                     dc.onDragOut(e, outEvts[i].id);
18263                 }
18264
18265                 // fire enter events
18266                 for (i=0,len=enterEvts.length; i<len; ++i) {
18267                     // dc.b4DragEnter(e, oDD.id);
18268                     dc.onDragEnter(e, enterEvts[i].id);
18269                 }
18270
18271                 // fire over events
18272                 for (i=0,len=overEvts.length; i<len; ++i) {
18273                     dc.b4DragOver(e, overEvts[i].id);
18274                     dc.onDragOver(e, overEvts[i].id);
18275                 }
18276
18277                 // fire drop events
18278                 for (i=0, len=dropEvts.length; i<len; ++i) {
18279                     dc.b4DragDrop(e, dropEvts[i].id);
18280                     dc.onDragDrop(e, dropEvts[i].id);
18281                 }
18282
18283             }
18284
18285             // notify about a drop that did not find a target
18286             if (isDrop && !dropEvts.length) {
18287                 dc.onInvalidDrop(e);
18288             }
18289
18290         },
18291
18292         /**
18293          * Helper function for getting the best match from the list of drag
18294          * and drop objects returned by the drag and drop events when we are
18295          * in INTERSECT mode.  It returns either the first object that the
18296          * cursor is over, or the object that has the greatest overlap with
18297          * the dragged element.
18298          * @method getBestMatch
18299          * @param  {DragDrop[]} dds The array of drag and drop objects
18300          * targeted
18301          * @return {DragDrop}       The best single match
18302          * @static
18303          */
18304         getBestMatch: function(dds) {
18305             var winner = null;
18306             // Return null if the input is not what we expect
18307             //if (!dds || !dds.length || dds.length == 0) {
18308                // winner = null;
18309             // If there is only one item, it wins
18310             //} else if (dds.length == 1) {
18311
18312             var len = dds.length;
18313
18314             if (len == 1) {
18315                 winner = dds[0];
18316             } else {
18317                 // Loop through the targeted items
18318                 for (var i=0; i<len; ++i) {
18319                     var dd = dds[i];
18320                     // If the cursor is over the object, it wins.  If the
18321                     // cursor is over multiple matches, the first one we come
18322                     // to wins.
18323                     if (dd.cursorIsOver) {
18324                         winner = dd;
18325                         break;
18326                     // Otherwise the object with the most overlap wins
18327                     } else {
18328                         if (!winner ||
18329                             winner.overlap.getArea() < dd.overlap.getArea()) {
18330                             winner = dd;
18331                         }
18332                     }
18333                 }
18334             }
18335
18336             return winner;
18337         },
18338
18339         /**
18340          * Refreshes the cache of the top-left and bottom-right points of the
18341          * drag and drop objects in the specified group(s).  This is in the
18342          * format that is stored in the drag and drop instance, so typical
18343          * usage is:
18344          * <code>
18345          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18346          * </code>
18347          * Alternatively:
18348          * <code>
18349          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18350          * </code>
18351          * @TODO this really should be an indexed array.  Alternatively this
18352          * method could accept both.
18353          * @method refreshCache
18354          * @param {Object} groups an associative array of groups to refresh
18355          * @static
18356          */
18357         refreshCache: function(groups) {
18358             for (var sGroup in groups) {
18359                 if ("string" != typeof sGroup) {
18360                     continue;
18361                 }
18362                 for (var i in this.ids[sGroup]) {
18363                     var oDD = this.ids[sGroup][i];
18364
18365                     if (this.isTypeOfDD(oDD)) {
18366                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18367                         var loc = this.getLocation(oDD);
18368                         if (loc) {
18369                             this.locationCache[oDD.id] = loc;
18370                         } else {
18371                             delete this.locationCache[oDD.id];
18372                             // this will unregister the drag and drop object if
18373                             // the element is not in a usable state
18374                             // oDD.unreg();
18375                         }
18376                     }
18377                 }
18378             }
18379         },
18380
18381         /**
18382          * This checks to make sure an element exists and is in the DOM.  The
18383          * main purpose is to handle cases where innerHTML is used to remove
18384          * drag and drop objects from the DOM.  IE provides an 'unspecified
18385          * error' when trying to access the offsetParent of such an element
18386          * @method verifyEl
18387          * @param {HTMLElement} el the element to check
18388          * @return {boolean} true if the element looks usable
18389          * @static
18390          */
18391         verifyEl: function(el) {
18392             if (el) {
18393                 var parent;
18394                 if(Roo.isIE){
18395                     try{
18396                         parent = el.offsetParent;
18397                     }catch(e){}
18398                 }else{
18399                     parent = el.offsetParent;
18400                 }
18401                 if (parent) {
18402                     return true;
18403                 }
18404             }
18405
18406             return false;
18407         },
18408
18409         /**
18410          * Returns a Region object containing the drag and drop element's position
18411          * and size, including the padding configured for it
18412          * @method getLocation
18413          * @param {DragDrop} oDD the drag and drop object to get the
18414          *                       location for
18415          * @return {Roo.lib.Region} a Region object representing the total area
18416          *                             the element occupies, including any padding
18417          *                             the instance is configured for.
18418          * @static
18419          */
18420         getLocation: function(oDD) {
18421             if (! this.isTypeOfDD(oDD)) {
18422                 return null;
18423             }
18424
18425             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18426
18427             try {
18428                 pos= Roo.lib.Dom.getXY(el);
18429             } catch (e) { }
18430
18431             if (!pos) {
18432                 return null;
18433             }
18434
18435             x1 = pos[0];
18436             x2 = x1 + el.offsetWidth;
18437             y1 = pos[1];
18438             y2 = y1 + el.offsetHeight;
18439
18440             t = y1 - oDD.padding[0];
18441             r = x2 + oDD.padding[1];
18442             b = y2 + oDD.padding[2];
18443             l = x1 - oDD.padding[3];
18444
18445             return new Roo.lib.Region( t, r, b, l );
18446         },
18447
18448         /**
18449          * Checks the cursor location to see if it over the target
18450          * @method isOverTarget
18451          * @param {Roo.lib.Point} pt The point to evaluate
18452          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18453          * @return {boolean} true if the mouse is over the target
18454          * @private
18455          * @static
18456          */
18457         isOverTarget: function(pt, oTarget, intersect) {
18458             // use cache if available
18459             var loc = this.locationCache[oTarget.id];
18460             if (!loc || !this.useCache) {
18461                 loc = this.getLocation(oTarget);
18462                 this.locationCache[oTarget.id] = loc;
18463
18464             }
18465
18466             if (!loc) {
18467                 return false;
18468             }
18469
18470             oTarget.cursorIsOver = loc.contains( pt );
18471
18472             // DragDrop is using this as a sanity check for the initial mousedown
18473             // in this case we are done.  In POINT mode, if the drag obj has no
18474             // contraints, we are also done. Otherwise we need to evaluate the
18475             // location of the target as related to the actual location of the
18476             // dragged element.
18477             var dc = this.dragCurrent;
18478             if (!dc || !dc.getTargetCoord ||
18479                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18480                 return oTarget.cursorIsOver;
18481             }
18482
18483             oTarget.overlap = null;
18484
18485             // Get the current location of the drag element, this is the
18486             // location of the mouse event less the delta that represents
18487             // where the original mousedown happened on the element.  We
18488             // need to consider constraints and ticks as well.
18489             var pos = dc.getTargetCoord(pt.x, pt.y);
18490
18491             var el = dc.getDragEl();
18492             var curRegion = new Roo.lib.Region( pos.y,
18493                                                    pos.x + el.offsetWidth,
18494                                                    pos.y + el.offsetHeight,
18495                                                    pos.x );
18496
18497             var overlap = curRegion.intersect(loc);
18498
18499             if (overlap) {
18500                 oTarget.overlap = overlap;
18501                 return (intersect) ? true : oTarget.cursorIsOver;
18502             } else {
18503                 return false;
18504             }
18505         },
18506
18507         /**
18508          * unload event handler
18509          * @method _onUnload
18510          * @private
18511          * @static
18512          */
18513         _onUnload: function(e, me) {
18514             Roo.dd.DragDropMgr.unregAll();
18515         },
18516
18517         /**
18518          * Cleans up the drag and drop events and objects.
18519          * @method unregAll
18520          * @private
18521          * @static
18522          */
18523         unregAll: function() {
18524
18525             if (this.dragCurrent) {
18526                 this.stopDrag();
18527                 this.dragCurrent = null;
18528             }
18529
18530             this._execOnAll("unreg", []);
18531
18532             for (i in this.elementCache) {
18533                 delete this.elementCache[i];
18534             }
18535
18536             this.elementCache = {};
18537             this.ids = {};
18538         },
18539
18540         /**
18541          * A cache of DOM elements
18542          * @property elementCache
18543          * @private
18544          * @static
18545          */
18546         elementCache: {},
18547
18548         /**
18549          * Get the wrapper for the DOM element specified
18550          * @method getElWrapper
18551          * @param {String} id the id of the element to get
18552          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18553          * @private
18554          * @deprecated This wrapper isn't that useful
18555          * @static
18556          */
18557         getElWrapper: function(id) {
18558             var oWrapper = this.elementCache[id];
18559             if (!oWrapper || !oWrapper.el) {
18560                 oWrapper = this.elementCache[id] =
18561                     new this.ElementWrapper(Roo.getDom(id));
18562             }
18563             return oWrapper;
18564         },
18565
18566         /**
18567          * Returns the actual DOM element
18568          * @method getElement
18569          * @param {String} id the id of the elment to get
18570          * @return {Object} The element
18571          * @deprecated use Roo.getDom instead
18572          * @static
18573          */
18574         getElement: function(id) {
18575             return Roo.getDom(id);
18576         },
18577
18578         /**
18579          * Returns the style property for the DOM element (i.e.,
18580          * document.getElById(id).style)
18581          * @method getCss
18582          * @param {String} id the id of the elment to get
18583          * @return {Object} The style property of the element
18584          * @deprecated use Roo.getDom instead
18585          * @static
18586          */
18587         getCss: function(id) {
18588             var el = Roo.getDom(id);
18589             return (el) ? el.style : null;
18590         },
18591
18592         /**
18593          * Inner class for cached elements
18594          * @class DragDropMgr.ElementWrapper
18595          * @for DragDropMgr
18596          * @private
18597          * @deprecated
18598          */
18599         ElementWrapper: function(el) {
18600                 /**
18601                  * The element
18602                  * @property el
18603                  */
18604                 this.el = el || null;
18605                 /**
18606                  * The element id
18607                  * @property id
18608                  */
18609                 this.id = this.el && el.id;
18610                 /**
18611                  * A reference to the style property
18612                  * @property css
18613                  */
18614                 this.css = this.el && el.style;
18615             },
18616
18617         /**
18618          * Returns the X position of an html element
18619          * @method getPosX
18620          * @param el the element for which to get the position
18621          * @return {int} the X coordinate
18622          * @for DragDropMgr
18623          * @deprecated use Roo.lib.Dom.getX instead
18624          * @static
18625          */
18626         getPosX: function(el) {
18627             return Roo.lib.Dom.getX(el);
18628         },
18629
18630         /**
18631          * Returns the Y position of an html element
18632          * @method getPosY
18633          * @param el the element for which to get the position
18634          * @return {int} the Y coordinate
18635          * @deprecated use Roo.lib.Dom.getY instead
18636          * @static
18637          */
18638         getPosY: function(el) {
18639             return Roo.lib.Dom.getY(el);
18640         },
18641
18642         /**
18643          * Swap two nodes.  In IE, we use the native method, for others we
18644          * emulate the IE behavior
18645          * @method swapNode
18646          * @param n1 the first node to swap
18647          * @param n2 the other node to swap
18648          * @static
18649          */
18650         swapNode: function(n1, n2) {
18651             if (n1.swapNode) {
18652                 n1.swapNode(n2);
18653             } else {
18654                 var p = n2.parentNode;
18655                 var s = n2.nextSibling;
18656
18657                 if (s == n1) {
18658                     p.insertBefore(n1, n2);
18659                 } else if (n2 == n1.nextSibling) {
18660                     p.insertBefore(n2, n1);
18661                 } else {
18662                     n1.parentNode.replaceChild(n2, n1);
18663                     p.insertBefore(n1, s);
18664                 }
18665             }
18666         },
18667
18668         /**
18669          * Returns the current scroll position
18670          * @method getScroll
18671          * @private
18672          * @static
18673          */
18674         getScroll: function () {
18675             var t, l, dde=document.documentElement, db=document.body;
18676             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18677                 t = dde.scrollTop;
18678                 l = dde.scrollLeft;
18679             } else if (db) {
18680                 t = db.scrollTop;
18681                 l = db.scrollLeft;
18682             } else {
18683
18684             }
18685             return { top: t, left: l };
18686         },
18687
18688         /**
18689          * Returns the specified element style property
18690          * @method getStyle
18691          * @param {HTMLElement} el          the element
18692          * @param {string}      styleProp   the style property
18693          * @return {string} The value of the style property
18694          * @deprecated use Roo.lib.Dom.getStyle
18695          * @static
18696          */
18697         getStyle: function(el, styleProp) {
18698             return Roo.fly(el).getStyle(styleProp);
18699         },
18700
18701         /**
18702          * Gets the scrollTop
18703          * @method getScrollTop
18704          * @return {int} the document's scrollTop
18705          * @static
18706          */
18707         getScrollTop: function () { return this.getScroll().top; },
18708
18709         /**
18710          * Gets the scrollLeft
18711          * @method getScrollLeft
18712          * @return {int} the document's scrollTop
18713          * @static
18714          */
18715         getScrollLeft: function () { return this.getScroll().left; },
18716
18717         /**
18718          * Sets the x/y position of an element to the location of the
18719          * target element.
18720          * @method moveToEl
18721          * @param {HTMLElement} moveEl      The element to move
18722          * @param {HTMLElement} targetEl    The position reference element
18723          * @static
18724          */
18725         moveToEl: function (moveEl, targetEl) {
18726             var aCoord = Roo.lib.Dom.getXY(targetEl);
18727             Roo.lib.Dom.setXY(moveEl, aCoord);
18728         },
18729
18730         /**
18731          * Numeric array sort function
18732          * @method numericSort
18733          * @static
18734          */
18735         numericSort: function(a, b) { return (a - b); },
18736
18737         /**
18738          * Internal counter
18739          * @property _timeoutCount
18740          * @private
18741          * @static
18742          */
18743         _timeoutCount: 0,
18744
18745         /**
18746          * Trying to make the load order less important.  Without this we get
18747          * an error if this file is loaded before the Event Utility.
18748          * @method _addListeners
18749          * @private
18750          * @static
18751          */
18752         _addListeners: function() {
18753             var DDM = Roo.dd.DDM;
18754             if ( Roo.lib.Event && document ) {
18755                 DDM._onLoad();
18756             } else {
18757                 if (DDM._timeoutCount > 2000) {
18758                 } else {
18759                     setTimeout(DDM._addListeners, 10);
18760                     if (document && document.body) {
18761                         DDM._timeoutCount += 1;
18762                     }
18763                 }
18764             }
18765         },
18766
18767         /**
18768          * Recursively searches the immediate parent and all child nodes for
18769          * the handle element in order to determine wheter or not it was
18770          * clicked.
18771          * @method handleWasClicked
18772          * @param node the html element to inspect
18773          * @static
18774          */
18775         handleWasClicked: function(node, id) {
18776             if (this.isHandle(id, node.id)) {
18777                 return true;
18778             } else {
18779                 // check to see if this is a text node child of the one we want
18780                 var p = node.parentNode;
18781
18782                 while (p) {
18783                     if (this.isHandle(id, p.id)) {
18784                         return true;
18785                     } else {
18786                         p = p.parentNode;
18787                     }
18788                 }
18789             }
18790
18791             return false;
18792         }
18793
18794     };
18795
18796 }();
18797
18798 // shorter alias, save a few bytes
18799 Roo.dd.DDM = Roo.dd.DragDropMgr;
18800 Roo.dd.DDM._addListeners();
18801
18802 }/*
18803  * Based on:
18804  * Ext JS Library 1.1.1
18805  * Copyright(c) 2006-2007, Ext JS, LLC.
18806  *
18807  * Originally Released Under LGPL - original licence link has changed is not relivant.
18808  *
18809  * Fork - LGPL
18810  * <script type="text/javascript">
18811  */
18812
18813 /**
18814  * @class Roo.dd.DD
18815  * A DragDrop implementation where the linked element follows the
18816  * mouse cursor during a drag.
18817  * @extends Roo.dd.DragDrop
18818  * @constructor
18819  * @param {String} id the id of the linked element
18820  * @param {String} sGroup the group of related DragDrop items
18821  * @param {object} config an object containing configurable attributes
18822  *                Valid properties for DD:
18823  *                    scroll
18824  */
18825 Roo.dd.DD = function(id, sGroup, config) {
18826     if (id) {
18827         this.init(id, sGroup, config);
18828     }
18829 };
18830
18831 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18832
18833     /**
18834      * When set to true, the utility automatically tries to scroll the browser
18835      * window wehn a drag and drop element is dragged near the viewport boundary.
18836      * Defaults to true.
18837      * @property scroll
18838      * @type boolean
18839      */
18840     scroll: true,
18841
18842     /**
18843      * Sets the pointer offset to the distance between the linked element's top
18844      * left corner and the location the element was clicked
18845      * @method autoOffset
18846      * @param {int} iPageX the X coordinate of the click
18847      * @param {int} iPageY the Y coordinate of the click
18848      */
18849     autoOffset: function(iPageX, iPageY) {
18850         var x = iPageX - this.startPageX;
18851         var y = iPageY - this.startPageY;
18852         this.setDelta(x, y);
18853     },
18854
18855     /**
18856      * Sets the pointer offset.  You can call this directly to force the
18857      * offset to be in a particular location (e.g., pass in 0,0 to set it
18858      * to the center of the object)
18859      * @method setDelta
18860      * @param {int} iDeltaX the distance from the left
18861      * @param {int} iDeltaY the distance from the top
18862      */
18863     setDelta: function(iDeltaX, iDeltaY) {
18864         this.deltaX = iDeltaX;
18865         this.deltaY = iDeltaY;
18866     },
18867
18868     /**
18869      * Sets the drag element to the location of the mousedown or click event,
18870      * maintaining the cursor location relative to the location on the element
18871      * that was clicked.  Override this if you want to place the element in a
18872      * location other than where the cursor is.
18873      * @method setDragElPos
18874      * @param {int} iPageX the X coordinate of the mousedown or drag event
18875      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18876      */
18877     setDragElPos: function(iPageX, iPageY) {
18878         // the first time we do this, we are going to check to make sure
18879         // the element has css positioning
18880
18881         var el = this.getDragEl();
18882         this.alignElWithMouse(el, iPageX, iPageY);
18883     },
18884
18885     /**
18886      * Sets the element to the location of the mousedown or click event,
18887      * maintaining the cursor location relative to the location on the element
18888      * that was clicked.  Override this if you want to place the element in a
18889      * location other than where the cursor is.
18890      * @method alignElWithMouse
18891      * @param {HTMLElement} el the element to move
18892      * @param {int} iPageX the X coordinate of the mousedown or drag event
18893      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18894      */
18895     alignElWithMouse: function(el, iPageX, iPageY) {
18896         var oCoord = this.getTargetCoord(iPageX, iPageY);
18897         var fly = el.dom ? el : Roo.fly(el);
18898         if (!this.deltaSetXY) {
18899             var aCoord = [oCoord.x, oCoord.y];
18900             fly.setXY(aCoord);
18901             var newLeft = fly.getLeft(true);
18902             var newTop  = fly.getTop(true);
18903             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18904         } else {
18905             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18906         }
18907
18908         this.cachePosition(oCoord.x, oCoord.y);
18909         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18910         return oCoord;
18911     },
18912
18913     /**
18914      * Saves the most recent position so that we can reset the constraints and
18915      * tick marks on-demand.  We need to know this so that we can calculate the
18916      * number of pixels the element is offset from its original position.
18917      * @method cachePosition
18918      * @param iPageX the current x position (optional, this just makes it so we
18919      * don't have to look it up again)
18920      * @param iPageY the current y position (optional, this just makes it so we
18921      * don't have to look it up again)
18922      */
18923     cachePosition: function(iPageX, iPageY) {
18924         if (iPageX) {
18925             this.lastPageX = iPageX;
18926             this.lastPageY = iPageY;
18927         } else {
18928             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18929             this.lastPageX = aCoord[0];
18930             this.lastPageY = aCoord[1];
18931         }
18932     },
18933
18934     /**
18935      * Auto-scroll the window if the dragged object has been moved beyond the
18936      * visible window boundary.
18937      * @method autoScroll
18938      * @param {int} x the drag element's x position
18939      * @param {int} y the drag element's y position
18940      * @param {int} h the height of the drag element
18941      * @param {int} w the width of the drag element
18942      * @private
18943      */
18944     autoScroll: function(x, y, h, w) {
18945
18946         if (this.scroll) {
18947             // The client height
18948             var clientH = Roo.lib.Dom.getViewWidth();
18949
18950             // The client width
18951             var clientW = Roo.lib.Dom.getViewHeight();
18952
18953             // The amt scrolled down
18954             var st = this.DDM.getScrollTop();
18955
18956             // The amt scrolled right
18957             var sl = this.DDM.getScrollLeft();
18958
18959             // Location of the bottom of the element
18960             var bot = h + y;
18961
18962             // Location of the right of the element
18963             var right = w + x;
18964
18965             // The distance from the cursor to the bottom of the visible area,
18966             // adjusted so that we don't scroll if the cursor is beyond the
18967             // element drag constraints
18968             var toBot = (clientH + st - y - this.deltaY);
18969
18970             // The distance from the cursor to the right of the visible area
18971             var toRight = (clientW + sl - x - this.deltaX);
18972
18973
18974             // How close to the edge the cursor must be before we scroll
18975             // var thresh = (document.all) ? 100 : 40;
18976             var thresh = 40;
18977
18978             // How many pixels to scroll per autoscroll op.  This helps to reduce
18979             // clunky scrolling. IE is more sensitive about this ... it needs this
18980             // value to be higher.
18981             var scrAmt = (document.all) ? 80 : 30;
18982
18983             // Scroll down if we are near the bottom of the visible page and the
18984             // obj extends below the crease
18985             if ( bot > clientH && toBot < thresh ) {
18986                 window.scrollTo(sl, st + scrAmt);
18987             }
18988
18989             // Scroll up if the window is scrolled down and the top of the object
18990             // goes above the top border
18991             if ( y < st && st > 0 && y - st < thresh ) {
18992                 window.scrollTo(sl, st - scrAmt);
18993             }
18994
18995             // Scroll right if the obj is beyond the right border and the cursor is
18996             // near the border.
18997             if ( right > clientW && toRight < thresh ) {
18998                 window.scrollTo(sl + scrAmt, st);
18999             }
19000
19001             // Scroll left if the window has been scrolled to the right and the obj
19002             // extends past the left border
19003             if ( x < sl && sl > 0 && x - sl < thresh ) {
19004                 window.scrollTo(sl - scrAmt, st);
19005             }
19006         }
19007     },
19008
19009     /**
19010      * Finds the location the element should be placed if we want to move
19011      * it to where the mouse location less the click offset would place us.
19012      * @method getTargetCoord
19013      * @param {int} iPageX the X coordinate of the click
19014      * @param {int} iPageY the Y coordinate of the click
19015      * @return an object that contains the coordinates (Object.x and Object.y)
19016      * @private
19017      */
19018     getTargetCoord: function(iPageX, iPageY) {
19019
19020
19021         var x = iPageX - this.deltaX;
19022         var y = iPageY - this.deltaY;
19023
19024         if (this.constrainX) {
19025             if (x < this.minX) { x = this.minX; }
19026             if (x > this.maxX) { x = this.maxX; }
19027         }
19028
19029         if (this.constrainY) {
19030             if (y < this.minY) { y = this.minY; }
19031             if (y > this.maxY) { y = this.maxY; }
19032         }
19033
19034         x = this.getTick(x, this.xTicks);
19035         y = this.getTick(y, this.yTicks);
19036
19037
19038         return {x:x, y:y};
19039     },
19040
19041     /*
19042      * Sets up config options specific to this class. Overrides
19043      * Roo.dd.DragDrop, but all versions of this method through the
19044      * inheritance chain are called
19045      */
19046     applyConfig: function() {
19047         Roo.dd.DD.superclass.applyConfig.call(this);
19048         this.scroll = (this.config.scroll !== false);
19049     },
19050
19051     /*
19052      * Event that fires prior to the onMouseDown event.  Overrides
19053      * Roo.dd.DragDrop.
19054      */
19055     b4MouseDown: function(e) {
19056         // this.resetConstraints();
19057         this.autoOffset(e.getPageX(),
19058                             e.getPageY());
19059     },
19060
19061     /*
19062      * Event that fires prior to the onDrag event.  Overrides
19063      * Roo.dd.DragDrop.
19064      */
19065     b4Drag: function(e) {
19066         this.setDragElPos(e.getPageX(),
19067                             e.getPageY());
19068     },
19069
19070     toString: function() {
19071         return ("DD " + this.id);
19072     }
19073
19074     //////////////////////////////////////////////////////////////////////////
19075     // Debugging ygDragDrop events that can be overridden
19076     //////////////////////////////////////////////////////////////////////////
19077     /*
19078     startDrag: function(x, y) {
19079     },
19080
19081     onDrag: function(e) {
19082     },
19083
19084     onDragEnter: function(e, id) {
19085     },
19086
19087     onDragOver: function(e, id) {
19088     },
19089
19090     onDragOut: function(e, id) {
19091     },
19092
19093     onDragDrop: function(e, id) {
19094     },
19095
19096     endDrag: function(e) {
19097     }
19098
19099     */
19100
19101 });/*
19102  * Based on:
19103  * Ext JS Library 1.1.1
19104  * Copyright(c) 2006-2007, Ext JS, LLC.
19105  *
19106  * Originally Released Under LGPL - original licence link has changed is not relivant.
19107  *
19108  * Fork - LGPL
19109  * <script type="text/javascript">
19110  */
19111
19112 /**
19113  * @class Roo.dd.DDProxy
19114  * A DragDrop implementation that inserts an empty, bordered div into
19115  * the document that follows the cursor during drag operations.  At the time of
19116  * the click, the frame div is resized to the dimensions of the linked html
19117  * element, and moved to the exact location of the linked element.
19118  *
19119  * References to the "frame" element refer to the single proxy element that
19120  * was created to be dragged in place of all DDProxy elements on the
19121  * page.
19122  *
19123  * @extends Roo.dd.DD
19124  * @constructor
19125  * @param {String} id the id of the linked html element
19126  * @param {String} sGroup the group of related DragDrop objects
19127  * @param {object} config an object containing configurable attributes
19128  *                Valid properties for DDProxy in addition to those in DragDrop:
19129  *                   resizeFrame, centerFrame, dragElId
19130  */
19131 Roo.dd.DDProxy = function(id, sGroup, config) {
19132     if (id) {
19133         this.init(id, sGroup, config);
19134         this.initFrame();
19135     }
19136 };
19137
19138 /**
19139  * The default drag frame div id
19140  * @property Roo.dd.DDProxy.dragElId
19141  * @type String
19142  * @static
19143  */
19144 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19145
19146 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19147
19148     /**
19149      * By default we resize the drag frame to be the same size as the element
19150      * we want to drag (this is to get the frame effect).  We can turn it off
19151      * if we want a different behavior.
19152      * @property resizeFrame
19153      * @type boolean
19154      */
19155     resizeFrame: true,
19156
19157     /**
19158      * By default the frame is positioned exactly where the drag element is, so
19159      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19160      * you do not have constraints on the obj is to have the drag frame centered
19161      * around the cursor.  Set centerFrame to true for this effect.
19162      * @property centerFrame
19163      * @type boolean
19164      */
19165     centerFrame: false,
19166
19167     /**
19168      * Creates the proxy element if it does not yet exist
19169      * @method createFrame
19170      */
19171     createFrame: function() {
19172         var self = this;
19173         var body = document.body;
19174
19175         if (!body || !body.firstChild) {
19176             setTimeout( function() { self.createFrame(); }, 50 );
19177             return;
19178         }
19179
19180         var div = this.getDragEl();
19181
19182         if (!div) {
19183             div    = document.createElement("div");
19184             div.id = this.dragElId;
19185             var s  = div.style;
19186
19187             s.position   = "absolute";
19188             s.visibility = "hidden";
19189             s.cursor     = "move";
19190             s.border     = "2px solid #aaa";
19191             s.zIndex     = 999;
19192
19193             // appendChild can blow up IE if invoked prior to the window load event
19194             // while rendering a table.  It is possible there are other scenarios
19195             // that would cause this to happen as well.
19196             body.insertBefore(div, body.firstChild);
19197         }
19198     },
19199
19200     /**
19201      * Initialization for the drag frame element.  Must be called in the
19202      * constructor of all subclasses
19203      * @method initFrame
19204      */
19205     initFrame: function() {
19206         this.createFrame();
19207     },
19208
19209     applyConfig: function() {
19210         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19211
19212         this.resizeFrame = (this.config.resizeFrame !== false);
19213         this.centerFrame = (this.config.centerFrame);
19214         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19215     },
19216
19217     /**
19218      * Resizes the drag frame to the dimensions of the clicked object, positions
19219      * it over the object, and finally displays it
19220      * @method showFrame
19221      * @param {int} iPageX X click position
19222      * @param {int} iPageY Y click position
19223      * @private
19224      */
19225     showFrame: function(iPageX, iPageY) {
19226         var el = this.getEl();
19227         var dragEl = this.getDragEl();
19228         var s = dragEl.style;
19229
19230         this._resizeProxy();
19231
19232         if (this.centerFrame) {
19233             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19234                            Math.round(parseInt(s.height, 10)/2) );
19235         }
19236
19237         this.setDragElPos(iPageX, iPageY);
19238
19239         Roo.fly(dragEl).show();
19240     },
19241
19242     /**
19243      * The proxy is automatically resized to the dimensions of the linked
19244      * element when a drag is initiated, unless resizeFrame is set to false
19245      * @method _resizeProxy
19246      * @private
19247      */
19248     _resizeProxy: function() {
19249         if (this.resizeFrame) {
19250             var el = this.getEl();
19251             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19252         }
19253     },
19254
19255     // overrides Roo.dd.DragDrop
19256     b4MouseDown: function(e) {
19257         var x = e.getPageX();
19258         var y = e.getPageY();
19259         this.autoOffset(x, y);
19260         this.setDragElPos(x, y);
19261     },
19262
19263     // overrides Roo.dd.DragDrop
19264     b4StartDrag: function(x, y) {
19265         // show the drag frame
19266         this.showFrame(x, y);
19267     },
19268
19269     // overrides Roo.dd.DragDrop
19270     b4EndDrag: function(e) {
19271         Roo.fly(this.getDragEl()).hide();
19272     },
19273
19274     // overrides Roo.dd.DragDrop
19275     // By default we try to move the element to the last location of the frame.
19276     // This is so that the default behavior mirrors that of Roo.dd.DD.
19277     endDrag: function(e) {
19278
19279         var lel = this.getEl();
19280         var del = this.getDragEl();
19281
19282         // Show the drag frame briefly so we can get its position
19283         del.style.visibility = "";
19284
19285         this.beforeMove();
19286         // Hide the linked element before the move to get around a Safari
19287         // rendering bug.
19288         lel.style.visibility = "hidden";
19289         Roo.dd.DDM.moveToEl(lel, del);
19290         del.style.visibility = "hidden";
19291         lel.style.visibility = "";
19292
19293         this.afterDrag();
19294     },
19295
19296     beforeMove : function(){
19297
19298     },
19299
19300     afterDrag : function(){
19301
19302     },
19303
19304     toString: function() {
19305         return ("DDProxy " + this.id);
19306     }
19307
19308 });
19309 /*
19310  * Based on:
19311  * Ext JS Library 1.1.1
19312  * Copyright(c) 2006-2007, Ext JS, LLC.
19313  *
19314  * Originally Released Under LGPL - original licence link has changed is not relivant.
19315  *
19316  * Fork - LGPL
19317  * <script type="text/javascript">
19318  */
19319
19320  /**
19321  * @class Roo.dd.DDTarget
19322  * A DragDrop implementation that does not move, but can be a drop
19323  * target.  You would get the same result by simply omitting implementation
19324  * for the event callbacks, but this way we reduce the processing cost of the
19325  * event listener and the callbacks.
19326  * @extends Roo.dd.DragDrop
19327  * @constructor
19328  * @param {String} id the id of the element that is a drop target
19329  * @param {String} sGroup the group of related DragDrop objects
19330  * @param {object} config an object containing configurable attributes
19331  *                 Valid properties for DDTarget in addition to those in
19332  *                 DragDrop:
19333  *                    none
19334  */
19335 Roo.dd.DDTarget = function(id, sGroup, config) {
19336     if (id) {
19337         this.initTarget(id, sGroup, config);
19338     }
19339     if (config.listeners || config.events) { 
19340        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19341             listeners : config.listeners || {}, 
19342             events : config.events || {} 
19343         });    
19344     }
19345 };
19346
19347 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19348 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19349     toString: function() {
19350         return ("DDTarget " + this.id);
19351     }
19352 });
19353 /*
19354  * Based on:
19355  * Ext JS Library 1.1.1
19356  * Copyright(c) 2006-2007, Ext JS, LLC.
19357  *
19358  * Originally Released Under LGPL - original licence link has changed is not relivant.
19359  *
19360  * Fork - LGPL
19361  * <script type="text/javascript">
19362  */
19363  
19364
19365 /**
19366  * @class Roo.dd.ScrollManager
19367  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19368  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19369  * @singleton
19370  */
19371 Roo.dd.ScrollManager = function(){
19372     var ddm = Roo.dd.DragDropMgr;
19373     var els = {};
19374     var dragEl = null;
19375     var proc = {};
19376     
19377     
19378     
19379     var onStop = function(e){
19380         dragEl = null;
19381         clearProc();
19382     };
19383     
19384     var triggerRefresh = function(){
19385         if(ddm.dragCurrent){
19386              ddm.refreshCache(ddm.dragCurrent.groups);
19387         }
19388     };
19389     
19390     var doScroll = function(){
19391         if(ddm.dragCurrent){
19392             var dds = Roo.dd.ScrollManager;
19393             if(!dds.animate){
19394                 if(proc.el.scroll(proc.dir, dds.increment)){
19395                     triggerRefresh();
19396                 }
19397             }else{
19398                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19399             }
19400         }
19401     };
19402     
19403     var clearProc = function(){
19404         if(proc.id){
19405             clearInterval(proc.id);
19406         }
19407         proc.id = 0;
19408         proc.el = null;
19409         proc.dir = "";
19410     };
19411     
19412     var startProc = function(el, dir){
19413          Roo.log('scroll startproc');
19414         clearProc();
19415         proc.el = el;
19416         proc.dir = dir;
19417         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19418     };
19419     
19420     var onFire = function(e, isDrop){
19421        
19422         if(isDrop || !ddm.dragCurrent){ return; }
19423         var dds = Roo.dd.ScrollManager;
19424         if(!dragEl || dragEl != ddm.dragCurrent){
19425             dragEl = ddm.dragCurrent;
19426             // refresh regions on drag start
19427             dds.refreshCache();
19428         }
19429         
19430         var xy = Roo.lib.Event.getXY(e);
19431         var pt = new Roo.lib.Point(xy[0], xy[1]);
19432         for(var id in els){
19433             var el = els[id], r = el._region;
19434             if(r && r.contains(pt) && el.isScrollable()){
19435                 if(r.bottom - pt.y <= dds.thresh){
19436                     if(proc.el != el){
19437                         startProc(el, "down");
19438                     }
19439                     return;
19440                 }else if(r.right - pt.x <= dds.thresh){
19441                     if(proc.el != el){
19442                         startProc(el, "left");
19443                     }
19444                     return;
19445                 }else if(pt.y - r.top <= dds.thresh){
19446                     if(proc.el != el){
19447                         startProc(el, "up");
19448                     }
19449                     return;
19450                 }else if(pt.x - r.left <= dds.thresh){
19451                     if(proc.el != el){
19452                         startProc(el, "right");
19453                     }
19454                     return;
19455                 }
19456             }
19457         }
19458         clearProc();
19459     };
19460     
19461     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19462     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19463     
19464     return {
19465         /**
19466          * Registers new overflow element(s) to auto scroll
19467          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19468          */
19469         register : function(el){
19470             if(el instanceof Array){
19471                 for(var i = 0, len = el.length; i < len; i++) {
19472                         this.register(el[i]);
19473                 }
19474             }else{
19475                 el = Roo.get(el);
19476                 els[el.id] = el;
19477             }
19478             Roo.dd.ScrollManager.els = els;
19479         },
19480         
19481         /**
19482          * Unregisters overflow element(s) so they are no longer scrolled
19483          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19484          */
19485         unregister : function(el){
19486             if(el instanceof Array){
19487                 for(var i = 0, len = el.length; i < len; i++) {
19488                         this.unregister(el[i]);
19489                 }
19490             }else{
19491                 el = Roo.get(el);
19492                 delete els[el.id];
19493             }
19494         },
19495         
19496         /**
19497          * The number of pixels from the edge of a container the pointer needs to be to 
19498          * trigger scrolling (defaults to 25)
19499          * @type Number
19500          */
19501         thresh : 25,
19502         
19503         /**
19504          * The number of pixels to scroll in each scroll increment (defaults to 50)
19505          * @type Number
19506          */
19507         increment : 100,
19508         
19509         /**
19510          * The frequency of scrolls in milliseconds (defaults to 500)
19511          * @type Number
19512          */
19513         frequency : 500,
19514         
19515         /**
19516          * True to animate the scroll (defaults to true)
19517          * @type Boolean
19518          */
19519         animate: true,
19520         
19521         /**
19522          * The animation duration in seconds - 
19523          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19524          * @type Number
19525          */
19526         animDuration: .4,
19527         
19528         /**
19529          * Manually trigger a cache refresh.
19530          */
19531         refreshCache : function(){
19532             for(var id in els){
19533                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19534                     els[id]._region = els[id].getRegion();
19535                 }
19536             }
19537         }
19538     };
19539 }();/*
19540  * Based on:
19541  * Ext JS Library 1.1.1
19542  * Copyright(c) 2006-2007, Ext JS, LLC.
19543  *
19544  * Originally Released Under LGPL - original licence link has changed is not relivant.
19545  *
19546  * Fork - LGPL
19547  * <script type="text/javascript">
19548  */
19549  
19550
19551 /**
19552  * @class Roo.dd.Registry
19553  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19554  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19555  * @singleton
19556  */
19557 Roo.dd.Registry = function(){
19558     var elements = {}; 
19559     var handles = {}; 
19560     var autoIdSeed = 0;
19561
19562     var getId = function(el, autogen){
19563         if(typeof el == "string"){
19564             return el;
19565         }
19566         var id = el.id;
19567         if(!id && autogen !== false){
19568             id = "roodd-" + (++autoIdSeed);
19569             el.id = id;
19570         }
19571         return id;
19572     };
19573     
19574     return {
19575     /**
19576      * Register a drag drop element
19577      * @param {String|HTMLElement} element The id or DOM node to register
19578      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19579      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19580      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19581      * populated in the data object (if applicable):
19582      * <pre>
19583 Value      Description<br />
19584 ---------  ------------------------------------------<br />
19585 handles    Array of DOM nodes that trigger dragging<br />
19586            for the element being registered<br />
19587 isHandle   True if the element passed in triggers<br />
19588            dragging itself, else false
19589 </pre>
19590      */
19591         register : function(el, data){
19592             data = data || {};
19593             if(typeof el == "string"){
19594                 el = document.getElementById(el);
19595             }
19596             data.ddel = el;
19597             elements[getId(el)] = data;
19598             if(data.isHandle !== false){
19599                 handles[data.ddel.id] = data;
19600             }
19601             if(data.handles){
19602                 var hs = data.handles;
19603                 for(var i = 0, len = hs.length; i < len; i++){
19604                         handles[getId(hs[i])] = data;
19605                 }
19606             }
19607         },
19608
19609     /**
19610      * Unregister a drag drop element
19611      * @param {String|HTMLElement}  element The id or DOM node to unregister
19612      */
19613         unregister : function(el){
19614             var id = getId(el, false);
19615             var data = elements[id];
19616             if(data){
19617                 delete elements[id];
19618                 if(data.handles){
19619                     var hs = data.handles;
19620                     for(var i = 0, len = hs.length; i < len; i++){
19621                         delete handles[getId(hs[i], false)];
19622                     }
19623                 }
19624             }
19625         },
19626
19627     /**
19628      * Returns the handle registered for a DOM Node by id
19629      * @param {String|HTMLElement} id The DOM node or id to look up
19630      * @return {Object} handle The custom handle data
19631      */
19632         getHandle : function(id){
19633             if(typeof id != "string"){ // must be element?
19634                 id = id.id;
19635             }
19636             return handles[id];
19637         },
19638
19639     /**
19640      * Returns the handle that is registered for the DOM node that is the target of the event
19641      * @param {Event} e The event
19642      * @return {Object} handle The custom handle data
19643      */
19644         getHandleFromEvent : function(e){
19645             var t = Roo.lib.Event.getTarget(e);
19646             return t ? handles[t.id] : null;
19647         },
19648
19649     /**
19650      * Returns a custom data object that is registered for a DOM node by id
19651      * @param {String|HTMLElement} id The DOM node or id to look up
19652      * @return {Object} data The custom data
19653      */
19654         getTarget : function(id){
19655             if(typeof id != "string"){ // must be element?
19656                 id = id.id;
19657             }
19658             return elements[id];
19659         },
19660
19661     /**
19662      * Returns a custom data object that is registered for the DOM node that is the target of the event
19663      * @param {Event} e The event
19664      * @return {Object} data The custom data
19665      */
19666         getTargetFromEvent : function(e){
19667             var t = Roo.lib.Event.getTarget(e);
19668             return t ? elements[t.id] || handles[t.id] : null;
19669         }
19670     };
19671 }();/*
19672  * Based on:
19673  * Ext JS Library 1.1.1
19674  * Copyright(c) 2006-2007, Ext JS, LLC.
19675  *
19676  * Originally Released Under LGPL - original licence link has changed is not relivant.
19677  *
19678  * Fork - LGPL
19679  * <script type="text/javascript">
19680  */
19681  
19682
19683 /**
19684  * @class Roo.dd.StatusProxy
19685  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19686  * default drag proxy used by all Roo.dd components.
19687  * @constructor
19688  * @param {Object} config
19689  */
19690 Roo.dd.StatusProxy = function(config){
19691     Roo.apply(this, config);
19692     this.id = this.id || Roo.id();
19693     this.el = new Roo.Layer({
19694         dh: {
19695             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19696                 {tag: "div", cls: "x-dd-drop-icon"},
19697                 {tag: "div", cls: "x-dd-drag-ghost"}
19698             ]
19699         }, 
19700         shadow: !config || config.shadow !== false
19701     });
19702     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19703     this.dropStatus = this.dropNotAllowed;
19704 };
19705
19706 Roo.dd.StatusProxy.prototype = {
19707     /**
19708      * @cfg {String} dropAllowed
19709      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19710      */
19711     dropAllowed : "x-dd-drop-ok",
19712     /**
19713      * @cfg {String} dropNotAllowed
19714      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19715      */
19716     dropNotAllowed : "x-dd-drop-nodrop",
19717
19718     /**
19719      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19720      * over the current target element.
19721      * @param {String} cssClass The css class for the new drop status indicator image
19722      */
19723     setStatus : function(cssClass){
19724         cssClass = cssClass || this.dropNotAllowed;
19725         if(this.dropStatus != cssClass){
19726             this.el.replaceClass(this.dropStatus, cssClass);
19727             this.dropStatus = cssClass;
19728         }
19729     },
19730
19731     /**
19732      * Resets the status indicator to the default dropNotAllowed value
19733      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19734      */
19735     reset : function(clearGhost){
19736         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19737         this.dropStatus = this.dropNotAllowed;
19738         if(clearGhost){
19739             this.ghost.update("");
19740         }
19741     },
19742
19743     /**
19744      * Updates the contents of the ghost element
19745      * @param {String} html The html that will replace the current innerHTML of the ghost element
19746      */
19747     update : function(html){
19748         if(typeof html == "string"){
19749             this.ghost.update(html);
19750         }else{
19751             this.ghost.update("");
19752             html.style.margin = "0";
19753             this.ghost.dom.appendChild(html);
19754         }
19755         // ensure float = none set?? cant remember why though.
19756         var el = this.ghost.dom.firstChild;
19757                 if(el){
19758                         Roo.fly(el).setStyle('float', 'none');
19759                 }
19760     },
19761     
19762     /**
19763      * Returns the underlying proxy {@link Roo.Layer}
19764      * @return {Roo.Layer} el
19765     */
19766     getEl : function(){
19767         return this.el;
19768     },
19769
19770     /**
19771      * Returns the ghost element
19772      * @return {Roo.Element} el
19773      */
19774     getGhost : function(){
19775         return this.ghost;
19776     },
19777
19778     /**
19779      * Hides the proxy
19780      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19781      */
19782     hide : function(clear){
19783         this.el.hide();
19784         if(clear){
19785             this.reset(true);
19786         }
19787     },
19788
19789     /**
19790      * Stops the repair animation if it's currently running
19791      */
19792     stop : function(){
19793         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19794             this.anim.stop();
19795         }
19796     },
19797
19798     /**
19799      * Displays this proxy
19800      */
19801     show : function(){
19802         this.el.show();
19803     },
19804
19805     /**
19806      * Force the Layer to sync its shadow and shim positions to the element
19807      */
19808     sync : function(){
19809         this.el.sync();
19810     },
19811
19812     /**
19813      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19814      * invalid drop operation by the item being dragged.
19815      * @param {Array} xy The XY position of the element ([x, y])
19816      * @param {Function} callback The function to call after the repair is complete
19817      * @param {Object} scope The scope in which to execute the callback
19818      */
19819     repair : function(xy, callback, scope){
19820         this.callback = callback;
19821         this.scope = scope;
19822         if(xy && this.animRepair !== false){
19823             this.el.addClass("x-dd-drag-repair");
19824             this.el.hideUnders(true);
19825             this.anim = this.el.shift({
19826                 duration: this.repairDuration || .5,
19827                 easing: 'easeOut',
19828                 xy: xy,
19829                 stopFx: true,
19830                 callback: this.afterRepair,
19831                 scope: this
19832             });
19833         }else{
19834             this.afterRepair();
19835         }
19836     },
19837
19838     // private
19839     afterRepair : function(){
19840         this.hide(true);
19841         if(typeof this.callback == "function"){
19842             this.callback.call(this.scope || this);
19843         }
19844         this.callback = null;
19845         this.scope = null;
19846     }
19847 };/*
19848  * Based on:
19849  * Ext JS Library 1.1.1
19850  * Copyright(c) 2006-2007, Ext JS, LLC.
19851  *
19852  * Originally Released Under LGPL - original licence link has changed is not relivant.
19853  *
19854  * Fork - LGPL
19855  * <script type="text/javascript">
19856  */
19857
19858 /**
19859  * @class Roo.dd.DragSource
19860  * @extends Roo.dd.DDProxy
19861  * A simple class that provides the basic implementation needed to make any element draggable.
19862  * @constructor
19863  * @param {String/HTMLElement/Element} el The container element
19864  * @param {Object} config
19865  */
19866 Roo.dd.DragSource = function(el, config){
19867     this.el = Roo.get(el);
19868     this.dragData = {};
19869     
19870     Roo.apply(this, config);
19871     
19872     if(!this.proxy){
19873         this.proxy = new Roo.dd.StatusProxy();
19874     }
19875
19876     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19877           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19878     
19879     this.dragging = false;
19880 };
19881
19882 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19883     /**
19884      * @cfg {String} dropAllowed
19885      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19886      */
19887     dropAllowed : "x-dd-drop-ok",
19888     /**
19889      * @cfg {String} dropNotAllowed
19890      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19891      */
19892     dropNotAllowed : "x-dd-drop-nodrop",
19893
19894     /**
19895      * Returns the data object associated with this drag source
19896      * @return {Object} data An object containing arbitrary data
19897      */
19898     getDragData : function(e){
19899         return this.dragData;
19900     },
19901
19902     // private
19903     onDragEnter : function(e, id){
19904         var target = Roo.dd.DragDropMgr.getDDById(id);
19905         this.cachedTarget = target;
19906         if(this.beforeDragEnter(target, e, id) !== false){
19907             if(target.isNotifyTarget){
19908                 var status = target.notifyEnter(this, e, this.dragData);
19909                 this.proxy.setStatus(status);
19910             }else{
19911                 this.proxy.setStatus(this.dropAllowed);
19912             }
19913             
19914             if(this.afterDragEnter){
19915                 /**
19916                  * An empty function by default, but provided so that you can perform a custom action
19917                  * when the dragged item enters the drop target by providing an implementation.
19918                  * @param {Roo.dd.DragDrop} target The drop target
19919                  * @param {Event} e The event object
19920                  * @param {String} id The id of the dragged element
19921                  * @method afterDragEnter
19922                  */
19923                 this.afterDragEnter(target, e, id);
19924             }
19925         }
19926     },
19927
19928     /**
19929      * An empty function by default, but provided so that you can perform a custom action
19930      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19931      * @param {Roo.dd.DragDrop} target The drop target
19932      * @param {Event} e The event object
19933      * @param {String} id The id of the dragged element
19934      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19935      */
19936     beforeDragEnter : function(target, e, id){
19937         return true;
19938     },
19939
19940     // private
19941     alignElWithMouse: function() {
19942         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19943         this.proxy.sync();
19944     },
19945
19946     // private
19947     onDragOver : function(e, id){
19948         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19949         if(this.beforeDragOver(target, e, id) !== false){
19950             if(target.isNotifyTarget){
19951                 var status = target.notifyOver(this, e, this.dragData);
19952                 this.proxy.setStatus(status);
19953             }
19954
19955             if(this.afterDragOver){
19956                 /**
19957                  * An empty function by default, but provided so that you can perform a custom action
19958                  * while the dragged item is over the drop target by providing an implementation.
19959                  * @param {Roo.dd.DragDrop} target The drop target
19960                  * @param {Event} e The event object
19961                  * @param {String} id The id of the dragged element
19962                  * @method afterDragOver
19963                  */
19964                 this.afterDragOver(target, e, id);
19965             }
19966         }
19967     },
19968
19969     /**
19970      * An empty function by default, but provided so that you can perform a custom action
19971      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19972      * @param {Roo.dd.DragDrop} target The drop target
19973      * @param {Event} e The event object
19974      * @param {String} id The id of the dragged element
19975      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19976      */
19977     beforeDragOver : function(target, e, id){
19978         return true;
19979     },
19980
19981     // private
19982     onDragOut : function(e, id){
19983         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19984         if(this.beforeDragOut(target, e, id) !== false){
19985             if(target.isNotifyTarget){
19986                 target.notifyOut(this, e, this.dragData);
19987             }
19988             this.proxy.reset();
19989             if(this.afterDragOut){
19990                 /**
19991                  * An empty function by default, but provided so that you can perform a custom action
19992                  * after the dragged item is dragged out of the target without dropping.
19993                  * @param {Roo.dd.DragDrop} target The drop target
19994                  * @param {Event} e The event object
19995                  * @param {String} id The id of the dragged element
19996                  * @method afterDragOut
19997                  */
19998                 this.afterDragOut(target, e, id);
19999             }
20000         }
20001         this.cachedTarget = null;
20002     },
20003
20004     /**
20005      * An empty function by default, but provided so that you can perform a custom action before the dragged
20006      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20007      * @param {Roo.dd.DragDrop} target The drop target
20008      * @param {Event} e The event object
20009      * @param {String} id The id of the dragged element
20010      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20011      */
20012     beforeDragOut : function(target, e, id){
20013         return true;
20014     },
20015     
20016     // private
20017     onDragDrop : function(e, id){
20018         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20019         if(this.beforeDragDrop(target, e, id) !== false){
20020             if(target.isNotifyTarget){
20021                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20022                     this.onValidDrop(target, e, id);
20023                 }else{
20024                     this.onInvalidDrop(target, e, id);
20025                 }
20026             }else{
20027                 this.onValidDrop(target, e, id);
20028             }
20029             
20030             if(this.afterDragDrop){
20031                 /**
20032                  * An empty function by default, but provided so that you can perform a custom action
20033                  * after a valid drag drop has occurred by providing an implementation.
20034                  * @param {Roo.dd.DragDrop} target The drop target
20035                  * @param {Event} e The event object
20036                  * @param {String} id The id of the dropped element
20037                  * @method afterDragDrop
20038                  */
20039                 this.afterDragDrop(target, e, id);
20040             }
20041         }
20042         delete this.cachedTarget;
20043     },
20044
20045     /**
20046      * An empty function by default, but provided so that you can perform a custom action before the dragged
20047      * item is dropped onto the target and optionally cancel the onDragDrop.
20048      * @param {Roo.dd.DragDrop} target The drop target
20049      * @param {Event} e The event object
20050      * @param {String} id The id of the dragged element
20051      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20052      */
20053     beforeDragDrop : function(target, e, id){
20054         return true;
20055     },
20056
20057     // private
20058     onValidDrop : function(target, e, id){
20059         this.hideProxy();
20060         if(this.afterValidDrop){
20061             /**
20062              * An empty function by default, but provided so that you can perform a custom action
20063              * after a valid drop has occurred by providing an implementation.
20064              * @param {Object} target The target DD 
20065              * @param {Event} e The event object
20066              * @param {String} id The id of the dropped element
20067              * @method afterInvalidDrop
20068              */
20069             this.afterValidDrop(target, e, id);
20070         }
20071     },
20072
20073     // private
20074     getRepairXY : function(e, data){
20075         return this.el.getXY();  
20076     },
20077
20078     // private
20079     onInvalidDrop : function(target, e, id){
20080         this.beforeInvalidDrop(target, e, id);
20081         if(this.cachedTarget){
20082             if(this.cachedTarget.isNotifyTarget){
20083                 this.cachedTarget.notifyOut(this, e, this.dragData);
20084             }
20085             this.cacheTarget = null;
20086         }
20087         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20088
20089         if(this.afterInvalidDrop){
20090             /**
20091              * An empty function by default, but provided so that you can perform a custom action
20092              * after an invalid drop has occurred by providing an implementation.
20093              * @param {Event} e The event object
20094              * @param {String} id The id of the dropped element
20095              * @method afterInvalidDrop
20096              */
20097             this.afterInvalidDrop(e, id);
20098         }
20099     },
20100
20101     // private
20102     afterRepair : function(){
20103         if(Roo.enableFx){
20104             this.el.highlight(this.hlColor || "c3daf9");
20105         }
20106         this.dragging = false;
20107     },
20108
20109     /**
20110      * An empty function by default, but provided so that you can perform a custom action after an invalid
20111      * drop has occurred.
20112      * @param {Roo.dd.DragDrop} target The drop target
20113      * @param {Event} e The event object
20114      * @param {String} id The id of the dragged element
20115      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20116      */
20117     beforeInvalidDrop : function(target, e, id){
20118         return true;
20119     },
20120
20121     // private
20122     handleMouseDown : function(e){
20123         if(this.dragging) {
20124             return;
20125         }
20126         var data = this.getDragData(e);
20127         if(data && this.onBeforeDrag(data, e) !== false){
20128             this.dragData = data;
20129             this.proxy.stop();
20130             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20131         } 
20132     },
20133
20134     /**
20135      * An empty function by default, but provided so that you can perform a custom action before the initial
20136      * drag event begins and optionally cancel it.
20137      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20138      * @param {Event} e The event object
20139      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20140      */
20141     onBeforeDrag : function(data, e){
20142         return true;
20143     },
20144
20145     /**
20146      * An empty function by default, but provided so that you can perform a custom action once the initial
20147      * drag event has begun.  The drag cannot be canceled from this function.
20148      * @param {Number} x The x position of the click on the dragged object
20149      * @param {Number} y The y position of the click on the dragged object
20150      */
20151     onStartDrag : Roo.emptyFn,
20152
20153     // private - YUI override
20154     startDrag : function(x, y){
20155         this.proxy.reset();
20156         this.dragging = true;
20157         this.proxy.update("");
20158         this.onInitDrag(x, y);
20159         this.proxy.show();
20160     },
20161
20162     // private
20163     onInitDrag : function(x, y){
20164         var clone = this.el.dom.cloneNode(true);
20165         clone.id = Roo.id(); // prevent duplicate ids
20166         this.proxy.update(clone);
20167         this.onStartDrag(x, y);
20168         return true;
20169     },
20170
20171     /**
20172      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20173      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20174      */
20175     getProxy : function(){
20176         return this.proxy;  
20177     },
20178
20179     /**
20180      * Hides the drag source's {@link Roo.dd.StatusProxy}
20181      */
20182     hideProxy : function(){
20183         this.proxy.hide();  
20184         this.proxy.reset(true);
20185         this.dragging = false;
20186     },
20187
20188     // private
20189     triggerCacheRefresh : function(){
20190         Roo.dd.DDM.refreshCache(this.groups);
20191     },
20192
20193     // private - override to prevent hiding
20194     b4EndDrag: function(e) {
20195     },
20196
20197     // private - override to prevent moving
20198     endDrag : function(e){
20199         this.onEndDrag(this.dragData, e);
20200     },
20201
20202     // private
20203     onEndDrag : function(data, e){
20204     },
20205     
20206     // private - pin to cursor
20207     autoOffset : function(x, y) {
20208         this.setDelta(-12, -20);
20209     }    
20210 });/*
20211  * Based on:
20212  * Ext JS Library 1.1.1
20213  * Copyright(c) 2006-2007, Ext JS, LLC.
20214  *
20215  * Originally Released Under LGPL - original licence link has changed is not relivant.
20216  *
20217  * Fork - LGPL
20218  * <script type="text/javascript">
20219  */
20220
20221
20222 /**
20223  * @class Roo.dd.DropTarget
20224  * @extends Roo.dd.DDTarget
20225  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20226  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20227  * @constructor
20228  * @param {String/HTMLElement/Element} el The container element
20229  * @param {Object} config
20230  */
20231 Roo.dd.DropTarget = function(el, config){
20232     this.el = Roo.get(el);
20233     
20234     var listeners = false; ;
20235     if (config && config.listeners) {
20236         listeners= config.listeners;
20237         delete config.listeners;
20238     }
20239     Roo.apply(this, config);
20240     
20241     if(this.containerScroll){
20242         Roo.dd.ScrollManager.register(this.el);
20243     }
20244     this.addEvents( {
20245          /**
20246          * @scope Roo.dd.DropTarget
20247          */
20248          
20249          /**
20250          * @event enter
20251          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20252          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20253          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20254          * 
20255          * IMPORTANT : it should set this.overClass and this.dropAllowed
20256          * 
20257          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20258          * @param {Event} e The event
20259          * @param {Object} data An object containing arbitrary data supplied by the drag source
20260          */
20261         "enter" : true,
20262         
20263          /**
20264          * @event over
20265          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20266          * This method will be called on every mouse movement while the drag source is over the drop target.
20267          * This default implementation simply returns the dropAllowed config value.
20268          * 
20269          * IMPORTANT : it should set this.dropAllowed
20270          * 
20271          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20272          * @param {Event} e The event
20273          * @param {Object} data An object containing arbitrary data supplied by the drag source
20274          
20275          */
20276         "over" : true,
20277         /**
20278          * @event out
20279          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20280          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20281          * overClass (if any) from the drop element.
20282          * 
20283          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20284          * @param {Event} e The event
20285          * @param {Object} data An object containing arbitrary data supplied by the drag source
20286          */
20287          "out" : true,
20288          
20289         /**
20290          * @event drop
20291          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20292          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20293          * implementation that does something to process the drop event and returns true so that the drag source's
20294          * repair action does not run.
20295          * 
20296          * IMPORTANT : it should set this.success
20297          * 
20298          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20299          * @param {Event} e The event
20300          * @param {Object} data An object containing arbitrary data supplied by the drag source
20301         */
20302          "drop" : true
20303     });
20304             
20305      
20306     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20307         this.el.dom, 
20308         this.ddGroup || this.group,
20309         {
20310             isTarget: true,
20311             listeners : listeners || {} 
20312            
20313         
20314         }
20315     );
20316
20317 };
20318
20319 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20320     /**
20321      * @cfg {String} overClass
20322      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20323      */
20324      /**
20325      * @cfg {String} ddGroup
20326      * The drag drop group to handle drop events for
20327      */
20328      
20329     /**
20330      * @cfg {String} dropAllowed
20331      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20332      */
20333     dropAllowed : "x-dd-drop-ok",
20334     /**
20335      * @cfg {String} dropNotAllowed
20336      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20337      */
20338     dropNotAllowed : "x-dd-drop-nodrop",
20339     /**
20340      * @cfg {boolean} success
20341      * set this after drop listener.. 
20342      */
20343     success : false,
20344     /**
20345      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20346      * if the drop point is valid for over/enter..
20347      */
20348     valid : false,
20349     // private
20350     isTarget : true,
20351
20352     // private
20353     isNotifyTarget : true,
20354     
20355     /**
20356      * @hide
20357      */
20358     notifyEnter : function(dd, e, data)
20359     {
20360         this.valid = true;
20361         this.fireEvent('enter', dd, e, data);
20362         if(this.overClass){
20363             this.el.addClass(this.overClass);
20364         }
20365         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20366             this.valid ? this.dropAllowed : this.dropNotAllowed
20367         );
20368     },
20369
20370     /**
20371      * @hide
20372      */
20373     notifyOver : function(dd, e, data)
20374     {
20375         this.valid = true;
20376         this.fireEvent('over', dd, e, data);
20377         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20378             this.valid ? this.dropAllowed : this.dropNotAllowed
20379         );
20380     },
20381
20382     /**
20383      * @hide
20384      */
20385     notifyOut : function(dd, e, data)
20386     {
20387         this.fireEvent('out', dd, e, data);
20388         if(this.overClass){
20389             this.el.removeClass(this.overClass);
20390         }
20391     },
20392
20393     /**
20394      * @hide
20395      */
20396     notifyDrop : function(dd, e, data)
20397     {
20398         this.success = false;
20399         this.fireEvent('drop', dd, e, data);
20400         return this.success;
20401     }
20402 });/*
20403  * Based on:
20404  * Ext JS Library 1.1.1
20405  * Copyright(c) 2006-2007, Ext JS, LLC.
20406  *
20407  * Originally Released Under LGPL - original licence link has changed is not relivant.
20408  *
20409  * Fork - LGPL
20410  * <script type="text/javascript">
20411  */
20412
20413
20414 /**
20415  * @class Roo.dd.DragZone
20416  * @extends Roo.dd.DragSource
20417  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20418  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20419  * @constructor
20420  * @param {String/HTMLElement/Element} el The container element
20421  * @param {Object} config
20422  */
20423 Roo.dd.DragZone = function(el, config){
20424     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20425     if(this.containerScroll){
20426         Roo.dd.ScrollManager.register(this.el);
20427     }
20428 };
20429
20430 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20431     /**
20432      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20433      * for auto scrolling during drag operations.
20434      */
20435     /**
20436      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20437      * method after a failed drop (defaults to "c3daf9" - light blue)
20438      */
20439
20440     /**
20441      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20442      * for a valid target to drag based on the mouse down. Override this method
20443      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20444      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20445      * @param {EventObject} e The mouse down event
20446      * @return {Object} The dragData
20447      */
20448     getDragData : function(e){
20449         return Roo.dd.Registry.getHandleFromEvent(e);
20450     },
20451     
20452     /**
20453      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20454      * this.dragData.ddel
20455      * @param {Number} x The x position of the click on the dragged object
20456      * @param {Number} y The y position of the click on the dragged object
20457      * @return {Boolean} true to continue the drag, false to cancel
20458      */
20459     onInitDrag : function(x, y){
20460         this.proxy.update(this.dragData.ddel.cloneNode(true));
20461         this.onStartDrag(x, y);
20462         return true;
20463     },
20464     
20465     /**
20466      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20467      */
20468     afterRepair : function(){
20469         if(Roo.enableFx){
20470             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20471         }
20472         this.dragging = false;
20473     },
20474
20475     /**
20476      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20477      * the XY of this.dragData.ddel
20478      * @param {EventObject} e The mouse up event
20479      * @return {Array} The xy location (e.g. [100, 200])
20480      */
20481     getRepairXY : function(e){
20482         return Roo.Element.fly(this.dragData.ddel).getXY();  
20483     }
20484 });/*
20485  * Based on:
20486  * Ext JS Library 1.1.1
20487  * Copyright(c) 2006-2007, Ext JS, LLC.
20488  *
20489  * Originally Released Under LGPL - original licence link has changed is not relivant.
20490  *
20491  * Fork - LGPL
20492  * <script type="text/javascript">
20493  */
20494 /**
20495  * @class Roo.dd.DropZone
20496  * @extends Roo.dd.DropTarget
20497  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20498  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20499  * @constructor
20500  * @param {String/HTMLElement/Element} el The container element
20501  * @param {Object} config
20502  */
20503 Roo.dd.DropZone = function(el, config){
20504     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20505 };
20506
20507 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20508     /**
20509      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20510      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20511      * provide your own custom lookup.
20512      * @param {Event} e The event
20513      * @return {Object} data The custom data
20514      */
20515     getTargetFromEvent : function(e){
20516         return Roo.dd.Registry.getTargetFromEvent(e);
20517     },
20518
20519     /**
20520      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20521      * that it has registered.  This method has no default implementation and should be overridden to provide
20522      * node-specific processing if necessary.
20523      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20524      * {@link #getTargetFromEvent} for this node)
20525      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20526      * @param {Event} e The event
20527      * @param {Object} data An object containing arbitrary data supplied by the drag source
20528      */
20529     onNodeEnter : function(n, dd, e, data){
20530         
20531     },
20532
20533     /**
20534      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20535      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20536      * overridden to provide the proper feedback.
20537      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20538      * {@link #getTargetFromEvent} for this node)
20539      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20540      * @param {Event} e The event
20541      * @param {Object} data An object containing arbitrary data supplied by the drag source
20542      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20543      * underlying {@link Roo.dd.StatusProxy} can be updated
20544      */
20545     onNodeOver : function(n, dd, e, data){
20546         return this.dropAllowed;
20547     },
20548
20549     /**
20550      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20551      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20552      * node-specific processing if necessary.
20553      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20554      * {@link #getTargetFromEvent} for this node)
20555      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20556      * @param {Event} e The event
20557      * @param {Object} data An object containing arbitrary data supplied by the drag source
20558      */
20559     onNodeOut : function(n, dd, e, data){
20560         
20561     },
20562
20563     /**
20564      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20565      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20566      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20567      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20568      * {@link #getTargetFromEvent} for this node)
20569      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20570      * @param {Event} e The event
20571      * @param {Object} data An object containing arbitrary data supplied by the drag source
20572      * @return {Boolean} True if the drop was valid, else false
20573      */
20574     onNodeDrop : function(n, dd, e, data){
20575         return false;
20576     },
20577
20578     /**
20579      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20580      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20581      * it should be overridden to provide the proper feedback if necessary.
20582      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20583      * @param {Event} e The event
20584      * @param {Object} data An object containing arbitrary data supplied by the drag source
20585      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20586      * underlying {@link Roo.dd.StatusProxy} can be updated
20587      */
20588     onContainerOver : function(dd, e, data){
20589         return this.dropNotAllowed;
20590     },
20591
20592     /**
20593      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20594      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20595      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20596      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20597      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20598      * @param {Event} e The event
20599      * @param {Object} data An object containing arbitrary data supplied by the drag source
20600      * @return {Boolean} True if the drop was valid, else false
20601      */
20602     onContainerDrop : function(dd, e, data){
20603         return false;
20604     },
20605
20606     /**
20607      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20608      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20609      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20610      * you should override this method and provide a custom implementation.
20611      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20612      * @param {Event} e The event
20613      * @param {Object} data An object containing arbitrary data supplied by the drag source
20614      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20615      * underlying {@link Roo.dd.StatusProxy} can be updated
20616      */
20617     notifyEnter : function(dd, e, data){
20618         return this.dropNotAllowed;
20619     },
20620
20621     /**
20622      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20623      * This method will be called on every mouse movement while the drag source is over the drop zone.
20624      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20625      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20626      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20627      * registered node, it will call {@link #onContainerOver}.
20628      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20629      * @param {Event} e The event
20630      * @param {Object} data An object containing arbitrary data supplied by the drag source
20631      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20632      * underlying {@link Roo.dd.StatusProxy} can be updated
20633      */
20634     notifyOver : function(dd, e, data){
20635         var n = this.getTargetFromEvent(e);
20636         if(!n){ // not over valid drop target
20637             if(this.lastOverNode){
20638                 this.onNodeOut(this.lastOverNode, dd, e, data);
20639                 this.lastOverNode = null;
20640             }
20641             return this.onContainerOver(dd, e, data);
20642         }
20643         if(this.lastOverNode != n){
20644             if(this.lastOverNode){
20645                 this.onNodeOut(this.lastOverNode, dd, e, data);
20646             }
20647             this.onNodeEnter(n, dd, e, data);
20648             this.lastOverNode = n;
20649         }
20650         return this.onNodeOver(n, dd, e, data);
20651     },
20652
20653     /**
20654      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20655      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20656      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20657      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20658      * @param {Event} e The event
20659      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20660      */
20661     notifyOut : function(dd, e, data){
20662         if(this.lastOverNode){
20663             this.onNodeOut(this.lastOverNode, dd, e, data);
20664             this.lastOverNode = null;
20665         }
20666     },
20667
20668     /**
20669      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20670      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20671      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20672      * otherwise it will call {@link #onContainerDrop}.
20673      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20674      * @param {Event} e The event
20675      * @param {Object} data An object containing arbitrary data supplied by the drag source
20676      * @return {Boolean} True if the drop was valid, else false
20677      */
20678     notifyDrop : function(dd, e, data){
20679         if(this.lastOverNode){
20680             this.onNodeOut(this.lastOverNode, dd, e, data);
20681             this.lastOverNode = null;
20682         }
20683         var n = this.getTargetFromEvent(e);
20684         return n ?
20685             this.onNodeDrop(n, dd, e, data) :
20686             this.onContainerDrop(dd, e, data);
20687     },
20688
20689     // private
20690     triggerCacheRefresh : function(){
20691         Roo.dd.DDM.refreshCache(this.groups);
20692     }  
20693 });/*
20694  * Based on:
20695  * Ext JS Library 1.1.1
20696  * Copyright(c) 2006-2007, Ext JS, LLC.
20697  *
20698  * Originally Released Under LGPL - original licence link has changed is not relivant.
20699  *
20700  * Fork - LGPL
20701  * <script type="text/javascript">
20702  */
20703
20704
20705 /**
20706  * @class Roo.data.SortTypes
20707  * @singleton
20708  * Defines the default sorting (casting?) comparison functions used when sorting data.
20709  */
20710 Roo.data.SortTypes = {
20711     /**
20712      * Default sort that does nothing
20713      * @param {Mixed} s The value being converted
20714      * @return {Mixed} The comparison value
20715      */
20716     none : function(s){
20717         return s;
20718     },
20719     
20720     /**
20721      * The regular expression used to strip tags
20722      * @type {RegExp}
20723      * @property
20724      */
20725     stripTagsRE : /<\/?[^>]+>/gi,
20726     
20727     /**
20728      * Strips all HTML tags to sort on text only
20729      * @param {Mixed} s The value being converted
20730      * @return {String} The comparison value
20731      */
20732     asText : function(s){
20733         return String(s).replace(this.stripTagsRE, "");
20734     },
20735     
20736     /**
20737      * Strips all HTML tags to sort on text only - Case insensitive
20738      * @param {Mixed} s The value being converted
20739      * @return {String} The comparison value
20740      */
20741     asUCText : function(s){
20742         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20743     },
20744     
20745     /**
20746      * Case insensitive string
20747      * @param {Mixed} s The value being converted
20748      * @return {String} The comparison value
20749      */
20750     asUCString : function(s) {
20751         return String(s).toUpperCase();
20752     },
20753     
20754     /**
20755      * Date sorting
20756      * @param {Mixed} s The value being converted
20757      * @return {Number} The comparison value
20758      */
20759     asDate : function(s) {
20760         if(!s){
20761             return 0;
20762         }
20763         if(s instanceof Date){
20764             return s.getTime();
20765         }
20766         return Date.parse(String(s));
20767     },
20768     
20769     /**
20770      * Float sorting
20771      * @param {Mixed} s The value being converted
20772      * @return {Float} The comparison value
20773      */
20774     asFloat : function(s) {
20775         var val = parseFloat(String(s).replace(/,/g, ""));
20776         if(isNaN(val)) val = 0;
20777         return val;
20778     },
20779     
20780     /**
20781      * Integer sorting
20782      * @param {Mixed} s The value being converted
20783      * @return {Number} The comparison value
20784      */
20785     asInt : function(s) {
20786         var val = parseInt(String(s).replace(/,/g, ""));
20787         if(isNaN(val)) val = 0;
20788         return val;
20789     }
20790 };/*
20791  * Based on:
20792  * Ext JS Library 1.1.1
20793  * Copyright(c) 2006-2007, Ext JS, LLC.
20794  *
20795  * Originally Released Under LGPL - original licence link has changed is not relivant.
20796  *
20797  * Fork - LGPL
20798  * <script type="text/javascript">
20799  */
20800
20801 /**
20802 * @class Roo.data.Record
20803  * Instances of this class encapsulate both record <em>definition</em> information, and record
20804  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20805  * to access Records cached in an {@link Roo.data.Store} object.<br>
20806  * <p>
20807  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20808  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20809  * objects.<br>
20810  * <p>
20811  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20812  * @constructor
20813  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20814  * {@link #create}. The parameters are the same.
20815  * @param {Array} data An associative Array of data values keyed by the field name.
20816  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20817  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20818  * not specified an integer id is generated.
20819  */
20820 Roo.data.Record = function(data, id){
20821     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20822     this.data = data;
20823 };
20824
20825 /**
20826  * Generate a constructor for a specific record layout.
20827  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20828  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20829  * Each field definition object may contain the following properties: <ul>
20830  * <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,
20831  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20832  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20833  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20834  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20835  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20836  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20837  * this may be omitted.</p></li>
20838  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20839  * <ul><li>auto (Default, implies no conversion)</li>
20840  * <li>string</li>
20841  * <li>int</li>
20842  * <li>float</li>
20843  * <li>boolean</li>
20844  * <li>date</li></ul></p></li>
20845  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20846  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20847  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20848  * by the Reader into an object that will be stored in the Record. It is passed the
20849  * following parameters:<ul>
20850  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20851  * </ul></p></li>
20852  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20853  * </ul>
20854  * <br>usage:<br><pre><code>
20855 var TopicRecord = Roo.data.Record.create(
20856     {name: 'title', mapping: 'topic_title'},
20857     {name: 'author', mapping: 'username'},
20858     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20859     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20860     {name: 'lastPoster', mapping: 'user2'},
20861     {name: 'excerpt', mapping: 'post_text'}
20862 );
20863
20864 var myNewRecord = new TopicRecord({
20865     title: 'Do my job please',
20866     author: 'noobie',
20867     totalPosts: 1,
20868     lastPost: new Date(),
20869     lastPoster: 'Animal',
20870     excerpt: 'No way dude!'
20871 });
20872 myStore.add(myNewRecord);
20873 </code></pre>
20874  * @method create
20875  * @static
20876  */
20877 Roo.data.Record.create = function(o){
20878     var f = function(){
20879         f.superclass.constructor.apply(this, arguments);
20880     };
20881     Roo.extend(f, Roo.data.Record);
20882     var p = f.prototype;
20883     p.fields = new Roo.util.MixedCollection(false, function(field){
20884         return field.name;
20885     });
20886     for(var i = 0, len = o.length; i < len; i++){
20887         p.fields.add(new Roo.data.Field(o[i]));
20888     }
20889     f.getField = function(name){
20890         return p.fields.get(name);  
20891     };
20892     return f;
20893 };
20894
20895 Roo.data.Record.AUTO_ID = 1000;
20896 Roo.data.Record.EDIT = 'edit';
20897 Roo.data.Record.REJECT = 'reject';
20898 Roo.data.Record.COMMIT = 'commit';
20899
20900 Roo.data.Record.prototype = {
20901     /**
20902      * Readonly flag - true if this record has been modified.
20903      * @type Boolean
20904      */
20905     dirty : false,
20906     editing : false,
20907     error: null,
20908     modified: null,
20909
20910     // private
20911     join : function(store){
20912         this.store = store;
20913     },
20914
20915     /**
20916      * Set the named field to the specified value.
20917      * @param {String} name The name of the field to set.
20918      * @param {Object} value The value to set the field to.
20919      */
20920     set : function(name, value){
20921         if(this.data[name] == value){
20922             return;
20923         }
20924         this.dirty = true;
20925         if(!this.modified){
20926             this.modified = {};
20927         }
20928         if(typeof this.modified[name] == 'undefined'){
20929             this.modified[name] = this.data[name];
20930         }
20931         this.data[name] = value;
20932         if(!this.editing && this.store){
20933             this.store.afterEdit(this);
20934         }       
20935     },
20936
20937     /**
20938      * Get the value of the named field.
20939      * @param {String} name The name of the field to get the value of.
20940      * @return {Object} The value of the field.
20941      */
20942     get : function(name){
20943         return this.data[name]; 
20944     },
20945
20946     // private
20947     beginEdit : function(){
20948         this.editing = true;
20949         this.modified = {}; 
20950     },
20951
20952     // private
20953     cancelEdit : function(){
20954         this.editing = false;
20955         delete this.modified;
20956     },
20957
20958     // private
20959     endEdit : function(){
20960         this.editing = false;
20961         if(this.dirty && this.store){
20962             this.store.afterEdit(this);
20963         }
20964     },
20965
20966     /**
20967      * Usually called by the {@link Roo.data.Store} which owns the Record.
20968      * Rejects all changes made to the Record since either creation, or the last commit operation.
20969      * Modified fields are reverted to their original values.
20970      * <p>
20971      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20972      * of reject operations.
20973      */
20974     reject : function(){
20975         var m = this.modified;
20976         for(var n in m){
20977             if(typeof m[n] != "function"){
20978                 this.data[n] = m[n];
20979             }
20980         }
20981         this.dirty = false;
20982         delete this.modified;
20983         this.editing = false;
20984         if(this.store){
20985             this.store.afterReject(this);
20986         }
20987     },
20988
20989     /**
20990      * Usually called by the {@link Roo.data.Store} which owns the Record.
20991      * Commits all changes made to the Record since either creation, or the last commit operation.
20992      * <p>
20993      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20994      * of commit operations.
20995      */
20996     commit : function(){
20997         this.dirty = false;
20998         delete this.modified;
20999         this.editing = false;
21000         if(this.store){
21001             this.store.afterCommit(this);
21002         }
21003     },
21004
21005     // private
21006     hasError : function(){
21007         return this.error != null;
21008     },
21009
21010     // private
21011     clearError : function(){
21012         this.error = null;
21013     },
21014
21015     /**
21016      * Creates a copy of this record.
21017      * @param {String} id (optional) A new record id if you don't want to use this record's id
21018      * @return {Record}
21019      */
21020     copy : function(newId) {
21021         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21022     }
21023 };/*
21024  * Based on:
21025  * Ext JS Library 1.1.1
21026  * Copyright(c) 2006-2007, Ext JS, LLC.
21027  *
21028  * Originally Released Under LGPL - original licence link has changed is not relivant.
21029  *
21030  * Fork - LGPL
21031  * <script type="text/javascript">
21032  */
21033
21034
21035
21036 /**
21037  * @class Roo.data.Store
21038  * @extends Roo.util.Observable
21039  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21040  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21041  * <p>
21042  * 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
21043  * has no knowledge of the format of the data returned by the Proxy.<br>
21044  * <p>
21045  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21046  * instances from the data object. These records are cached and made available through accessor functions.
21047  * @constructor
21048  * Creates a new Store.
21049  * @param {Object} config A config object containing the objects needed for the Store to access data,
21050  * and read the data into Records.
21051  */
21052 Roo.data.Store = function(config){
21053     this.data = new Roo.util.MixedCollection(false);
21054     this.data.getKey = function(o){
21055         return o.id;
21056     };
21057     this.baseParams = {};
21058     // private
21059     this.paramNames = {
21060         "start" : "start",
21061         "limit" : "limit",
21062         "sort" : "sort",
21063         "dir" : "dir",
21064         "multisort" : "_multisort"
21065     };
21066
21067     if(config && config.data){
21068         this.inlineData = config.data;
21069         delete config.data;
21070     }
21071
21072     Roo.apply(this, config);
21073     
21074     if(this.reader){ // reader passed
21075         this.reader = Roo.factory(this.reader, Roo.data);
21076         this.reader.xmodule = this.xmodule || false;
21077         if(!this.recordType){
21078             this.recordType = this.reader.recordType;
21079         }
21080         if(this.reader.onMetaChange){
21081             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21082         }
21083     }
21084
21085     if(this.recordType){
21086         this.fields = this.recordType.prototype.fields;
21087     }
21088     this.modified = [];
21089
21090     this.addEvents({
21091         /**
21092          * @event datachanged
21093          * Fires when the data cache has changed, and a widget which is using this Store
21094          * as a Record cache should refresh its view.
21095          * @param {Store} this
21096          */
21097         datachanged : true,
21098         /**
21099          * @event metachange
21100          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21101          * @param {Store} this
21102          * @param {Object} meta The JSON metadata
21103          */
21104         metachange : true,
21105         /**
21106          * @event add
21107          * Fires when Records have been added to the Store
21108          * @param {Store} this
21109          * @param {Roo.data.Record[]} records The array of Records added
21110          * @param {Number} index The index at which the record(s) were added
21111          */
21112         add : true,
21113         /**
21114          * @event remove
21115          * Fires when a Record has been removed from the Store
21116          * @param {Store} this
21117          * @param {Roo.data.Record} record The Record that was removed
21118          * @param {Number} index The index at which the record was removed
21119          */
21120         remove : true,
21121         /**
21122          * @event update
21123          * Fires when a Record has been updated
21124          * @param {Store} this
21125          * @param {Roo.data.Record} record The Record that was updated
21126          * @param {String} operation The update operation being performed.  Value may be one of:
21127          * <pre><code>
21128  Roo.data.Record.EDIT
21129  Roo.data.Record.REJECT
21130  Roo.data.Record.COMMIT
21131          * </code></pre>
21132          */
21133         update : true,
21134         /**
21135          * @event clear
21136          * Fires when the data cache has been cleared.
21137          * @param {Store} this
21138          */
21139         clear : true,
21140         /**
21141          * @event beforeload
21142          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21143          * the load action will be canceled.
21144          * @param {Store} this
21145          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21146          */
21147         beforeload : true,
21148         /**
21149          * @event beforeloadadd
21150          * Fires after a new set of Records has been loaded.
21151          * @param {Store} this
21152          * @param {Roo.data.Record[]} records The Records that were loaded
21153          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21154          */
21155         beforeloadadd : true,
21156         /**
21157          * @event load
21158          * Fires after a new set of Records has been loaded, before they are added to the store.
21159          * @param {Store} this
21160          * @param {Roo.data.Record[]} records The Records that were loaded
21161          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21162          * @params {Object} return from reader
21163          */
21164         load : true,
21165         /**
21166          * @event loadexception
21167          * Fires if an exception occurs in the Proxy during loading.
21168          * Called with the signature of the Proxy's "loadexception" event.
21169          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21170          * 
21171          * @param {Proxy} 
21172          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21173          * @param {Object} load options 
21174          * @param {Object} jsonData from your request (normally this contains the Exception)
21175          */
21176         loadexception : true
21177     });
21178     
21179     if(this.proxy){
21180         this.proxy = Roo.factory(this.proxy, Roo.data);
21181         this.proxy.xmodule = this.xmodule || false;
21182         this.relayEvents(this.proxy,  ["loadexception"]);
21183     }
21184     this.sortToggle = {};
21185     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21186
21187     Roo.data.Store.superclass.constructor.call(this);
21188
21189     if(this.inlineData){
21190         this.loadData(this.inlineData);
21191         delete this.inlineData;
21192     }
21193 };
21194
21195 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21196      /**
21197     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21198     * without a remote query - used by combo/forms at present.
21199     */
21200     
21201     /**
21202     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21203     */
21204     /**
21205     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21206     */
21207     /**
21208     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21209     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21210     */
21211     /**
21212     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21213     * on any HTTP request
21214     */
21215     /**
21216     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21217     */
21218     /**
21219     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21220     */
21221     multiSort: false,
21222     /**
21223     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21224     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21225     */
21226     remoteSort : false,
21227
21228     /**
21229     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21230      * loaded or when a record is removed. (defaults to false).
21231     */
21232     pruneModifiedRecords : false,
21233
21234     // private
21235     lastOptions : null,
21236
21237     /**
21238      * Add Records to the Store and fires the add event.
21239      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21240      */
21241     add : function(records){
21242         records = [].concat(records);
21243         for(var i = 0, len = records.length; i < len; i++){
21244             records[i].join(this);
21245         }
21246         var index = this.data.length;
21247         this.data.addAll(records);
21248         this.fireEvent("add", this, records, index);
21249     },
21250
21251     /**
21252      * Remove a Record from the Store and fires the remove event.
21253      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21254      */
21255     remove : function(record){
21256         var index = this.data.indexOf(record);
21257         this.data.removeAt(index);
21258         if(this.pruneModifiedRecords){
21259             this.modified.remove(record);
21260         }
21261         this.fireEvent("remove", this, record, index);
21262     },
21263
21264     /**
21265      * Remove all Records from the Store and fires the clear event.
21266      */
21267     removeAll : function(){
21268         this.data.clear();
21269         if(this.pruneModifiedRecords){
21270             this.modified = [];
21271         }
21272         this.fireEvent("clear", this);
21273     },
21274
21275     /**
21276      * Inserts Records to the Store at the given index and fires the add event.
21277      * @param {Number} index The start index at which to insert the passed Records.
21278      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21279      */
21280     insert : function(index, records){
21281         records = [].concat(records);
21282         for(var i = 0, len = records.length; i < len; i++){
21283             this.data.insert(index, records[i]);
21284             records[i].join(this);
21285         }
21286         this.fireEvent("add", this, records, index);
21287     },
21288
21289     /**
21290      * Get the index within the cache of the passed Record.
21291      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21292      * @return {Number} The index of the passed Record. Returns -1 if not found.
21293      */
21294     indexOf : function(record){
21295         return this.data.indexOf(record);
21296     },
21297
21298     /**
21299      * Get the index within the cache of the Record with the passed id.
21300      * @param {String} id The id of the Record to find.
21301      * @return {Number} The index of the Record. Returns -1 if not found.
21302      */
21303     indexOfId : function(id){
21304         return this.data.indexOfKey(id);
21305     },
21306
21307     /**
21308      * Get the Record with the specified id.
21309      * @param {String} id The id of the Record to find.
21310      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21311      */
21312     getById : function(id){
21313         return this.data.key(id);
21314     },
21315
21316     /**
21317      * Get the Record at the specified index.
21318      * @param {Number} index The index of the Record to find.
21319      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21320      */
21321     getAt : function(index){
21322         return this.data.itemAt(index);
21323     },
21324
21325     /**
21326      * Returns a range of Records between specified indices.
21327      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21328      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21329      * @return {Roo.data.Record[]} An array of Records
21330      */
21331     getRange : function(start, end){
21332         return this.data.getRange(start, end);
21333     },
21334
21335     // private
21336     storeOptions : function(o){
21337         o = Roo.apply({}, o);
21338         delete o.callback;
21339         delete o.scope;
21340         this.lastOptions = o;
21341     },
21342
21343     /**
21344      * Loads the Record cache from the configured Proxy using the configured Reader.
21345      * <p>
21346      * If using remote paging, then the first load call must specify the <em>start</em>
21347      * and <em>limit</em> properties in the options.params property to establish the initial
21348      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21349      * <p>
21350      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21351      * and this call will return before the new data has been loaded. Perform any post-processing
21352      * in a callback function, or in a "load" event handler.</strong>
21353      * <p>
21354      * @param {Object} options An object containing properties which control loading options:<ul>
21355      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21356      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21357      * passed the following arguments:<ul>
21358      * <li>r : Roo.data.Record[]</li>
21359      * <li>options: Options object from the load call</li>
21360      * <li>success: Boolean success indicator</li></ul></li>
21361      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21362      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21363      * </ul>
21364      */
21365     load : function(options){
21366         options = options || {};
21367         if(this.fireEvent("beforeload", this, options) !== false){
21368             this.storeOptions(options);
21369             var p = Roo.apply(options.params || {}, this.baseParams);
21370             // if meta was not loaded from remote source.. try requesting it.
21371             if (!this.reader.metaFromRemote) {
21372                 p._requestMeta = 1;
21373             }
21374             if(this.sortInfo && this.remoteSort){
21375                 var pn = this.paramNames;
21376                 p[pn["sort"]] = this.sortInfo.field;
21377                 p[pn["dir"]] = this.sortInfo.direction;
21378             }
21379             if (this.multiSort) {
21380                 var pn = this.paramNames;
21381                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21382             }
21383             
21384             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21385         }
21386     },
21387
21388     /**
21389      * Reloads the Record cache from the configured Proxy using the configured Reader and
21390      * the options from the last load operation performed.
21391      * @param {Object} options (optional) An object containing properties which may override the options
21392      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21393      * the most recently used options are reused).
21394      */
21395     reload : function(options){
21396         this.load(Roo.applyIf(options||{}, this.lastOptions));
21397     },
21398
21399     // private
21400     // Called as a callback by the Reader during a load operation.
21401     loadRecords : function(o, options, success){
21402         if(!o || success === false){
21403             if(success !== false){
21404                 this.fireEvent("load", this, [], options, o);
21405             }
21406             if(options.callback){
21407                 options.callback.call(options.scope || this, [], options, false);
21408             }
21409             return;
21410         }
21411         // if data returned failure - throw an exception.
21412         if (o.success === false) {
21413             // show a message if no listener is registered.
21414             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21415                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21416             }
21417             // loadmask wil be hooked into this..
21418             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21419             return;
21420         }
21421         var r = o.records, t = o.totalRecords || r.length;
21422         
21423         this.fireEvent("beforeloadadd", this, r, options, o);
21424         
21425         if(!options || options.add !== true){
21426             if(this.pruneModifiedRecords){
21427                 this.modified = [];
21428             }
21429             for(var i = 0, len = r.length; i < len; i++){
21430                 r[i].join(this);
21431             }
21432             if(this.snapshot){
21433                 this.data = this.snapshot;
21434                 delete this.snapshot;
21435             }
21436             this.data.clear();
21437             this.data.addAll(r);
21438             this.totalLength = t;
21439             this.applySort();
21440             this.fireEvent("datachanged", this);
21441         }else{
21442             this.totalLength = Math.max(t, this.data.length+r.length);
21443             this.add(r);
21444         }
21445         this.fireEvent("load", this, r, options, o);
21446         if(options.callback){
21447             options.callback.call(options.scope || this, r, options, true);
21448         }
21449     },
21450
21451
21452     /**
21453      * Loads data from a passed data block. A Reader which understands the format of the data
21454      * must have been configured in the constructor.
21455      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21456      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21457      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21458      */
21459     loadData : function(o, append){
21460         var r = this.reader.readRecords(o);
21461         this.loadRecords(r, {add: append}, true);
21462     },
21463
21464     /**
21465      * Gets the number of cached records.
21466      * <p>
21467      * <em>If using paging, this may not be the total size of the dataset. If the data object
21468      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21469      * the data set size</em>
21470      */
21471     getCount : function(){
21472         return this.data.length || 0;
21473     },
21474
21475     /**
21476      * Gets the total number of records in the dataset as returned by the server.
21477      * <p>
21478      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21479      * the dataset size</em>
21480      */
21481     getTotalCount : function(){
21482         return this.totalLength || 0;
21483     },
21484
21485     /**
21486      * Returns the sort state of the Store as an object with two properties:
21487      * <pre><code>
21488  field {String} The name of the field by which the Records are sorted
21489  direction {String} The sort order, "ASC" or "DESC"
21490      * </code></pre>
21491      */
21492     getSortState : function(){
21493         return this.sortInfo;
21494     },
21495
21496     // private
21497     applySort : function(){
21498         if(this.sortInfo && !this.remoteSort){
21499             var s = this.sortInfo, f = s.field;
21500             var st = this.fields.get(f).sortType;
21501             var fn = function(r1, r2){
21502                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21503                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21504             };
21505             this.data.sort(s.direction, fn);
21506             if(this.snapshot && this.snapshot != this.data){
21507                 this.snapshot.sort(s.direction, fn);
21508             }
21509         }
21510     },
21511
21512     /**
21513      * Sets the default sort column and order to be used by the next load operation.
21514      * @param {String} fieldName The name of the field to sort by.
21515      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21516      */
21517     setDefaultSort : function(field, dir){
21518         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21519     },
21520
21521     /**
21522      * Sort the Records.
21523      * If remote sorting is used, the sort is performed on the server, and the cache is
21524      * reloaded. If local sorting is used, the cache is sorted internally.
21525      * @param {String} fieldName The name of the field to sort by.
21526      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21527      */
21528     sort : function(fieldName, dir){
21529         var f = this.fields.get(fieldName);
21530         if(!dir){
21531             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21532             
21533             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21534                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21535             }else{
21536                 dir = f.sortDir;
21537             }
21538         }
21539         this.sortToggle[f.name] = dir;
21540         this.sortInfo = {field: f.name, direction: dir};
21541         if(!this.remoteSort){
21542             this.applySort();
21543             this.fireEvent("datachanged", this);
21544         }else{
21545             this.load(this.lastOptions);
21546         }
21547     },
21548
21549     /**
21550      * Calls the specified function for each of the Records in the cache.
21551      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21552      * Returning <em>false</em> aborts and exits the iteration.
21553      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21554      */
21555     each : function(fn, scope){
21556         this.data.each(fn, scope);
21557     },
21558
21559     /**
21560      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21561      * (e.g., during paging).
21562      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21563      */
21564     getModifiedRecords : function(){
21565         return this.modified;
21566     },
21567
21568     // private
21569     createFilterFn : function(property, value, anyMatch){
21570         if(!value.exec){ // not a regex
21571             value = String(value);
21572             if(value.length == 0){
21573                 return false;
21574             }
21575             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21576         }
21577         return function(r){
21578             return value.test(r.data[property]);
21579         };
21580     },
21581
21582     /**
21583      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21584      * @param {String} property A field on your records
21585      * @param {Number} start The record index to start at (defaults to 0)
21586      * @param {Number} end The last record index to include (defaults to length - 1)
21587      * @return {Number} The sum
21588      */
21589     sum : function(property, start, end){
21590         var rs = this.data.items, v = 0;
21591         start = start || 0;
21592         end = (end || end === 0) ? end : rs.length-1;
21593
21594         for(var i = start; i <= end; i++){
21595             v += (rs[i].data[property] || 0);
21596         }
21597         return v;
21598     },
21599
21600     /**
21601      * Filter the records by a specified property.
21602      * @param {String} field A field on your records
21603      * @param {String/RegExp} value Either a string that the field
21604      * should start with or a RegExp to test against the field
21605      * @param {Boolean} anyMatch True to match any part not just the beginning
21606      */
21607     filter : function(property, value, anyMatch){
21608         var fn = this.createFilterFn(property, value, anyMatch);
21609         return fn ? this.filterBy(fn) : this.clearFilter();
21610     },
21611
21612     /**
21613      * Filter by a function. The specified function will be called with each
21614      * record in this data source. If the function returns true the record is included,
21615      * otherwise it is filtered.
21616      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21617      * @param {Object} scope (optional) The scope of the function (defaults to this)
21618      */
21619     filterBy : function(fn, scope){
21620         this.snapshot = this.snapshot || this.data;
21621         this.data = this.queryBy(fn, scope||this);
21622         this.fireEvent("datachanged", this);
21623     },
21624
21625     /**
21626      * Query the records by a specified property.
21627      * @param {String} field A field on your records
21628      * @param {String/RegExp} value Either a string that the field
21629      * should start with or a RegExp to test against the field
21630      * @param {Boolean} anyMatch True to match any part not just the beginning
21631      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21632      */
21633     query : function(property, value, anyMatch){
21634         var fn = this.createFilterFn(property, value, anyMatch);
21635         return fn ? this.queryBy(fn) : this.data.clone();
21636     },
21637
21638     /**
21639      * Query by a function. The specified function will be called with each
21640      * record in this data source. If the function returns true the record is included
21641      * in the results.
21642      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21643      * @param {Object} scope (optional) The scope of the function (defaults to this)
21644       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21645      **/
21646     queryBy : function(fn, scope){
21647         var data = this.snapshot || this.data;
21648         return data.filterBy(fn, scope||this);
21649     },
21650
21651     /**
21652      * Collects unique values for a particular dataIndex from this store.
21653      * @param {String} dataIndex The property to collect
21654      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21655      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21656      * @return {Array} An array of the unique values
21657      **/
21658     collect : function(dataIndex, allowNull, bypassFilter){
21659         var d = (bypassFilter === true && this.snapshot) ?
21660                 this.snapshot.items : this.data.items;
21661         var v, sv, r = [], l = {};
21662         for(var i = 0, len = d.length; i < len; i++){
21663             v = d[i].data[dataIndex];
21664             sv = String(v);
21665             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21666                 l[sv] = true;
21667                 r[r.length] = v;
21668             }
21669         }
21670         return r;
21671     },
21672
21673     /**
21674      * Revert to a view of the Record cache with no filtering applied.
21675      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21676      */
21677     clearFilter : function(suppressEvent){
21678         if(this.snapshot && this.snapshot != this.data){
21679             this.data = this.snapshot;
21680             delete this.snapshot;
21681             if(suppressEvent !== true){
21682                 this.fireEvent("datachanged", this);
21683             }
21684         }
21685     },
21686
21687     // private
21688     afterEdit : function(record){
21689         if(this.modified.indexOf(record) == -1){
21690             this.modified.push(record);
21691         }
21692         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21693     },
21694     
21695     // private
21696     afterReject : function(record){
21697         this.modified.remove(record);
21698         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21699     },
21700
21701     // private
21702     afterCommit : function(record){
21703         this.modified.remove(record);
21704         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21705     },
21706
21707     /**
21708      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21709      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21710      */
21711     commitChanges : function(){
21712         var m = this.modified.slice(0);
21713         this.modified = [];
21714         for(var i = 0, len = m.length; i < len; i++){
21715             m[i].commit();
21716         }
21717     },
21718
21719     /**
21720      * Cancel outstanding changes on all changed records.
21721      */
21722     rejectChanges : function(){
21723         var m = this.modified.slice(0);
21724         this.modified = [];
21725         for(var i = 0, len = m.length; i < len; i++){
21726             m[i].reject();
21727         }
21728     },
21729
21730     onMetaChange : function(meta, rtype, o){
21731         this.recordType = rtype;
21732         this.fields = rtype.prototype.fields;
21733         delete this.snapshot;
21734         this.sortInfo = meta.sortInfo || this.sortInfo;
21735         this.modified = [];
21736         this.fireEvent('metachange', this, this.reader.meta);
21737     }
21738 });/*
21739  * Based on:
21740  * Ext JS Library 1.1.1
21741  * Copyright(c) 2006-2007, Ext JS, LLC.
21742  *
21743  * Originally Released Under LGPL - original licence link has changed is not relivant.
21744  *
21745  * Fork - LGPL
21746  * <script type="text/javascript">
21747  */
21748
21749 /**
21750  * @class Roo.data.SimpleStore
21751  * @extends Roo.data.Store
21752  * Small helper class to make creating Stores from Array data easier.
21753  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21754  * @cfg {Array} fields An array of field definition objects, or field name strings.
21755  * @cfg {Array} data The multi-dimensional array of data
21756  * @constructor
21757  * @param {Object} config
21758  */
21759 Roo.data.SimpleStore = function(config){
21760     Roo.data.SimpleStore.superclass.constructor.call(this, {
21761         isLocal : true,
21762         reader: new Roo.data.ArrayReader({
21763                 id: config.id
21764             },
21765             Roo.data.Record.create(config.fields)
21766         ),
21767         proxy : new Roo.data.MemoryProxy(config.data)
21768     });
21769     this.load();
21770 };
21771 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21772  * Based on:
21773  * Ext JS Library 1.1.1
21774  * Copyright(c) 2006-2007, Ext JS, LLC.
21775  *
21776  * Originally Released Under LGPL - original licence link has changed is not relivant.
21777  *
21778  * Fork - LGPL
21779  * <script type="text/javascript">
21780  */
21781
21782 /**
21783 /**
21784  * @extends Roo.data.Store
21785  * @class Roo.data.JsonStore
21786  * Small helper class to make creating Stores for JSON data easier. <br/>
21787 <pre><code>
21788 var store = new Roo.data.JsonStore({
21789     url: 'get-images.php',
21790     root: 'images',
21791     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21792 });
21793 </code></pre>
21794  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21795  * JsonReader and HttpProxy (unless inline data is provided).</b>
21796  * @cfg {Array} fields An array of field definition objects, or field name strings.
21797  * @constructor
21798  * @param {Object} config
21799  */
21800 Roo.data.JsonStore = function(c){
21801     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21802         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21803         reader: new Roo.data.JsonReader(c, c.fields)
21804     }));
21805 };
21806 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21807  * Based on:
21808  * Ext JS Library 1.1.1
21809  * Copyright(c) 2006-2007, Ext JS, LLC.
21810  *
21811  * Originally Released Under LGPL - original licence link has changed is not relivant.
21812  *
21813  * Fork - LGPL
21814  * <script type="text/javascript">
21815  */
21816
21817  
21818 Roo.data.Field = function(config){
21819     if(typeof config == "string"){
21820         config = {name: config};
21821     }
21822     Roo.apply(this, config);
21823     
21824     if(!this.type){
21825         this.type = "auto";
21826     }
21827     
21828     var st = Roo.data.SortTypes;
21829     // named sortTypes are supported, here we look them up
21830     if(typeof this.sortType == "string"){
21831         this.sortType = st[this.sortType];
21832     }
21833     
21834     // set default sortType for strings and dates
21835     if(!this.sortType){
21836         switch(this.type){
21837             case "string":
21838                 this.sortType = st.asUCString;
21839                 break;
21840             case "date":
21841                 this.sortType = st.asDate;
21842                 break;
21843             default:
21844                 this.sortType = st.none;
21845         }
21846     }
21847
21848     // define once
21849     var stripRe = /[\$,%]/g;
21850
21851     // prebuilt conversion function for this field, instead of
21852     // switching every time we're reading a value
21853     if(!this.convert){
21854         var cv, dateFormat = this.dateFormat;
21855         switch(this.type){
21856             case "":
21857             case "auto":
21858             case undefined:
21859                 cv = function(v){ return v; };
21860                 break;
21861             case "string":
21862                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21863                 break;
21864             case "int":
21865                 cv = function(v){
21866                     return v !== undefined && v !== null && v !== '' ?
21867                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21868                     };
21869                 break;
21870             case "float":
21871                 cv = function(v){
21872                     return v !== undefined && v !== null && v !== '' ?
21873                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21874                     };
21875                 break;
21876             case "bool":
21877             case "boolean":
21878                 cv = function(v){ return v === true || v === "true" || v == 1; };
21879                 break;
21880             case "date":
21881                 cv = function(v){
21882                     if(!v){
21883                         return '';
21884                     }
21885                     if(v instanceof Date){
21886                         return v;
21887                     }
21888                     if(dateFormat){
21889                         if(dateFormat == "timestamp"){
21890                             return new Date(v*1000);
21891                         }
21892                         return Date.parseDate(v, dateFormat);
21893                     }
21894                     var parsed = Date.parse(v);
21895                     return parsed ? new Date(parsed) : null;
21896                 };
21897              break;
21898             
21899         }
21900         this.convert = cv;
21901     }
21902 };
21903
21904 Roo.data.Field.prototype = {
21905     dateFormat: null,
21906     defaultValue: "",
21907     mapping: null,
21908     sortType : null,
21909     sortDir : "ASC"
21910 };/*
21911  * Based on:
21912  * Ext JS Library 1.1.1
21913  * Copyright(c) 2006-2007, Ext JS, LLC.
21914  *
21915  * Originally Released Under LGPL - original licence link has changed is not relivant.
21916  *
21917  * Fork - LGPL
21918  * <script type="text/javascript">
21919  */
21920  
21921 // Base class for reading structured data from a data source.  This class is intended to be
21922 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21923
21924 /**
21925  * @class Roo.data.DataReader
21926  * Base class for reading structured data from a data source.  This class is intended to be
21927  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21928  */
21929
21930 Roo.data.DataReader = function(meta, recordType){
21931     
21932     this.meta = meta;
21933     
21934     this.recordType = recordType instanceof Array ? 
21935         Roo.data.Record.create(recordType) : recordType;
21936 };
21937
21938 Roo.data.DataReader.prototype = {
21939      /**
21940      * Create an empty record
21941      * @param {Object} data (optional) - overlay some values
21942      * @return {Roo.data.Record} record created.
21943      */
21944     newRow :  function(d) {
21945         var da =  {};
21946         this.recordType.prototype.fields.each(function(c) {
21947             switch( c.type) {
21948                 case 'int' : da[c.name] = 0; break;
21949                 case 'date' : da[c.name] = new Date(); break;
21950                 case 'float' : da[c.name] = 0.0; break;
21951                 case 'boolean' : da[c.name] = false; break;
21952                 default : da[c.name] = ""; break;
21953             }
21954             
21955         });
21956         return new this.recordType(Roo.apply(da, d));
21957     }
21958     
21959 };/*
21960  * Based on:
21961  * Ext JS Library 1.1.1
21962  * Copyright(c) 2006-2007, Ext JS, LLC.
21963  *
21964  * Originally Released Under LGPL - original licence link has changed is not relivant.
21965  *
21966  * Fork - LGPL
21967  * <script type="text/javascript">
21968  */
21969
21970 /**
21971  * @class Roo.data.DataProxy
21972  * @extends Roo.data.Observable
21973  * This class is an abstract base class for implementations which provide retrieval of
21974  * unformatted data objects.<br>
21975  * <p>
21976  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21977  * (of the appropriate type which knows how to parse the data object) to provide a block of
21978  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21979  * <p>
21980  * Custom implementations must implement the load method as described in
21981  * {@link Roo.data.HttpProxy#load}.
21982  */
21983 Roo.data.DataProxy = function(){
21984     this.addEvents({
21985         /**
21986          * @event beforeload
21987          * Fires before a network request is made to retrieve a data object.
21988          * @param {Object} This DataProxy object.
21989          * @param {Object} params The params parameter to the load function.
21990          */
21991         beforeload : true,
21992         /**
21993          * @event load
21994          * Fires before the load method's callback is called.
21995          * @param {Object} This DataProxy object.
21996          * @param {Object} o The data object.
21997          * @param {Object} arg The callback argument object passed to the load function.
21998          */
21999         load : true,
22000         /**
22001          * @event loadexception
22002          * Fires if an Exception occurs during data retrieval.
22003          * @param {Object} This DataProxy object.
22004          * @param {Object} o The data object.
22005          * @param {Object} arg The callback argument object passed to the load function.
22006          * @param {Object} e The Exception.
22007          */
22008         loadexception : true
22009     });
22010     Roo.data.DataProxy.superclass.constructor.call(this);
22011 };
22012
22013 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22014
22015     /**
22016      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22017      */
22018 /*
22019  * Based on:
22020  * Ext JS Library 1.1.1
22021  * Copyright(c) 2006-2007, Ext JS, LLC.
22022  *
22023  * Originally Released Under LGPL - original licence link has changed is not relivant.
22024  *
22025  * Fork - LGPL
22026  * <script type="text/javascript">
22027  */
22028 /**
22029  * @class Roo.data.MemoryProxy
22030  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22031  * to the Reader when its load method is called.
22032  * @constructor
22033  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22034  */
22035 Roo.data.MemoryProxy = function(data){
22036     if (data.data) {
22037         data = data.data;
22038     }
22039     Roo.data.MemoryProxy.superclass.constructor.call(this);
22040     this.data = data;
22041 };
22042
22043 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22044     /**
22045      * Load data from the requested source (in this case an in-memory
22046      * data object passed to the constructor), read the data object into
22047      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22048      * process that block using the passed callback.
22049      * @param {Object} params This parameter is not used by the MemoryProxy class.
22050      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22051      * object into a block of Roo.data.Records.
22052      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22053      * The function must be passed <ul>
22054      * <li>The Record block object</li>
22055      * <li>The "arg" argument from the load function</li>
22056      * <li>A boolean success indicator</li>
22057      * </ul>
22058      * @param {Object} scope The scope in which to call the callback
22059      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22060      */
22061     load : function(params, reader, callback, scope, arg){
22062         params = params || {};
22063         var result;
22064         try {
22065             result = reader.readRecords(this.data);
22066         }catch(e){
22067             this.fireEvent("loadexception", this, arg, null, e);
22068             callback.call(scope, null, arg, false);
22069             return;
22070         }
22071         callback.call(scope, result, arg, true);
22072     },
22073     
22074     // private
22075     update : function(params, records){
22076         
22077     }
22078 });/*
22079  * Based on:
22080  * Ext JS Library 1.1.1
22081  * Copyright(c) 2006-2007, Ext JS, LLC.
22082  *
22083  * Originally Released Under LGPL - original licence link has changed is not relivant.
22084  *
22085  * Fork - LGPL
22086  * <script type="text/javascript">
22087  */
22088 /**
22089  * @class Roo.data.HttpProxy
22090  * @extends Roo.data.DataProxy
22091  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22092  * configured to reference a certain URL.<br><br>
22093  * <p>
22094  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22095  * from which the running page was served.<br><br>
22096  * <p>
22097  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22098  * <p>
22099  * Be aware that to enable the browser to parse an XML document, the server must set
22100  * the Content-Type header in the HTTP response to "text/xml".
22101  * @constructor
22102  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22103  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22104  * will be used to make the request.
22105  */
22106 Roo.data.HttpProxy = function(conn){
22107     Roo.data.HttpProxy.superclass.constructor.call(this);
22108     // is conn a conn config or a real conn?
22109     this.conn = conn;
22110     this.useAjax = !conn || !conn.events;
22111   
22112 };
22113
22114 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22115     // thse are take from connection...
22116     
22117     /**
22118      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22119      */
22120     /**
22121      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22122      * extra parameters to each request made by this object. (defaults to undefined)
22123      */
22124     /**
22125      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22126      *  to each request made by this object. (defaults to undefined)
22127      */
22128     /**
22129      * @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)
22130      */
22131     /**
22132      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22133      */
22134      /**
22135      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22136      * @type Boolean
22137      */
22138   
22139
22140     /**
22141      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22142      * @type Boolean
22143      */
22144     /**
22145      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22146      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22147      * a finer-grained basis than the DataProxy events.
22148      */
22149     getConnection : function(){
22150         return this.useAjax ? Roo.Ajax : this.conn;
22151     },
22152
22153     /**
22154      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22155      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22156      * process that block using the passed callback.
22157      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22158      * for the request to the remote server.
22159      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22160      * object into a block of Roo.data.Records.
22161      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22162      * The function must be passed <ul>
22163      * <li>The Record block object</li>
22164      * <li>The "arg" argument from the load function</li>
22165      * <li>A boolean success indicator</li>
22166      * </ul>
22167      * @param {Object} scope The scope in which to call the callback
22168      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22169      */
22170     load : function(params, reader, callback, scope, arg){
22171         if(this.fireEvent("beforeload", this, params) !== false){
22172             var  o = {
22173                 params : params || {},
22174                 request: {
22175                     callback : callback,
22176                     scope : scope,
22177                     arg : arg
22178                 },
22179                 reader: reader,
22180                 callback : this.loadResponse,
22181                 scope: this
22182             };
22183             if(this.useAjax){
22184                 Roo.applyIf(o, this.conn);
22185                 if(this.activeRequest){
22186                     Roo.Ajax.abort(this.activeRequest);
22187                 }
22188                 this.activeRequest = Roo.Ajax.request(o);
22189             }else{
22190                 this.conn.request(o);
22191             }
22192         }else{
22193             callback.call(scope||this, null, arg, false);
22194         }
22195     },
22196
22197     // private
22198     loadResponse : function(o, success, response){
22199         delete this.activeRequest;
22200         if(!success){
22201             this.fireEvent("loadexception", this, o, response);
22202             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22203             return;
22204         }
22205         var result;
22206         try {
22207             result = o.reader.read(response);
22208         }catch(e){
22209             this.fireEvent("loadexception", this, o, response, e);
22210             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22211             return;
22212         }
22213         
22214         this.fireEvent("load", this, o, o.request.arg);
22215         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22216     },
22217
22218     // private
22219     update : function(dataSet){
22220
22221     },
22222
22223     // private
22224     updateResponse : function(dataSet){
22225
22226     }
22227 });/*
22228  * Based on:
22229  * Ext JS Library 1.1.1
22230  * Copyright(c) 2006-2007, Ext JS, LLC.
22231  *
22232  * Originally Released Under LGPL - original licence link has changed is not relivant.
22233  *
22234  * Fork - LGPL
22235  * <script type="text/javascript">
22236  */
22237
22238 /**
22239  * @class Roo.data.ScriptTagProxy
22240  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22241  * other than the originating domain of the running page.<br><br>
22242  * <p>
22243  * <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
22244  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22245  * <p>
22246  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22247  * source code that is used as the source inside a &lt;script> tag.<br><br>
22248  * <p>
22249  * In order for the browser to process the returned data, the server must wrap the data object
22250  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22251  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22252  * depending on whether the callback name was passed:
22253  * <p>
22254  * <pre><code>
22255 boolean scriptTag = false;
22256 String cb = request.getParameter("callback");
22257 if (cb != null) {
22258     scriptTag = true;
22259     response.setContentType("text/javascript");
22260 } else {
22261     response.setContentType("application/x-json");
22262 }
22263 Writer out = response.getWriter();
22264 if (scriptTag) {
22265     out.write(cb + "(");
22266 }
22267 out.print(dataBlock.toJsonString());
22268 if (scriptTag) {
22269     out.write(");");
22270 }
22271 </pre></code>
22272  *
22273  * @constructor
22274  * @param {Object} config A configuration object.
22275  */
22276 Roo.data.ScriptTagProxy = function(config){
22277     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22278     Roo.apply(this, config);
22279     this.head = document.getElementsByTagName("head")[0];
22280 };
22281
22282 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22283
22284 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22285     /**
22286      * @cfg {String} url The URL from which to request the data object.
22287      */
22288     /**
22289      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22290      */
22291     timeout : 30000,
22292     /**
22293      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22294      * the server the name of the callback function set up by the load call to process the returned data object.
22295      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22296      * javascript output which calls this named function passing the data object as its only parameter.
22297      */
22298     callbackParam : "callback",
22299     /**
22300      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22301      * name to the request.
22302      */
22303     nocache : true,
22304
22305     /**
22306      * Load data from the configured URL, read the data object into
22307      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22308      * process that block using the passed callback.
22309      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22310      * for the request to the remote server.
22311      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22312      * object into a block of Roo.data.Records.
22313      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22314      * The function must be passed <ul>
22315      * <li>The Record block object</li>
22316      * <li>The "arg" argument from the load function</li>
22317      * <li>A boolean success indicator</li>
22318      * </ul>
22319      * @param {Object} scope The scope in which to call the callback
22320      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22321      */
22322     load : function(params, reader, callback, scope, arg){
22323         if(this.fireEvent("beforeload", this, params) !== false){
22324
22325             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22326
22327             var url = this.url;
22328             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22329             if(this.nocache){
22330                 url += "&_dc=" + (new Date().getTime());
22331             }
22332             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22333             var trans = {
22334                 id : transId,
22335                 cb : "stcCallback"+transId,
22336                 scriptId : "stcScript"+transId,
22337                 params : params,
22338                 arg : arg,
22339                 url : url,
22340                 callback : callback,
22341                 scope : scope,
22342                 reader : reader
22343             };
22344             var conn = this;
22345
22346             window[trans.cb] = function(o){
22347                 conn.handleResponse(o, trans);
22348             };
22349
22350             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22351
22352             if(this.autoAbort !== false){
22353                 this.abort();
22354             }
22355
22356             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22357
22358             var script = document.createElement("script");
22359             script.setAttribute("src", url);
22360             script.setAttribute("type", "text/javascript");
22361             script.setAttribute("id", trans.scriptId);
22362             this.head.appendChild(script);
22363
22364             this.trans = trans;
22365         }else{
22366             callback.call(scope||this, null, arg, false);
22367         }
22368     },
22369
22370     // private
22371     isLoading : function(){
22372         return this.trans ? true : false;
22373     },
22374
22375     /**
22376      * Abort the current server request.
22377      */
22378     abort : function(){
22379         if(this.isLoading()){
22380             this.destroyTrans(this.trans);
22381         }
22382     },
22383
22384     // private
22385     destroyTrans : function(trans, isLoaded){
22386         this.head.removeChild(document.getElementById(trans.scriptId));
22387         clearTimeout(trans.timeoutId);
22388         if(isLoaded){
22389             window[trans.cb] = undefined;
22390             try{
22391                 delete window[trans.cb];
22392             }catch(e){}
22393         }else{
22394             // if hasn't been loaded, wait for load to remove it to prevent script error
22395             window[trans.cb] = function(){
22396                 window[trans.cb] = undefined;
22397                 try{
22398                     delete window[trans.cb];
22399                 }catch(e){}
22400             };
22401         }
22402     },
22403
22404     // private
22405     handleResponse : function(o, trans){
22406         this.trans = false;
22407         this.destroyTrans(trans, true);
22408         var result;
22409         try {
22410             result = trans.reader.readRecords(o);
22411         }catch(e){
22412             this.fireEvent("loadexception", this, o, trans.arg, e);
22413             trans.callback.call(trans.scope||window, null, trans.arg, false);
22414             return;
22415         }
22416         this.fireEvent("load", this, o, trans.arg);
22417         trans.callback.call(trans.scope||window, result, trans.arg, true);
22418     },
22419
22420     // private
22421     handleFailure : function(trans){
22422         this.trans = false;
22423         this.destroyTrans(trans, false);
22424         this.fireEvent("loadexception", this, null, trans.arg);
22425         trans.callback.call(trans.scope||window, null, trans.arg, false);
22426     }
22427 });/*
22428  * Based on:
22429  * Ext JS Library 1.1.1
22430  * Copyright(c) 2006-2007, Ext JS, LLC.
22431  *
22432  * Originally Released Under LGPL - original licence link has changed is not relivant.
22433  *
22434  * Fork - LGPL
22435  * <script type="text/javascript">
22436  */
22437
22438 /**
22439  * @class Roo.data.JsonReader
22440  * @extends Roo.data.DataReader
22441  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22442  * based on mappings in a provided Roo.data.Record constructor.
22443  * 
22444  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22445  * in the reply previously. 
22446  * 
22447  * <p>
22448  * Example code:
22449  * <pre><code>
22450 var RecordDef = Roo.data.Record.create([
22451     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22452     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22453 ]);
22454 var myReader = new Roo.data.JsonReader({
22455     totalProperty: "results",    // The property which contains the total dataset size (optional)
22456     root: "rows",                // The property which contains an Array of row objects
22457     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22458 }, RecordDef);
22459 </code></pre>
22460  * <p>
22461  * This would consume a JSON file like this:
22462  * <pre><code>
22463 { 'results': 2, 'rows': [
22464     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22465     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22466 }
22467 </code></pre>
22468  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22469  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22470  * paged from the remote server.
22471  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22472  * @cfg {String} root name of the property which contains the Array of row objects.
22473  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22474  * @constructor
22475  * Create a new JsonReader
22476  * @param {Object} meta Metadata configuration options
22477  * @param {Object} recordType Either an Array of field definition objects,
22478  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22479  */
22480 Roo.data.JsonReader = function(meta, recordType){
22481     
22482     meta = meta || {};
22483     // set some defaults:
22484     Roo.applyIf(meta, {
22485         totalProperty: 'total',
22486         successProperty : 'success',
22487         root : 'data',
22488         id : 'id'
22489     });
22490     
22491     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22492 };
22493 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22494     
22495     /**
22496      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22497      * Used by Store query builder to append _requestMeta to params.
22498      * 
22499      */
22500     metaFromRemote : false,
22501     /**
22502      * This method is only used by a DataProxy which has retrieved data from a remote server.
22503      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22504      * @return {Object} data A data block which is used by an Roo.data.Store object as
22505      * a cache of Roo.data.Records.
22506      */
22507     read : function(response){
22508         var json = response.responseText;
22509        
22510         var o = /* eval:var:o */ eval("("+json+")");
22511         if(!o) {
22512             throw {message: "JsonReader.read: Json object not found"};
22513         }
22514         
22515         if(o.metaData){
22516             
22517             delete this.ef;
22518             this.metaFromRemote = true;
22519             this.meta = o.metaData;
22520             this.recordType = Roo.data.Record.create(o.metaData.fields);
22521             this.onMetaChange(this.meta, this.recordType, o);
22522         }
22523         return this.readRecords(o);
22524     },
22525
22526     // private function a store will implement
22527     onMetaChange : function(meta, recordType, o){
22528
22529     },
22530
22531     /**
22532          * @ignore
22533          */
22534     simpleAccess: function(obj, subsc) {
22535         return obj[subsc];
22536     },
22537
22538         /**
22539          * @ignore
22540          */
22541     getJsonAccessor: function(){
22542         var re = /[\[\.]/;
22543         return function(expr) {
22544             try {
22545                 return(re.test(expr))
22546                     ? new Function("obj", "return obj." + expr)
22547                     : function(obj){
22548                         return obj[expr];
22549                     };
22550             } catch(e){}
22551             return Roo.emptyFn;
22552         };
22553     }(),
22554
22555     /**
22556      * Create a data block containing Roo.data.Records from an XML document.
22557      * @param {Object} o An object which contains an Array of row objects in the property specified
22558      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22559      * which contains the total size of the dataset.
22560      * @return {Object} data A data block which is used by an Roo.data.Store object as
22561      * a cache of Roo.data.Records.
22562      */
22563     readRecords : function(o){
22564         /**
22565          * After any data loads, the raw JSON data is available for further custom processing.
22566          * @type Object
22567          */
22568         this.o = o;
22569         var s = this.meta, Record = this.recordType,
22570             f = Record.prototype.fields, fi = f.items, fl = f.length;
22571
22572 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22573         if (!this.ef) {
22574             if(s.totalProperty) {
22575                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22576                 }
22577                 if(s.successProperty) {
22578                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22579                 }
22580                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22581                 if (s.id) {
22582                         var g = this.getJsonAccessor(s.id);
22583                         this.getId = function(rec) {
22584                                 var r = g(rec);
22585                                 return (r === undefined || r === "") ? null : r;
22586                         };
22587                 } else {
22588                         this.getId = function(){return null;};
22589                 }
22590             this.ef = [];
22591             for(var jj = 0; jj < fl; jj++){
22592                 f = fi[jj];
22593                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22594                 this.ef[jj] = this.getJsonAccessor(map);
22595             }
22596         }
22597
22598         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22599         if(s.totalProperty){
22600             var vt = parseInt(this.getTotal(o), 10);
22601             if(!isNaN(vt)){
22602                 totalRecords = vt;
22603             }
22604         }
22605         if(s.successProperty){
22606             var vs = this.getSuccess(o);
22607             if(vs === false || vs === 'false'){
22608                 success = false;
22609             }
22610         }
22611         var records = [];
22612             for(var i = 0; i < c; i++){
22613                     var n = root[i];
22614                 var values = {};
22615                 var id = this.getId(n);
22616                 for(var j = 0; j < fl; j++){
22617                     f = fi[j];
22618                 var v = this.ef[j](n);
22619                 if (!f.convert) {
22620                     Roo.log('missing convert for ' + f.name);
22621                     Roo.log(f);
22622                     continue;
22623                 }
22624                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22625                 }
22626                 var record = new Record(values, id);
22627                 record.json = n;
22628                 records[i] = record;
22629             }
22630             return {
22631             raw : o,
22632                 success : success,
22633                 records : records,
22634                 totalRecords : totalRecords
22635             };
22636     }
22637 });/*
22638  * Based on:
22639  * Ext JS Library 1.1.1
22640  * Copyright(c) 2006-2007, Ext JS, LLC.
22641  *
22642  * Originally Released Under LGPL - original licence link has changed is not relivant.
22643  *
22644  * Fork - LGPL
22645  * <script type="text/javascript">
22646  */
22647
22648 /**
22649  * @class Roo.data.XmlReader
22650  * @extends Roo.data.DataReader
22651  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22652  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22653  * <p>
22654  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22655  * header in the HTTP response must be set to "text/xml".</em>
22656  * <p>
22657  * Example code:
22658  * <pre><code>
22659 var RecordDef = Roo.data.Record.create([
22660    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22661    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22662 ]);
22663 var myReader = new Roo.data.XmlReader({
22664    totalRecords: "results", // The element which contains the total dataset size (optional)
22665    record: "row",           // The repeated element which contains row information
22666    id: "id"                 // The element within the row that provides an ID for the record (optional)
22667 }, RecordDef);
22668 </code></pre>
22669  * <p>
22670  * This would consume an XML file like this:
22671  * <pre><code>
22672 &lt;?xml?>
22673 &lt;dataset>
22674  &lt;results>2&lt;/results>
22675  &lt;row>
22676    &lt;id>1&lt;/id>
22677    &lt;name>Bill&lt;/name>
22678    &lt;occupation>Gardener&lt;/occupation>
22679  &lt;/row>
22680  &lt;row>
22681    &lt;id>2&lt;/id>
22682    &lt;name>Ben&lt;/name>
22683    &lt;occupation>Horticulturalist&lt;/occupation>
22684  &lt;/row>
22685 &lt;/dataset>
22686 </code></pre>
22687  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22688  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22689  * paged from the remote server.
22690  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22691  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22692  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22693  * a record identifier value.
22694  * @constructor
22695  * Create a new XmlReader
22696  * @param {Object} meta Metadata configuration options
22697  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22698  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22699  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22700  */
22701 Roo.data.XmlReader = function(meta, recordType){
22702     meta = meta || {};
22703     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22704 };
22705 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22706     /**
22707      * This method is only used by a DataProxy which has retrieved data from a remote server.
22708          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22709          * to contain a method called 'responseXML' that returns an XML document object.
22710      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22711      * a cache of Roo.data.Records.
22712      */
22713     read : function(response){
22714         var doc = response.responseXML;
22715         if(!doc) {
22716             throw {message: "XmlReader.read: XML Document not available"};
22717         }
22718         return this.readRecords(doc);
22719     },
22720
22721     /**
22722      * Create a data block containing Roo.data.Records from an XML document.
22723          * @param {Object} doc A parsed XML document.
22724      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22725      * a cache of Roo.data.Records.
22726      */
22727     readRecords : function(doc){
22728         /**
22729          * After any data loads/reads, the raw XML Document is available for further custom processing.
22730          * @type XMLDocument
22731          */
22732         this.xmlData = doc;
22733         var root = doc.documentElement || doc;
22734         var q = Roo.DomQuery;
22735         var recordType = this.recordType, fields = recordType.prototype.fields;
22736         var sid = this.meta.id;
22737         var totalRecords = 0, success = true;
22738         if(this.meta.totalRecords){
22739             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22740         }
22741         
22742         if(this.meta.success){
22743             var sv = q.selectValue(this.meta.success, root, true);
22744             success = sv !== false && sv !== 'false';
22745         }
22746         var records = [];
22747         var ns = q.select(this.meta.record, root);
22748         for(var i = 0, len = ns.length; i < len; i++) {
22749                 var n = ns[i];
22750                 var values = {};
22751                 var id = sid ? q.selectValue(sid, n) : undefined;
22752                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22753                     var f = fields.items[j];
22754                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22755                     v = f.convert(v);
22756                     values[f.name] = v;
22757                 }
22758                 var record = new recordType(values, id);
22759                 record.node = n;
22760                 records[records.length] = record;
22761             }
22762
22763             return {
22764                 success : success,
22765                 records : records,
22766                 totalRecords : totalRecords || records.length
22767             };
22768     }
22769 });/*
22770  * Based on:
22771  * Ext JS Library 1.1.1
22772  * Copyright(c) 2006-2007, Ext JS, LLC.
22773  *
22774  * Originally Released Under LGPL - original licence link has changed is not relivant.
22775  *
22776  * Fork - LGPL
22777  * <script type="text/javascript">
22778  */
22779
22780 /**
22781  * @class Roo.data.ArrayReader
22782  * @extends Roo.data.DataReader
22783  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22784  * Each element of that Array represents a row of data fields. The
22785  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22786  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22787  * <p>
22788  * Example code:.
22789  * <pre><code>
22790 var RecordDef = Roo.data.Record.create([
22791     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22792     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22793 ]);
22794 var myReader = new Roo.data.ArrayReader({
22795     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22796 }, RecordDef);
22797 </code></pre>
22798  * <p>
22799  * This would consume an Array like this:
22800  * <pre><code>
22801 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22802   </code></pre>
22803  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22804  * @constructor
22805  * Create a new JsonReader
22806  * @param {Object} meta Metadata configuration options.
22807  * @param {Object} recordType Either an Array of field definition objects
22808  * as specified to {@link Roo.data.Record#create},
22809  * or an {@link Roo.data.Record} object
22810  * created using {@link Roo.data.Record#create}.
22811  */
22812 Roo.data.ArrayReader = function(meta, recordType){
22813     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22814 };
22815
22816 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22817     /**
22818      * Create a data block containing Roo.data.Records from an XML document.
22819      * @param {Object} o An Array of row objects which represents the dataset.
22820      * @return {Object} data A data block which is used by an Roo.data.Store object as
22821      * a cache of Roo.data.Records.
22822      */
22823     readRecords : function(o){
22824         var sid = this.meta ? this.meta.id : null;
22825         var recordType = this.recordType, fields = recordType.prototype.fields;
22826         var records = [];
22827         var root = o;
22828             for(var i = 0; i < root.length; i++){
22829                     var n = root[i];
22830                 var values = {};
22831                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22832                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22833                 var f = fields.items[j];
22834                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22835                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22836                 v = f.convert(v);
22837                 values[f.name] = v;
22838             }
22839                 var record = new recordType(values, id);
22840                 record.json = n;
22841                 records[records.length] = record;
22842             }
22843             return {
22844                 records : records,
22845                 totalRecords : records.length
22846             };
22847     }
22848 });/*
22849  * Based on:
22850  * Ext JS Library 1.1.1
22851  * Copyright(c) 2006-2007, Ext JS, LLC.
22852  *
22853  * Originally Released Under LGPL - original licence link has changed is not relivant.
22854  *
22855  * Fork - LGPL
22856  * <script type="text/javascript">
22857  */
22858
22859
22860 /**
22861  * @class Roo.data.Tree
22862  * @extends Roo.util.Observable
22863  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22864  * in the tree have most standard DOM functionality.
22865  * @constructor
22866  * @param {Node} root (optional) The root node
22867  */
22868 Roo.data.Tree = function(root){
22869    this.nodeHash = {};
22870    /**
22871     * The root node for this tree
22872     * @type Node
22873     */
22874    this.root = null;
22875    if(root){
22876        this.setRootNode(root);
22877    }
22878    this.addEvents({
22879        /**
22880         * @event append
22881         * Fires when a new child node is appended to a node in this tree.
22882         * @param {Tree} tree The owner tree
22883         * @param {Node} parent The parent node
22884         * @param {Node} node The newly appended node
22885         * @param {Number} index The index of the newly appended node
22886         */
22887        "append" : true,
22888        /**
22889         * @event remove
22890         * Fires when a child node is removed from a node in this tree.
22891         * @param {Tree} tree The owner tree
22892         * @param {Node} parent The parent node
22893         * @param {Node} node The child node removed
22894         */
22895        "remove" : true,
22896        /**
22897         * @event move
22898         * Fires when a node is moved to a new location in the tree
22899         * @param {Tree} tree The owner tree
22900         * @param {Node} node The node moved
22901         * @param {Node} oldParent The old parent of this node
22902         * @param {Node} newParent The new parent of this node
22903         * @param {Number} index The index it was moved to
22904         */
22905        "move" : true,
22906        /**
22907         * @event insert
22908         * Fires when a new child node is inserted in a node in this tree.
22909         * @param {Tree} tree The owner tree
22910         * @param {Node} parent The parent node
22911         * @param {Node} node The child node inserted
22912         * @param {Node} refNode The child node the node was inserted before
22913         */
22914        "insert" : true,
22915        /**
22916         * @event beforeappend
22917         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22918         * @param {Tree} tree The owner tree
22919         * @param {Node} parent The parent node
22920         * @param {Node} node The child node to be appended
22921         */
22922        "beforeappend" : true,
22923        /**
22924         * @event beforeremove
22925         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22926         * @param {Tree} tree The owner tree
22927         * @param {Node} parent The parent node
22928         * @param {Node} node The child node to be removed
22929         */
22930        "beforeremove" : true,
22931        /**
22932         * @event beforemove
22933         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22934         * @param {Tree} tree The owner tree
22935         * @param {Node} node The node being moved
22936         * @param {Node} oldParent The parent of the node
22937         * @param {Node} newParent The new parent the node is moving to
22938         * @param {Number} index The index it is being moved to
22939         */
22940        "beforemove" : true,
22941        /**
22942         * @event beforeinsert
22943         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22944         * @param {Tree} tree The owner tree
22945         * @param {Node} parent The parent node
22946         * @param {Node} node The child node to be inserted
22947         * @param {Node} refNode The child node the node is being inserted before
22948         */
22949        "beforeinsert" : true
22950    });
22951
22952     Roo.data.Tree.superclass.constructor.call(this);
22953 };
22954
22955 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22956     pathSeparator: "/",
22957
22958     proxyNodeEvent : function(){
22959         return this.fireEvent.apply(this, arguments);
22960     },
22961
22962     /**
22963      * Returns the root node for this tree.
22964      * @return {Node}
22965      */
22966     getRootNode : function(){
22967         return this.root;
22968     },
22969
22970     /**
22971      * Sets the root node for this tree.
22972      * @param {Node} node
22973      * @return {Node}
22974      */
22975     setRootNode : function(node){
22976         this.root = node;
22977         node.ownerTree = this;
22978         node.isRoot = true;
22979         this.registerNode(node);
22980         return node;
22981     },
22982
22983     /**
22984      * Gets a node in this tree by its id.
22985      * @param {String} id
22986      * @return {Node}
22987      */
22988     getNodeById : function(id){
22989         return this.nodeHash[id];
22990     },
22991
22992     registerNode : function(node){
22993         this.nodeHash[node.id] = node;
22994     },
22995
22996     unregisterNode : function(node){
22997         delete this.nodeHash[node.id];
22998     },
22999
23000     toString : function(){
23001         return "[Tree"+(this.id?" "+this.id:"")+"]";
23002     }
23003 });
23004
23005 /**
23006  * @class Roo.data.Node
23007  * @extends Roo.util.Observable
23008  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23009  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23010  * @constructor
23011  * @param {Object} attributes The attributes/config for the node
23012  */
23013 Roo.data.Node = function(attributes){
23014     /**
23015      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23016      * @type {Object}
23017      */
23018     this.attributes = attributes || {};
23019     this.leaf = this.attributes.leaf;
23020     /**
23021      * The node id. @type String
23022      */
23023     this.id = this.attributes.id;
23024     if(!this.id){
23025         this.id = Roo.id(null, "ynode-");
23026         this.attributes.id = this.id;
23027     }
23028      
23029     
23030     /**
23031      * All child nodes of this node. @type Array
23032      */
23033     this.childNodes = [];
23034     if(!this.childNodes.indexOf){ // indexOf is a must
23035         this.childNodes.indexOf = function(o){
23036             for(var i = 0, len = this.length; i < len; i++){
23037                 if(this[i] == o) {
23038                     return i;
23039                 }
23040             }
23041             return -1;
23042         };
23043     }
23044     /**
23045      * The parent node for this node. @type Node
23046      */
23047     this.parentNode = null;
23048     /**
23049      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23050      */
23051     this.firstChild = null;
23052     /**
23053      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23054      */
23055     this.lastChild = null;
23056     /**
23057      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23058      */
23059     this.previousSibling = null;
23060     /**
23061      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23062      */
23063     this.nextSibling = null;
23064
23065     this.addEvents({
23066        /**
23067         * @event append
23068         * Fires when a new child node is appended
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} this This node
23071         * @param {Node} node The newly appended node
23072         * @param {Number} index The index of the newly appended node
23073         */
23074        "append" : true,
23075        /**
23076         * @event remove
23077         * Fires when a child node is removed
23078         * @param {Tree} tree The owner tree
23079         * @param {Node} this This node
23080         * @param {Node} node The removed node
23081         */
23082        "remove" : true,
23083        /**
23084         * @event move
23085         * Fires when this node is moved to a new location in the tree
23086         * @param {Tree} tree The owner tree
23087         * @param {Node} this This node
23088         * @param {Node} oldParent The old parent of this node
23089         * @param {Node} newParent The new parent of this node
23090         * @param {Number} index The index it was moved to
23091         */
23092        "move" : true,
23093        /**
23094         * @event insert
23095         * Fires when a new child node is inserted.
23096         * @param {Tree} tree The owner tree
23097         * @param {Node} this This node
23098         * @param {Node} node The child node inserted
23099         * @param {Node} refNode The child node the node was inserted before
23100         */
23101        "insert" : true,
23102        /**
23103         * @event beforeappend
23104         * Fires before a new child is appended, return false to cancel the append.
23105         * @param {Tree} tree The owner tree
23106         * @param {Node} this This node
23107         * @param {Node} node The child node to be appended
23108         */
23109        "beforeappend" : true,
23110        /**
23111         * @event beforeremove
23112         * Fires before a child is removed, return false to cancel the remove.
23113         * @param {Tree} tree The owner tree
23114         * @param {Node} this This node
23115         * @param {Node} node The child node to be removed
23116         */
23117        "beforeremove" : true,
23118        /**
23119         * @event beforemove
23120         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23121         * @param {Tree} tree The owner tree
23122         * @param {Node} this This node
23123         * @param {Node} oldParent The parent of this node
23124         * @param {Node} newParent The new parent this node is moving to
23125         * @param {Number} index The index it is being moved to
23126         */
23127        "beforemove" : true,
23128        /**
23129         * @event beforeinsert
23130         * Fires before a new child is inserted, return false to cancel the insert.
23131         * @param {Tree} tree The owner tree
23132         * @param {Node} this This node
23133         * @param {Node} node The child node to be inserted
23134         * @param {Node} refNode The child node the node is being inserted before
23135         */
23136        "beforeinsert" : true
23137    });
23138     this.listeners = this.attributes.listeners;
23139     Roo.data.Node.superclass.constructor.call(this);
23140 };
23141
23142 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23143     fireEvent : function(evtName){
23144         // first do standard event for this node
23145         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23146             return false;
23147         }
23148         // then bubble it up to the tree if the event wasn't cancelled
23149         var ot = this.getOwnerTree();
23150         if(ot){
23151             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23152                 return false;
23153             }
23154         }
23155         return true;
23156     },
23157
23158     /**
23159      * Returns true if this node is a leaf
23160      * @return {Boolean}
23161      */
23162     isLeaf : function(){
23163         return this.leaf === true;
23164     },
23165
23166     // private
23167     setFirstChild : function(node){
23168         this.firstChild = node;
23169     },
23170
23171     //private
23172     setLastChild : function(node){
23173         this.lastChild = node;
23174     },
23175
23176
23177     /**
23178      * Returns true if this node is the last child of its parent
23179      * @return {Boolean}
23180      */
23181     isLast : function(){
23182        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23183     },
23184
23185     /**
23186      * Returns true if this node is the first child of its parent
23187      * @return {Boolean}
23188      */
23189     isFirst : function(){
23190        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23191     },
23192
23193     hasChildNodes : function(){
23194         return !this.isLeaf() && this.childNodes.length > 0;
23195     },
23196
23197     /**
23198      * Insert node(s) as the last child node of this node.
23199      * @param {Node/Array} node The node or Array of nodes to append
23200      * @return {Node} The appended node if single append, or null if an array was passed
23201      */
23202     appendChild : function(node){
23203         var multi = false;
23204         if(node instanceof Array){
23205             multi = node;
23206         }else if(arguments.length > 1){
23207             multi = arguments;
23208         }
23209         // if passed an array or multiple args do them one by one
23210         if(multi){
23211             for(var i = 0, len = multi.length; i < len; i++) {
23212                 this.appendChild(multi[i]);
23213             }
23214         }else{
23215             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23216                 return false;
23217             }
23218             var index = this.childNodes.length;
23219             var oldParent = node.parentNode;
23220             // it's a move, make sure we move it cleanly
23221             if(oldParent){
23222                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23223                     return false;
23224                 }
23225                 oldParent.removeChild(node);
23226             }
23227             index = this.childNodes.length;
23228             if(index == 0){
23229                 this.setFirstChild(node);
23230             }
23231             this.childNodes.push(node);
23232             node.parentNode = this;
23233             var ps = this.childNodes[index-1];
23234             if(ps){
23235                 node.previousSibling = ps;
23236                 ps.nextSibling = node;
23237             }else{
23238                 node.previousSibling = null;
23239             }
23240             node.nextSibling = null;
23241             this.setLastChild(node);
23242             node.setOwnerTree(this.getOwnerTree());
23243             this.fireEvent("append", this.ownerTree, this, node, index);
23244             if(oldParent){
23245                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23246             }
23247             return node;
23248         }
23249     },
23250
23251     /**
23252      * Removes a child node from this node.
23253      * @param {Node} node The node to remove
23254      * @return {Node} The removed node
23255      */
23256     removeChild : function(node){
23257         var index = this.childNodes.indexOf(node);
23258         if(index == -1){
23259             return false;
23260         }
23261         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23262             return false;
23263         }
23264
23265         // remove it from childNodes collection
23266         this.childNodes.splice(index, 1);
23267
23268         // update siblings
23269         if(node.previousSibling){
23270             node.previousSibling.nextSibling = node.nextSibling;
23271         }
23272         if(node.nextSibling){
23273             node.nextSibling.previousSibling = node.previousSibling;
23274         }
23275
23276         // update child refs
23277         if(this.firstChild == node){
23278             this.setFirstChild(node.nextSibling);
23279         }
23280         if(this.lastChild == node){
23281             this.setLastChild(node.previousSibling);
23282         }
23283
23284         node.setOwnerTree(null);
23285         // clear any references from the node
23286         node.parentNode = null;
23287         node.previousSibling = null;
23288         node.nextSibling = null;
23289         this.fireEvent("remove", this.ownerTree, this, node);
23290         return node;
23291     },
23292
23293     /**
23294      * Inserts the first node before the second node in this nodes childNodes collection.
23295      * @param {Node} node The node to insert
23296      * @param {Node} refNode The node to insert before (if null the node is appended)
23297      * @return {Node} The inserted node
23298      */
23299     insertBefore : function(node, refNode){
23300         if(!refNode){ // like standard Dom, refNode can be null for append
23301             return this.appendChild(node);
23302         }
23303         // nothing to do
23304         if(node == refNode){
23305             return false;
23306         }
23307
23308         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23309             return false;
23310         }
23311         var index = this.childNodes.indexOf(refNode);
23312         var oldParent = node.parentNode;
23313         var refIndex = index;
23314
23315         // when moving internally, indexes will change after remove
23316         if(oldParent == this && this.childNodes.indexOf(node) < index){
23317             refIndex--;
23318         }
23319
23320         // it's a move, make sure we move it cleanly
23321         if(oldParent){
23322             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23323                 return false;
23324             }
23325             oldParent.removeChild(node);
23326         }
23327         if(refIndex == 0){
23328             this.setFirstChild(node);
23329         }
23330         this.childNodes.splice(refIndex, 0, node);
23331         node.parentNode = this;
23332         var ps = this.childNodes[refIndex-1];
23333         if(ps){
23334             node.previousSibling = ps;
23335             ps.nextSibling = node;
23336         }else{
23337             node.previousSibling = null;
23338         }
23339         node.nextSibling = refNode;
23340         refNode.previousSibling = node;
23341         node.setOwnerTree(this.getOwnerTree());
23342         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23343         if(oldParent){
23344             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23345         }
23346         return node;
23347     },
23348
23349     /**
23350      * Returns the child node at the specified index.
23351      * @param {Number} index
23352      * @return {Node}
23353      */
23354     item : function(index){
23355         return this.childNodes[index];
23356     },
23357
23358     /**
23359      * Replaces one child node in this node with another.
23360      * @param {Node} newChild The replacement node
23361      * @param {Node} oldChild The node to replace
23362      * @return {Node} The replaced node
23363      */
23364     replaceChild : function(newChild, oldChild){
23365         this.insertBefore(newChild, oldChild);
23366         this.removeChild(oldChild);
23367         return oldChild;
23368     },
23369
23370     /**
23371      * Returns the index of a child node
23372      * @param {Node} node
23373      * @return {Number} The index of the node or -1 if it was not found
23374      */
23375     indexOf : function(child){
23376         return this.childNodes.indexOf(child);
23377     },
23378
23379     /**
23380      * Returns the tree this node is in.
23381      * @return {Tree}
23382      */
23383     getOwnerTree : function(){
23384         // if it doesn't have one, look for one
23385         if(!this.ownerTree){
23386             var p = this;
23387             while(p){
23388                 if(p.ownerTree){
23389                     this.ownerTree = p.ownerTree;
23390                     break;
23391                 }
23392                 p = p.parentNode;
23393             }
23394         }
23395         return this.ownerTree;
23396     },
23397
23398     /**
23399      * Returns depth of this node (the root node has a depth of 0)
23400      * @return {Number}
23401      */
23402     getDepth : function(){
23403         var depth = 0;
23404         var p = this;
23405         while(p.parentNode){
23406             ++depth;
23407             p = p.parentNode;
23408         }
23409         return depth;
23410     },
23411
23412     // private
23413     setOwnerTree : function(tree){
23414         // if it's move, we need to update everyone
23415         if(tree != this.ownerTree){
23416             if(this.ownerTree){
23417                 this.ownerTree.unregisterNode(this);
23418             }
23419             this.ownerTree = tree;
23420             var cs = this.childNodes;
23421             for(var i = 0, len = cs.length; i < len; i++) {
23422                 cs[i].setOwnerTree(tree);
23423             }
23424             if(tree){
23425                 tree.registerNode(this);
23426             }
23427         }
23428     },
23429
23430     /**
23431      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23432      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23433      * @return {String} The path
23434      */
23435     getPath : function(attr){
23436         attr = attr || "id";
23437         var p = this.parentNode;
23438         var b = [this.attributes[attr]];
23439         while(p){
23440             b.unshift(p.attributes[attr]);
23441             p = p.parentNode;
23442         }
23443         var sep = this.getOwnerTree().pathSeparator;
23444         return sep + b.join(sep);
23445     },
23446
23447     /**
23448      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23449      * function call will be the scope provided or the current node. The arguments to the function
23450      * will be the args provided or the current node. If the function returns false at any point,
23451      * the bubble is stopped.
23452      * @param {Function} fn The function to call
23453      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23454      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23455      */
23456     bubble : function(fn, scope, args){
23457         var p = this;
23458         while(p){
23459             if(fn.call(scope || p, args || p) === false){
23460                 break;
23461             }
23462             p = p.parentNode;
23463         }
23464     },
23465
23466     /**
23467      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23468      * function call will be the scope provided or the current node. The arguments to the function
23469      * will be the args provided or the current node. If the function returns false at any point,
23470      * the cascade is stopped on that branch.
23471      * @param {Function} fn The function to call
23472      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23473      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23474      */
23475     cascade : function(fn, scope, args){
23476         if(fn.call(scope || this, args || this) !== false){
23477             var cs = this.childNodes;
23478             for(var i = 0, len = cs.length; i < len; i++) {
23479                 cs[i].cascade(fn, scope, args);
23480             }
23481         }
23482     },
23483
23484     /**
23485      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23486      * function call will be the scope provided or the current node. The arguments to the function
23487      * will be the args provided or the current node. If the function returns false at any point,
23488      * the iteration stops.
23489      * @param {Function} fn The function to call
23490      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23491      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23492      */
23493     eachChild : function(fn, scope, args){
23494         var cs = this.childNodes;
23495         for(var i = 0, len = cs.length; i < len; i++) {
23496                 if(fn.call(scope || this, args || cs[i]) === false){
23497                     break;
23498                 }
23499         }
23500     },
23501
23502     /**
23503      * Finds the first child that has the attribute with the specified value.
23504      * @param {String} attribute The attribute name
23505      * @param {Mixed} value The value to search for
23506      * @return {Node} The found child or null if none was found
23507      */
23508     findChild : function(attribute, value){
23509         var cs = this.childNodes;
23510         for(var i = 0, len = cs.length; i < len; i++) {
23511                 if(cs[i].attributes[attribute] == value){
23512                     return cs[i];
23513                 }
23514         }
23515         return null;
23516     },
23517
23518     /**
23519      * Finds the first child by a custom function. The child matches if the function passed
23520      * returns true.
23521      * @param {Function} fn
23522      * @param {Object} scope (optional)
23523      * @return {Node} The found child or null if none was found
23524      */
23525     findChildBy : function(fn, scope){
23526         var cs = this.childNodes;
23527         for(var i = 0, len = cs.length; i < len; i++) {
23528                 if(fn.call(scope||cs[i], cs[i]) === true){
23529                     return cs[i];
23530                 }
23531         }
23532         return null;
23533     },
23534
23535     /**
23536      * Sorts this nodes children using the supplied sort function
23537      * @param {Function} fn
23538      * @param {Object} scope (optional)
23539      */
23540     sort : function(fn, scope){
23541         var cs = this.childNodes;
23542         var len = cs.length;
23543         if(len > 0){
23544             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23545             cs.sort(sortFn);
23546             for(var i = 0; i < len; i++){
23547                 var n = cs[i];
23548                 n.previousSibling = cs[i-1];
23549                 n.nextSibling = cs[i+1];
23550                 if(i == 0){
23551                     this.setFirstChild(n);
23552                 }
23553                 if(i == len-1){
23554                     this.setLastChild(n);
23555                 }
23556             }
23557         }
23558     },
23559
23560     /**
23561      * Returns true if this node is an ancestor (at any point) of the passed node.
23562      * @param {Node} node
23563      * @return {Boolean}
23564      */
23565     contains : function(node){
23566         return node.isAncestor(this);
23567     },
23568
23569     /**
23570      * Returns true if the passed node is an ancestor (at any point) of this node.
23571      * @param {Node} node
23572      * @return {Boolean}
23573      */
23574     isAncestor : function(node){
23575         var p = this.parentNode;
23576         while(p){
23577             if(p == node){
23578                 return true;
23579             }
23580             p = p.parentNode;
23581         }
23582         return false;
23583     },
23584
23585     toString : function(){
23586         return "[Node"+(this.id?" "+this.id:"")+"]";
23587     }
23588 });/*
23589  * Based on:
23590  * Ext JS Library 1.1.1
23591  * Copyright(c) 2006-2007, Ext JS, LLC.
23592  *
23593  * Originally Released Under LGPL - original licence link has changed is not relivant.
23594  *
23595  * Fork - LGPL
23596  * <script type="text/javascript">
23597  */
23598  (function(){ 
23599 /**
23600  * @class Roo.Layer
23601  * @extends Roo.Element
23602  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23603  * automatic maintaining of shadow/shim positions.
23604  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23605  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23606  * you can pass a string with a CSS class name. False turns off the shadow.
23607  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23608  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23609  * @cfg {String} cls CSS class to add to the element
23610  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23611  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23612  * @constructor
23613  * @param {Object} config An object with config options.
23614  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23615  */
23616
23617 Roo.Layer = function(config, existingEl){
23618     config = config || {};
23619     var dh = Roo.DomHelper;
23620     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23621     if(existingEl){
23622         this.dom = Roo.getDom(existingEl);
23623     }
23624     if(!this.dom){
23625         var o = config.dh || {tag: "div", cls: "x-layer"};
23626         this.dom = dh.append(pel, o);
23627     }
23628     if(config.cls){
23629         this.addClass(config.cls);
23630     }
23631     this.constrain = config.constrain !== false;
23632     this.visibilityMode = Roo.Element.VISIBILITY;
23633     if(config.id){
23634         this.id = this.dom.id = config.id;
23635     }else{
23636         this.id = Roo.id(this.dom);
23637     }
23638     this.zindex = config.zindex || this.getZIndex();
23639     this.position("absolute", this.zindex);
23640     if(config.shadow){
23641         this.shadowOffset = config.shadowOffset || 4;
23642         this.shadow = new Roo.Shadow({
23643             offset : this.shadowOffset,
23644             mode : config.shadow
23645         });
23646     }else{
23647         this.shadowOffset = 0;
23648     }
23649     this.useShim = config.shim !== false && Roo.useShims;
23650     this.useDisplay = config.useDisplay;
23651     this.hide();
23652 };
23653
23654 var supr = Roo.Element.prototype;
23655
23656 // shims are shared among layer to keep from having 100 iframes
23657 var shims = [];
23658
23659 Roo.extend(Roo.Layer, Roo.Element, {
23660
23661     getZIndex : function(){
23662         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23663     },
23664
23665     getShim : function(){
23666         if(!this.useShim){
23667             return null;
23668         }
23669         if(this.shim){
23670             return this.shim;
23671         }
23672         var shim = shims.shift();
23673         if(!shim){
23674             shim = this.createShim();
23675             shim.enableDisplayMode('block');
23676             shim.dom.style.display = 'none';
23677             shim.dom.style.visibility = 'visible';
23678         }
23679         var pn = this.dom.parentNode;
23680         if(shim.dom.parentNode != pn){
23681             pn.insertBefore(shim.dom, this.dom);
23682         }
23683         shim.setStyle('z-index', this.getZIndex()-2);
23684         this.shim = shim;
23685         return shim;
23686     },
23687
23688     hideShim : function(){
23689         if(this.shim){
23690             this.shim.setDisplayed(false);
23691             shims.push(this.shim);
23692             delete this.shim;
23693         }
23694     },
23695
23696     disableShadow : function(){
23697         if(this.shadow){
23698             this.shadowDisabled = true;
23699             this.shadow.hide();
23700             this.lastShadowOffset = this.shadowOffset;
23701             this.shadowOffset = 0;
23702         }
23703     },
23704
23705     enableShadow : function(show){
23706         if(this.shadow){
23707             this.shadowDisabled = false;
23708             this.shadowOffset = this.lastShadowOffset;
23709             delete this.lastShadowOffset;
23710             if(show){
23711                 this.sync(true);
23712             }
23713         }
23714     },
23715
23716     // private
23717     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23718     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23719     sync : function(doShow){
23720         var sw = this.shadow;
23721         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23722             var sh = this.getShim();
23723
23724             var w = this.getWidth(),
23725                 h = this.getHeight();
23726
23727             var l = this.getLeft(true),
23728                 t = this.getTop(true);
23729
23730             if(sw && !this.shadowDisabled){
23731                 if(doShow && !sw.isVisible()){
23732                     sw.show(this);
23733                 }else{
23734                     sw.realign(l, t, w, h);
23735                 }
23736                 if(sh){
23737                     if(doShow){
23738                        sh.show();
23739                     }
23740                     // fit the shim behind the shadow, so it is shimmed too
23741                     var a = sw.adjusts, s = sh.dom.style;
23742                     s.left = (Math.min(l, l+a.l))+"px";
23743                     s.top = (Math.min(t, t+a.t))+"px";
23744                     s.width = (w+a.w)+"px";
23745                     s.height = (h+a.h)+"px";
23746                 }
23747             }else if(sh){
23748                 if(doShow){
23749                    sh.show();
23750                 }
23751                 sh.setSize(w, h);
23752                 sh.setLeftTop(l, t);
23753             }
23754             
23755         }
23756     },
23757
23758     // private
23759     destroy : function(){
23760         this.hideShim();
23761         if(this.shadow){
23762             this.shadow.hide();
23763         }
23764         this.removeAllListeners();
23765         var pn = this.dom.parentNode;
23766         if(pn){
23767             pn.removeChild(this.dom);
23768         }
23769         Roo.Element.uncache(this.id);
23770     },
23771
23772     remove : function(){
23773         this.destroy();
23774     },
23775
23776     // private
23777     beginUpdate : function(){
23778         this.updating = true;
23779     },
23780
23781     // private
23782     endUpdate : function(){
23783         this.updating = false;
23784         this.sync(true);
23785     },
23786
23787     // private
23788     hideUnders : function(negOffset){
23789         if(this.shadow){
23790             this.shadow.hide();
23791         }
23792         this.hideShim();
23793     },
23794
23795     // private
23796     constrainXY : function(){
23797         if(this.constrain){
23798             var vw = Roo.lib.Dom.getViewWidth(),
23799                 vh = Roo.lib.Dom.getViewHeight();
23800             var s = Roo.get(document).getScroll();
23801
23802             var xy = this.getXY();
23803             var x = xy[0], y = xy[1];   
23804             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23805             // only move it if it needs it
23806             var moved = false;
23807             // first validate right/bottom
23808             if((x + w) > vw+s.left){
23809                 x = vw - w - this.shadowOffset;
23810                 moved = true;
23811             }
23812             if((y + h) > vh+s.top){
23813                 y = vh - h - this.shadowOffset;
23814                 moved = true;
23815             }
23816             // then make sure top/left isn't negative
23817             if(x < s.left){
23818                 x = s.left;
23819                 moved = true;
23820             }
23821             if(y < s.top){
23822                 y = s.top;
23823                 moved = true;
23824             }
23825             if(moved){
23826                 if(this.avoidY){
23827                     var ay = this.avoidY;
23828                     if(y <= ay && (y+h) >= ay){
23829                         y = ay-h-5;   
23830                     }
23831                 }
23832                 xy = [x, y];
23833                 this.storeXY(xy);
23834                 supr.setXY.call(this, xy);
23835                 this.sync();
23836             }
23837         }
23838     },
23839
23840     isVisible : function(){
23841         return this.visible;    
23842     },
23843
23844     // private
23845     showAction : function(){
23846         this.visible = true; // track visibility to prevent getStyle calls
23847         if(this.useDisplay === true){
23848             this.setDisplayed("");
23849         }else if(this.lastXY){
23850             supr.setXY.call(this, this.lastXY);
23851         }else if(this.lastLT){
23852             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23853         }
23854     },
23855
23856     // private
23857     hideAction : function(){
23858         this.visible = false;
23859         if(this.useDisplay === true){
23860             this.setDisplayed(false);
23861         }else{
23862             this.setLeftTop(-10000,-10000);
23863         }
23864     },
23865
23866     // overridden Element method
23867     setVisible : function(v, a, d, c, e){
23868         if(v){
23869             this.showAction();
23870         }
23871         if(a && v){
23872             var cb = function(){
23873                 this.sync(true);
23874                 if(c){
23875                     c();
23876                 }
23877             }.createDelegate(this);
23878             supr.setVisible.call(this, true, true, d, cb, e);
23879         }else{
23880             if(!v){
23881                 this.hideUnders(true);
23882             }
23883             var cb = c;
23884             if(a){
23885                 cb = function(){
23886                     this.hideAction();
23887                     if(c){
23888                         c();
23889                     }
23890                 }.createDelegate(this);
23891             }
23892             supr.setVisible.call(this, v, a, d, cb, e);
23893             if(v){
23894                 this.sync(true);
23895             }else if(!a){
23896                 this.hideAction();
23897             }
23898         }
23899     },
23900
23901     storeXY : function(xy){
23902         delete this.lastLT;
23903         this.lastXY = xy;
23904     },
23905
23906     storeLeftTop : function(left, top){
23907         delete this.lastXY;
23908         this.lastLT = [left, top];
23909     },
23910
23911     // private
23912     beforeFx : function(){
23913         this.beforeAction();
23914         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23915     },
23916
23917     // private
23918     afterFx : function(){
23919         Roo.Layer.superclass.afterFx.apply(this, arguments);
23920         this.sync(this.isVisible());
23921     },
23922
23923     // private
23924     beforeAction : function(){
23925         if(!this.updating && this.shadow){
23926             this.shadow.hide();
23927         }
23928     },
23929
23930     // overridden Element method
23931     setLeft : function(left){
23932         this.storeLeftTop(left, this.getTop(true));
23933         supr.setLeft.apply(this, arguments);
23934         this.sync();
23935     },
23936
23937     setTop : function(top){
23938         this.storeLeftTop(this.getLeft(true), top);
23939         supr.setTop.apply(this, arguments);
23940         this.sync();
23941     },
23942
23943     setLeftTop : function(left, top){
23944         this.storeLeftTop(left, top);
23945         supr.setLeftTop.apply(this, arguments);
23946         this.sync();
23947     },
23948
23949     setXY : function(xy, a, d, c, e){
23950         this.fixDisplay();
23951         this.beforeAction();
23952         this.storeXY(xy);
23953         var cb = this.createCB(c);
23954         supr.setXY.call(this, xy, a, d, cb, e);
23955         if(!a){
23956             cb();
23957         }
23958     },
23959
23960     // private
23961     createCB : function(c){
23962         var el = this;
23963         return function(){
23964             el.constrainXY();
23965             el.sync(true);
23966             if(c){
23967                 c();
23968             }
23969         };
23970     },
23971
23972     // overridden Element method
23973     setX : function(x, a, d, c, e){
23974         this.setXY([x, this.getY()], a, d, c, e);
23975     },
23976
23977     // overridden Element method
23978     setY : function(y, a, d, c, e){
23979         this.setXY([this.getX(), y], a, d, c, e);
23980     },
23981
23982     // overridden Element method
23983     setSize : function(w, h, a, d, c, e){
23984         this.beforeAction();
23985         var cb = this.createCB(c);
23986         supr.setSize.call(this, w, h, a, d, cb, e);
23987         if(!a){
23988             cb();
23989         }
23990     },
23991
23992     // overridden Element method
23993     setWidth : function(w, a, d, c, e){
23994         this.beforeAction();
23995         var cb = this.createCB(c);
23996         supr.setWidth.call(this, w, a, d, cb, e);
23997         if(!a){
23998             cb();
23999         }
24000     },
24001
24002     // overridden Element method
24003     setHeight : function(h, a, d, c, e){
24004         this.beforeAction();
24005         var cb = this.createCB(c);
24006         supr.setHeight.call(this, h, a, d, cb, e);
24007         if(!a){
24008             cb();
24009         }
24010     },
24011
24012     // overridden Element method
24013     setBounds : function(x, y, w, h, a, d, c, e){
24014         this.beforeAction();
24015         var cb = this.createCB(c);
24016         if(!a){
24017             this.storeXY([x, y]);
24018             supr.setXY.call(this, [x, y]);
24019             supr.setSize.call(this, w, h, a, d, cb, e);
24020             cb();
24021         }else{
24022             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24023         }
24024         return this;
24025     },
24026     
24027     /**
24028      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24029      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24030      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24031      * @param {Number} zindex The new z-index to set
24032      * @return {this} The Layer
24033      */
24034     setZIndex : function(zindex){
24035         this.zindex = zindex;
24036         this.setStyle("z-index", zindex + 2);
24037         if(this.shadow){
24038             this.shadow.setZIndex(zindex + 1);
24039         }
24040         if(this.shim){
24041             this.shim.setStyle("z-index", zindex);
24042         }
24043     }
24044 });
24045 })();/*
24046  * Based on:
24047  * Ext JS Library 1.1.1
24048  * Copyright(c) 2006-2007, Ext JS, LLC.
24049  *
24050  * Originally Released Under LGPL - original licence link has changed is not relivant.
24051  *
24052  * Fork - LGPL
24053  * <script type="text/javascript">
24054  */
24055
24056
24057 /**
24058  * @class Roo.Shadow
24059  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24060  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24061  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24062  * @constructor
24063  * Create a new Shadow
24064  * @param {Object} config The config object
24065  */
24066 Roo.Shadow = function(config){
24067     Roo.apply(this, config);
24068     if(typeof this.mode != "string"){
24069         this.mode = this.defaultMode;
24070     }
24071     var o = this.offset, a = {h: 0};
24072     var rad = Math.floor(this.offset/2);
24073     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24074         case "drop":
24075             a.w = 0;
24076             a.l = a.t = o;
24077             a.t -= 1;
24078             if(Roo.isIE){
24079                 a.l -= this.offset + rad;
24080                 a.t -= this.offset + rad;
24081                 a.w -= rad;
24082                 a.h -= rad;
24083                 a.t += 1;
24084             }
24085         break;
24086         case "sides":
24087             a.w = (o*2);
24088             a.l = -o;
24089             a.t = o-1;
24090             if(Roo.isIE){
24091                 a.l -= (this.offset - rad);
24092                 a.t -= this.offset + rad;
24093                 a.l += 1;
24094                 a.w -= (this.offset - rad)*2;
24095                 a.w -= rad + 1;
24096                 a.h -= 1;
24097             }
24098         break;
24099         case "frame":
24100             a.w = a.h = (o*2);
24101             a.l = a.t = -o;
24102             a.t += 1;
24103             a.h -= 2;
24104             if(Roo.isIE){
24105                 a.l -= (this.offset - rad);
24106                 a.t -= (this.offset - rad);
24107                 a.l += 1;
24108                 a.w -= (this.offset + rad + 1);
24109                 a.h -= (this.offset + rad);
24110                 a.h += 1;
24111             }
24112         break;
24113     };
24114
24115     this.adjusts = a;
24116 };
24117
24118 Roo.Shadow.prototype = {
24119     /**
24120      * @cfg {String} mode
24121      * The shadow display mode.  Supports the following options:<br />
24122      * sides: Shadow displays on both sides and bottom only<br />
24123      * frame: Shadow displays equally on all four sides<br />
24124      * drop: Traditional bottom-right drop shadow (default)
24125      */
24126     /**
24127      * @cfg {String} offset
24128      * The number of pixels to offset the shadow from the element (defaults to 4)
24129      */
24130     offset: 4,
24131
24132     // private
24133     defaultMode: "drop",
24134
24135     /**
24136      * Displays the shadow under the target element
24137      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24138      */
24139     show : function(target){
24140         target = Roo.get(target);
24141         if(!this.el){
24142             this.el = Roo.Shadow.Pool.pull();
24143             if(this.el.dom.nextSibling != target.dom){
24144                 this.el.insertBefore(target);
24145             }
24146         }
24147         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24148         if(Roo.isIE){
24149             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24150         }
24151         this.realign(
24152             target.getLeft(true),
24153             target.getTop(true),
24154             target.getWidth(),
24155             target.getHeight()
24156         );
24157         this.el.dom.style.display = "block";
24158     },
24159
24160     /**
24161      * Returns true if the shadow is visible, else false
24162      */
24163     isVisible : function(){
24164         return this.el ? true : false;  
24165     },
24166
24167     /**
24168      * Direct alignment when values are already available. Show must be called at least once before
24169      * calling this method to ensure it is initialized.
24170      * @param {Number} left The target element left position
24171      * @param {Number} top The target element top position
24172      * @param {Number} width The target element width
24173      * @param {Number} height The target element height
24174      */
24175     realign : function(l, t, w, h){
24176         if(!this.el){
24177             return;
24178         }
24179         var a = this.adjusts, d = this.el.dom, s = d.style;
24180         var iea = 0;
24181         s.left = (l+a.l)+"px";
24182         s.top = (t+a.t)+"px";
24183         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24184  
24185         if(s.width != sws || s.height != shs){
24186             s.width = sws;
24187             s.height = shs;
24188             if(!Roo.isIE){
24189                 var cn = d.childNodes;
24190                 var sww = Math.max(0, (sw-12))+"px";
24191                 cn[0].childNodes[1].style.width = sww;
24192                 cn[1].childNodes[1].style.width = sww;
24193                 cn[2].childNodes[1].style.width = sww;
24194                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24195             }
24196         }
24197     },
24198
24199     /**
24200      * Hides this shadow
24201      */
24202     hide : function(){
24203         if(this.el){
24204             this.el.dom.style.display = "none";
24205             Roo.Shadow.Pool.push(this.el);
24206             delete this.el;
24207         }
24208     },
24209
24210     /**
24211      * Adjust the z-index of this shadow
24212      * @param {Number} zindex The new z-index
24213      */
24214     setZIndex : function(z){
24215         this.zIndex = z;
24216         if(this.el){
24217             this.el.setStyle("z-index", z);
24218         }
24219     }
24220 };
24221
24222 // Private utility class that manages the internal Shadow cache
24223 Roo.Shadow.Pool = function(){
24224     var p = [];
24225     var markup = Roo.isIE ?
24226                  '<div class="x-ie-shadow"></div>' :
24227                  '<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>';
24228     return {
24229         pull : function(){
24230             var sh = p.shift();
24231             if(!sh){
24232                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24233                 sh.autoBoxAdjust = false;
24234             }
24235             return sh;
24236         },
24237
24238         push : function(sh){
24239             p.push(sh);
24240         }
24241     };
24242 }();/*
24243  * Based on:
24244  * Ext JS Library 1.1.1
24245  * Copyright(c) 2006-2007, Ext JS, LLC.
24246  *
24247  * Originally Released Under LGPL - original licence link has changed is not relivant.
24248  *
24249  * Fork - LGPL
24250  * <script type="text/javascript">
24251  */
24252
24253
24254 /**
24255  * @class Roo.SplitBar
24256  * @extends Roo.util.Observable
24257  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24258  * <br><br>
24259  * Usage:
24260  * <pre><code>
24261 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24262                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24263 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24264 split.minSize = 100;
24265 split.maxSize = 600;
24266 split.animate = true;
24267 split.on('moved', splitterMoved);
24268 </code></pre>
24269  * @constructor
24270  * Create a new SplitBar
24271  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24272  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24273  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24274  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24275                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24276                         position of the SplitBar).
24277  */
24278 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24279     
24280     /** @private */
24281     this.el = Roo.get(dragElement, true);
24282     this.el.dom.unselectable = "on";
24283     /** @private */
24284     this.resizingEl = Roo.get(resizingElement, true);
24285
24286     /**
24287      * @private
24288      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24289      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24290      * @type Number
24291      */
24292     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24293     
24294     /**
24295      * The minimum size of the resizing element. (Defaults to 0)
24296      * @type Number
24297      */
24298     this.minSize = 0;
24299     
24300     /**
24301      * The maximum size of the resizing element. (Defaults to 2000)
24302      * @type Number
24303      */
24304     this.maxSize = 2000;
24305     
24306     /**
24307      * Whether to animate the transition to the new size
24308      * @type Boolean
24309      */
24310     this.animate = false;
24311     
24312     /**
24313      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24314      * @type Boolean
24315      */
24316     this.useShim = false;
24317     
24318     /** @private */
24319     this.shim = null;
24320     
24321     if(!existingProxy){
24322         /** @private */
24323         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24324     }else{
24325         this.proxy = Roo.get(existingProxy).dom;
24326     }
24327     /** @private */
24328     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24329     
24330     /** @private */
24331     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24332     
24333     /** @private */
24334     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24335     
24336     /** @private */
24337     this.dragSpecs = {};
24338     
24339     /**
24340      * @private The adapter to use to positon and resize elements
24341      */
24342     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24343     this.adapter.init(this);
24344     
24345     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24346         /** @private */
24347         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24348         this.el.addClass("x-splitbar-h");
24349     }else{
24350         /** @private */
24351         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24352         this.el.addClass("x-splitbar-v");
24353     }
24354     
24355     this.addEvents({
24356         /**
24357          * @event resize
24358          * Fires when the splitter is moved (alias for {@link #event-moved})
24359          * @param {Roo.SplitBar} this
24360          * @param {Number} newSize the new width or height
24361          */
24362         "resize" : true,
24363         /**
24364          * @event moved
24365          * Fires when the splitter is moved
24366          * @param {Roo.SplitBar} this
24367          * @param {Number} newSize the new width or height
24368          */
24369         "moved" : true,
24370         /**
24371          * @event beforeresize
24372          * Fires before the splitter is dragged
24373          * @param {Roo.SplitBar} this
24374          */
24375         "beforeresize" : true,
24376
24377         "beforeapply" : true
24378     });
24379
24380     Roo.util.Observable.call(this);
24381 };
24382
24383 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24384     onStartProxyDrag : function(x, y){
24385         this.fireEvent("beforeresize", this);
24386         if(!this.overlay){
24387             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24388             o.unselectable();
24389             o.enableDisplayMode("block");
24390             // all splitbars share the same overlay
24391             Roo.SplitBar.prototype.overlay = o;
24392         }
24393         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24394         this.overlay.show();
24395         Roo.get(this.proxy).setDisplayed("block");
24396         var size = this.adapter.getElementSize(this);
24397         this.activeMinSize = this.getMinimumSize();;
24398         this.activeMaxSize = this.getMaximumSize();;
24399         var c1 = size - this.activeMinSize;
24400         var c2 = Math.max(this.activeMaxSize - size, 0);
24401         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24402             this.dd.resetConstraints();
24403             this.dd.setXConstraint(
24404                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24405                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24406             );
24407             this.dd.setYConstraint(0, 0);
24408         }else{
24409             this.dd.resetConstraints();
24410             this.dd.setXConstraint(0, 0);
24411             this.dd.setYConstraint(
24412                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24413                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24414             );
24415          }
24416         this.dragSpecs.startSize = size;
24417         this.dragSpecs.startPoint = [x, y];
24418         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24419     },
24420     
24421     /** 
24422      * @private Called after the drag operation by the DDProxy
24423      */
24424     onEndProxyDrag : function(e){
24425         Roo.get(this.proxy).setDisplayed(false);
24426         var endPoint = Roo.lib.Event.getXY(e);
24427         if(this.overlay){
24428             this.overlay.hide();
24429         }
24430         var newSize;
24431         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24432             newSize = this.dragSpecs.startSize + 
24433                 (this.placement == Roo.SplitBar.LEFT ?
24434                     endPoint[0] - this.dragSpecs.startPoint[0] :
24435                     this.dragSpecs.startPoint[0] - endPoint[0]
24436                 );
24437         }else{
24438             newSize = this.dragSpecs.startSize + 
24439                 (this.placement == Roo.SplitBar.TOP ?
24440                     endPoint[1] - this.dragSpecs.startPoint[1] :
24441                     this.dragSpecs.startPoint[1] - endPoint[1]
24442                 );
24443         }
24444         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24445         if(newSize != this.dragSpecs.startSize){
24446             if(this.fireEvent('beforeapply', this, newSize) !== false){
24447                 this.adapter.setElementSize(this, newSize);
24448                 this.fireEvent("moved", this, newSize);
24449                 this.fireEvent("resize", this, newSize);
24450             }
24451         }
24452     },
24453     
24454     /**
24455      * Get the adapter this SplitBar uses
24456      * @return The adapter object
24457      */
24458     getAdapter : function(){
24459         return this.adapter;
24460     },
24461     
24462     /**
24463      * Set the adapter this SplitBar uses
24464      * @param {Object} adapter A SplitBar adapter object
24465      */
24466     setAdapter : function(adapter){
24467         this.adapter = adapter;
24468         this.adapter.init(this);
24469     },
24470     
24471     /**
24472      * Gets the minimum size for the resizing element
24473      * @return {Number} The minimum size
24474      */
24475     getMinimumSize : function(){
24476         return this.minSize;
24477     },
24478     
24479     /**
24480      * Sets the minimum size for the resizing element
24481      * @param {Number} minSize The minimum size
24482      */
24483     setMinimumSize : function(minSize){
24484         this.minSize = minSize;
24485     },
24486     
24487     /**
24488      * Gets the maximum size for the resizing element
24489      * @return {Number} The maximum size
24490      */
24491     getMaximumSize : function(){
24492         return this.maxSize;
24493     },
24494     
24495     /**
24496      * Sets the maximum size for the resizing element
24497      * @param {Number} maxSize The maximum size
24498      */
24499     setMaximumSize : function(maxSize){
24500         this.maxSize = maxSize;
24501     },
24502     
24503     /**
24504      * Sets the initialize size for the resizing element
24505      * @param {Number} size The initial size
24506      */
24507     setCurrentSize : function(size){
24508         var oldAnimate = this.animate;
24509         this.animate = false;
24510         this.adapter.setElementSize(this, size);
24511         this.animate = oldAnimate;
24512     },
24513     
24514     /**
24515      * Destroy this splitbar. 
24516      * @param {Boolean} removeEl True to remove the element
24517      */
24518     destroy : function(removeEl){
24519         if(this.shim){
24520             this.shim.remove();
24521         }
24522         this.dd.unreg();
24523         this.proxy.parentNode.removeChild(this.proxy);
24524         if(removeEl){
24525             this.el.remove();
24526         }
24527     }
24528 });
24529
24530 /**
24531  * @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.
24532  */
24533 Roo.SplitBar.createProxy = function(dir){
24534     var proxy = new Roo.Element(document.createElement("div"));
24535     proxy.unselectable();
24536     var cls = 'x-splitbar-proxy';
24537     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24538     document.body.appendChild(proxy.dom);
24539     return proxy.dom;
24540 };
24541
24542 /** 
24543  * @class Roo.SplitBar.BasicLayoutAdapter
24544  * Default Adapter. It assumes the splitter and resizing element are not positioned
24545  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24546  */
24547 Roo.SplitBar.BasicLayoutAdapter = function(){
24548 };
24549
24550 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24551     // do nothing for now
24552     init : function(s){
24553     
24554     },
24555     /**
24556      * Called before drag operations to get the current size of the resizing element. 
24557      * @param {Roo.SplitBar} s The SplitBar using this adapter
24558      */
24559      getElementSize : function(s){
24560         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24561             return s.resizingEl.getWidth();
24562         }else{
24563             return s.resizingEl.getHeight();
24564         }
24565     },
24566     
24567     /**
24568      * Called after drag operations to set the size of the resizing element.
24569      * @param {Roo.SplitBar} s The SplitBar using this adapter
24570      * @param {Number} newSize The new size to set
24571      * @param {Function} onComplete A function to be invoked when resizing is complete
24572      */
24573     setElementSize : function(s, newSize, onComplete){
24574         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24575             if(!s.animate){
24576                 s.resizingEl.setWidth(newSize);
24577                 if(onComplete){
24578                     onComplete(s, newSize);
24579                 }
24580             }else{
24581                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24582             }
24583         }else{
24584             
24585             if(!s.animate){
24586                 s.resizingEl.setHeight(newSize);
24587                 if(onComplete){
24588                     onComplete(s, newSize);
24589                 }
24590             }else{
24591                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24592             }
24593         }
24594     }
24595 };
24596
24597 /** 
24598  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24599  * @extends Roo.SplitBar.BasicLayoutAdapter
24600  * Adapter that  moves the splitter element to align with the resized sizing element. 
24601  * Used with an absolute positioned SplitBar.
24602  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24603  * document.body, make sure you assign an id to the body element.
24604  */
24605 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24606     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24607     this.container = Roo.get(container);
24608 };
24609
24610 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24611     init : function(s){
24612         this.basic.init(s);
24613     },
24614     
24615     getElementSize : function(s){
24616         return this.basic.getElementSize(s);
24617     },
24618     
24619     setElementSize : function(s, newSize, onComplete){
24620         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24621     },
24622     
24623     moveSplitter : function(s){
24624         var yes = Roo.SplitBar;
24625         switch(s.placement){
24626             case yes.LEFT:
24627                 s.el.setX(s.resizingEl.getRight());
24628                 break;
24629             case yes.RIGHT:
24630                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24631                 break;
24632             case yes.TOP:
24633                 s.el.setY(s.resizingEl.getBottom());
24634                 break;
24635             case yes.BOTTOM:
24636                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24637                 break;
24638         }
24639     }
24640 };
24641
24642 /**
24643  * Orientation constant - Create a vertical SplitBar
24644  * @static
24645  * @type Number
24646  */
24647 Roo.SplitBar.VERTICAL = 1;
24648
24649 /**
24650  * Orientation constant - Create a horizontal SplitBar
24651  * @static
24652  * @type Number
24653  */
24654 Roo.SplitBar.HORIZONTAL = 2;
24655
24656 /**
24657  * Placement constant - The resizing element is to the left of the splitter element
24658  * @static
24659  * @type Number
24660  */
24661 Roo.SplitBar.LEFT = 1;
24662
24663 /**
24664  * Placement constant - The resizing element is to the right of the splitter element
24665  * @static
24666  * @type Number
24667  */
24668 Roo.SplitBar.RIGHT = 2;
24669
24670 /**
24671  * Placement constant - The resizing element is positioned above the splitter element
24672  * @static
24673  * @type Number
24674  */
24675 Roo.SplitBar.TOP = 3;
24676
24677 /**
24678  * Placement constant - The resizing element is positioned under splitter element
24679  * @static
24680  * @type Number
24681  */
24682 Roo.SplitBar.BOTTOM = 4;
24683 /*
24684  * Based on:
24685  * Ext JS Library 1.1.1
24686  * Copyright(c) 2006-2007, Ext JS, LLC.
24687  *
24688  * Originally Released Under LGPL - original licence link has changed is not relivant.
24689  *
24690  * Fork - LGPL
24691  * <script type="text/javascript">
24692  */
24693
24694 /**
24695  * @class Roo.View
24696  * @extends Roo.util.Observable
24697  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24698  * This class also supports single and multi selection modes. <br>
24699  * Create a data model bound view:
24700  <pre><code>
24701  var store = new Roo.data.Store(...);
24702
24703  var view = new Roo.View({
24704     el : "my-element",
24705     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24706  
24707     singleSelect: true,
24708     selectedClass: "ydataview-selected",
24709     store: store
24710  });
24711
24712  // listen for node click?
24713  view.on("click", function(vw, index, node, e){
24714  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24715  });
24716
24717  // load XML data
24718  dataModel.load("foobar.xml");
24719  </code></pre>
24720  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24721  * <br><br>
24722  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24723  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24724  * 
24725  * Note: old style constructor is still suported (container, template, config)
24726  * 
24727  * @constructor
24728  * Create a new View
24729  * @param {Object} config The config object
24730  * 
24731  */
24732 Roo.View = function(config, depreciated_tpl, depreciated_config){
24733     
24734     if (typeof(depreciated_tpl) == 'undefined') {
24735         // new way.. - universal constructor.
24736         Roo.apply(this, config);
24737         this.el  = Roo.get(this.el);
24738     } else {
24739         // old format..
24740         this.el  = Roo.get(config);
24741         this.tpl = depreciated_tpl;
24742         Roo.apply(this, depreciated_config);
24743     }
24744     this.wrapEl  = this.el.wrap().wrap();
24745     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24746     
24747     
24748     if(typeof(this.tpl) == "string"){
24749         this.tpl = new Roo.Template(this.tpl);
24750     } else {
24751         // support xtype ctors..
24752         this.tpl = new Roo.factory(this.tpl, Roo);
24753     }
24754     
24755     
24756     this.tpl.compile();
24757    
24758   
24759     
24760      
24761     /** @private */
24762     this.addEvents({
24763         /**
24764          * @event beforeclick
24765          * Fires before a click is processed. Returns false to cancel the default action.
24766          * @param {Roo.View} this
24767          * @param {Number} index The index of the target node
24768          * @param {HTMLElement} node The target node
24769          * @param {Roo.EventObject} e The raw event object
24770          */
24771             "beforeclick" : true,
24772         /**
24773          * @event click
24774          * Fires when a template node is clicked.
24775          * @param {Roo.View} this
24776          * @param {Number} index The index of the target node
24777          * @param {HTMLElement} node The target node
24778          * @param {Roo.EventObject} e The raw event object
24779          */
24780             "click" : true,
24781         /**
24782          * @event dblclick
24783          * Fires when a template node is double clicked.
24784          * @param {Roo.View} this
24785          * @param {Number} index The index of the target node
24786          * @param {HTMLElement} node The target node
24787          * @param {Roo.EventObject} e The raw event object
24788          */
24789             "dblclick" : true,
24790         /**
24791          * @event contextmenu
24792          * Fires when a template node is right clicked.
24793          * @param {Roo.View} this
24794          * @param {Number} index The index of the target node
24795          * @param {HTMLElement} node The target node
24796          * @param {Roo.EventObject} e The raw event object
24797          */
24798             "contextmenu" : true,
24799         /**
24800          * @event selectionchange
24801          * Fires when the selected nodes change.
24802          * @param {Roo.View} this
24803          * @param {Array} selections Array of the selected nodes
24804          */
24805             "selectionchange" : true,
24806     
24807         /**
24808          * @event beforeselect
24809          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24810          * @param {Roo.View} this
24811          * @param {HTMLElement} node The node to be selected
24812          * @param {Array} selections Array of currently selected nodes
24813          */
24814             "beforeselect" : true,
24815         /**
24816          * @event preparedata
24817          * Fires on every row to render, to allow you to change the data.
24818          * @param {Roo.View} this
24819          * @param {Object} data to be rendered (change this)
24820          */
24821           "preparedata" : true
24822           
24823           
24824         });
24825
24826
24827
24828     this.el.on({
24829         "click": this.onClick,
24830         "dblclick": this.onDblClick,
24831         "contextmenu": this.onContextMenu,
24832         scope:this
24833     });
24834
24835     this.selections = [];
24836     this.nodes = [];
24837     this.cmp = new Roo.CompositeElementLite([]);
24838     if(this.store){
24839         this.store = Roo.factory(this.store, Roo.data);
24840         this.setStore(this.store, true);
24841     }
24842     
24843     if ( this.footer && this.footer.xtype) {
24844            
24845          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24846         
24847         this.footer.dataSource = this.store
24848         this.footer.container = fctr;
24849         this.footer = Roo.factory(this.footer, Roo);
24850         fctr.insertFirst(this.el);
24851         
24852         // this is a bit insane - as the paging toolbar seems to detach the el..
24853 //        dom.parentNode.parentNode.parentNode
24854          // they get detached?
24855     }
24856     
24857     
24858     Roo.View.superclass.constructor.call(this);
24859     
24860     
24861 };
24862
24863 Roo.extend(Roo.View, Roo.util.Observable, {
24864     
24865      /**
24866      * @cfg {Roo.data.Store} store Data store to load data from.
24867      */
24868     store : false,
24869     
24870     /**
24871      * @cfg {String|Roo.Element} el The container element.
24872      */
24873     el : '',
24874     
24875     /**
24876      * @cfg {String|Roo.Template} tpl The template used by this View 
24877      */
24878     tpl : false,
24879     /**
24880      * @cfg {String} dataName the named area of the template to use as the data area
24881      *                          Works with domtemplates roo-name="name"
24882      */
24883     dataName: false,
24884     /**
24885      * @cfg {String} selectedClass The css class to add to selected nodes
24886      */
24887     selectedClass : "x-view-selected",
24888      /**
24889      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24890      */
24891     emptyText : "",
24892     
24893     /**
24894      * @cfg {String} text to display on mask (default Loading)
24895      */
24896     mask : false,
24897     /**
24898      * @cfg {Boolean} multiSelect Allow multiple selection
24899      */
24900     multiSelect : false,
24901     /**
24902      * @cfg {Boolean} singleSelect Allow single selection
24903      */
24904     singleSelect:  false,
24905     
24906     /**
24907      * @cfg {Boolean} toggleSelect - selecting 
24908      */
24909     toggleSelect : false,
24910     
24911     /**
24912      * Returns the element this view is bound to.
24913      * @return {Roo.Element}
24914      */
24915     getEl : function(){
24916         return this.wrapEl;
24917     },
24918     
24919     
24920
24921     /**
24922      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24923      */
24924     refresh : function(){
24925         var t = this.tpl;
24926         
24927         // if we are using something like 'domtemplate', then
24928         // the what gets used is:
24929         // t.applySubtemplate(NAME, data, wrapping data..)
24930         // the outer template then get' applied with
24931         //     the store 'extra data'
24932         // and the body get's added to the
24933         //      roo-name="data" node?
24934         //      <span class='roo-tpl-{name}'></span> ?????
24935         
24936         
24937         
24938         this.clearSelections();
24939         this.el.update("");
24940         var html = [];
24941         var records = this.store.getRange();
24942         if(records.length < 1) {
24943             
24944             // is this valid??  = should it render a template??
24945             
24946             this.el.update(this.emptyText);
24947             return;
24948         }
24949         var el = this.el;
24950         if (this.dataName) {
24951             this.el.update(t.apply(this.store.meta)); //????
24952             el = this.el.child('.roo-tpl-' + this.dataName);
24953         }
24954         
24955         for(var i = 0, len = records.length; i < len; i++){
24956             var data = this.prepareData(records[i].data, i, records[i]);
24957             this.fireEvent("preparedata", this, data, i, records[i]);
24958             html[html.length] = Roo.util.Format.trim(
24959                 this.dataName ?
24960                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24961                     t.apply(data)
24962             );
24963         }
24964         
24965         
24966         
24967         el.update(html.join(""));
24968         this.nodes = el.dom.childNodes;
24969         this.updateIndexes(0);
24970     },
24971
24972     /**
24973      * Function to override to reformat the data that is sent to
24974      * the template for each node.
24975      * DEPRICATED - use the preparedata event handler.
24976      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24977      * a JSON object for an UpdateManager bound view).
24978      */
24979     prepareData : function(data, index, record)
24980     {
24981         this.fireEvent("preparedata", this, data, index, record);
24982         return data;
24983     },
24984
24985     onUpdate : function(ds, record){
24986         this.clearSelections();
24987         var index = this.store.indexOf(record);
24988         var n = this.nodes[index];
24989         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24990         n.parentNode.removeChild(n);
24991         this.updateIndexes(index, index);
24992     },
24993
24994     
24995     
24996 // --------- FIXME     
24997     onAdd : function(ds, records, index)
24998     {
24999         this.clearSelections();
25000         if(this.nodes.length == 0){
25001             this.refresh();
25002             return;
25003         }
25004         var n = this.nodes[index];
25005         for(var i = 0, len = records.length; i < len; i++){
25006             var d = this.prepareData(records[i].data, i, records[i]);
25007             if(n){
25008                 this.tpl.insertBefore(n, d);
25009             }else{
25010                 
25011                 this.tpl.append(this.el, d);
25012             }
25013         }
25014         this.updateIndexes(index);
25015     },
25016
25017     onRemove : function(ds, record, index){
25018         this.clearSelections();
25019         var el = this.dataName  ?
25020             this.el.child('.roo-tpl-' + this.dataName) :
25021             this.el; 
25022         el.dom.removeChild(this.nodes[index]);
25023         this.updateIndexes(index);
25024     },
25025
25026     /**
25027      * Refresh an individual node.
25028      * @param {Number} index
25029      */
25030     refreshNode : function(index){
25031         this.onUpdate(this.store, this.store.getAt(index));
25032     },
25033
25034     updateIndexes : function(startIndex, endIndex){
25035         var ns = this.nodes;
25036         startIndex = startIndex || 0;
25037         endIndex = endIndex || ns.length - 1;
25038         for(var i = startIndex; i <= endIndex; i++){
25039             ns[i].nodeIndex = i;
25040         }
25041     },
25042
25043     /**
25044      * Changes the data store this view uses and refresh the view.
25045      * @param {Store} store
25046      */
25047     setStore : function(store, initial){
25048         if(!initial && this.store){
25049             this.store.un("datachanged", this.refresh);
25050             this.store.un("add", this.onAdd);
25051             this.store.un("remove", this.onRemove);
25052             this.store.un("update", this.onUpdate);
25053             this.store.un("clear", this.refresh);
25054             this.store.un("beforeload", this.onBeforeLoad);
25055             this.store.un("load", this.onLoad);
25056             this.store.un("loadexception", this.onLoad);
25057         }
25058         if(store){
25059           
25060             store.on("datachanged", this.refresh, this);
25061             store.on("add", this.onAdd, this);
25062             store.on("remove", this.onRemove, this);
25063             store.on("update", this.onUpdate, this);
25064             store.on("clear", this.refresh, this);
25065             store.on("beforeload", this.onBeforeLoad, this);
25066             store.on("load", this.onLoad, this);
25067             store.on("loadexception", this.onLoad, this);
25068         }
25069         
25070         if(store){
25071             this.refresh();
25072         }
25073     },
25074     /**
25075      * onbeforeLoad - masks the loading area.
25076      *
25077      */
25078     onBeforeLoad : function()
25079     {
25080         this.el.update("");
25081         this.el.mask(this.mask ? this.mask : "Loading" ); 
25082     },
25083     onLoad : function ()
25084     {
25085         this.el.unmask();
25086     },
25087     
25088
25089     /**
25090      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25091      * @param {HTMLElement} node
25092      * @return {HTMLElement} The template node
25093      */
25094     findItemFromChild : function(node){
25095         var el = this.dataName  ?
25096             this.el.child('.roo-tpl-' + this.dataName,true) :
25097             this.el.dom; 
25098         
25099         if(!node || node.parentNode == el){
25100                     return node;
25101             }
25102             var p = node.parentNode;
25103             while(p && p != el){
25104             if(p.parentNode == el){
25105                 return p;
25106             }
25107             p = p.parentNode;
25108         }
25109             return null;
25110     },
25111
25112     /** @ignore */
25113     onClick : function(e){
25114         var item = this.findItemFromChild(e.getTarget());
25115         if(item){
25116             var index = this.indexOf(item);
25117             if(this.onItemClick(item, index, e) !== false){
25118                 this.fireEvent("click", this, index, item, e);
25119             }
25120         }else{
25121             this.clearSelections();
25122         }
25123     },
25124
25125     /** @ignore */
25126     onContextMenu : function(e){
25127         var item = this.findItemFromChild(e.getTarget());
25128         if(item){
25129             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25130         }
25131     },
25132
25133     /** @ignore */
25134     onDblClick : function(e){
25135         var item = this.findItemFromChild(e.getTarget());
25136         if(item){
25137             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25138         }
25139     },
25140
25141     onItemClick : function(item, index, e)
25142     {
25143         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25144             return false;
25145         }
25146         if (this.toggleSelect) {
25147             var m = this.isSelected(item) ? 'unselect' : 'select';
25148             Roo.log(m);
25149             var _t = this;
25150             _t[m](item, true, false);
25151             return true;
25152         }
25153         if(this.multiSelect || this.singleSelect){
25154             if(this.multiSelect && e.shiftKey && this.lastSelection){
25155                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25156             }else{
25157                 this.select(item, this.multiSelect && e.ctrlKey);
25158                 this.lastSelection = item;
25159             }
25160             e.preventDefault();
25161         }
25162         return true;
25163     },
25164
25165     /**
25166      * Get the number of selected nodes.
25167      * @return {Number}
25168      */
25169     getSelectionCount : function(){
25170         return this.selections.length;
25171     },
25172
25173     /**
25174      * Get the currently selected nodes.
25175      * @return {Array} An array of HTMLElements
25176      */
25177     getSelectedNodes : function(){
25178         return this.selections;
25179     },
25180
25181     /**
25182      * Get the indexes of the selected nodes.
25183      * @return {Array}
25184      */
25185     getSelectedIndexes : function(){
25186         var indexes = [], s = this.selections;
25187         for(var i = 0, len = s.length; i < len; i++){
25188             indexes.push(s[i].nodeIndex);
25189         }
25190         return indexes;
25191     },
25192
25193     /**
25194      * Clear all selections
25195      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25196      */
25197     clearSelections : function(suppressEvent){
25198         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25199             this.cmp.elements = this.selections;
25200             this.cmp.removeClass(this.selectedClass);
25201             this.selections = [];
25202             if(!suppressEvent){
25203                 this.fireEvent("selectionchange", this, this.selections);
25204             }
25205         }
25206     },
25207
25208     /**
25209      * Returns true if the passed node is selected
25210      * @param {HTMLElement/Number} node The node or node index
25211      * @return {Boolean}
25212      */
25213     isSelected : function(node){
25214         var s = this.selections;
25215         if(s.length < 1){
25216             return false;
25217         }
25218         node = this.getNode(node);
25219         return s.indexOf(node) !== -1;
25220     },
25221
25222     /**
25223      * Selects nodes.
25224      * @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
25225      * @param {Boolean} keepExisting (optional) true to keep existing selections
25226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25227      */
25228     select : function(nodeInfo, keepExisting, suppressEvent){
25229         if(nodeInfo instanceof Array){
25230             if(!keepExisting){
25231                 this.clearSelections(true);
25232             }
25233             for(var i = 0, len = nodeInfo.length; i < len; i++){
25234                 this.select(nodeInfo[i], true, true);
25235             }
25236             return;
25237         } 
25238         var node = this.getNode(nodeInfo);
25239         if(!node || this.isSelected(node)){
25240             return; // already selected.
25241         }
25242         if(!keepExisting){
25243             this.clearSelections(true);
25244         }
25245         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25246             Roo.fly(node).addClass(this.selectedClass);
25247             this.selections.push(node);
25248             if(!suppressEvent){
25249                 this.fireEvent("selectionchange", this, this.selections);
25250             }
25251         }
25252         
25253         
25254     },
25255       /**
25256      * Unselects nodes.
25257      * @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
25258      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25259      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25260      */
25261     unselect : function(nodeInfo, keepExisting, suppressEvent)
25262     {
25263         if(nodeInfo instanceof Array){
25264             Roo.each(this.selections, function(s) {
25265                 this.unselect(s, nodeInfo);
25266             }, this);
25267             return;
25268         }
25269         var node = this.getNode(nodeInfo);
25270         if(!node || !this.isSelected(node)){
25271             Roo.log("not selected");
25272             return; // not selected.
25273         }
25274         // fireevent???
25275         var ns = [];
25276         Roo.each(this.selections, function(s) {
25277             if (s == node ) {
25278                 Roo.fly(node).removeClass(this.selectedClass);
25279
25280                 return;
25281             }
25282             ns.push(s);
25283         },this);
25284         
25285         this.selections= ns;
25286         this.fireEvent("selectionchange", this, this.selections);
25287     },
25288
25289     /**
25290      * Gets a template node.
25291      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25292      * @return {HTMLElement} The node or null if it wasn't found
25293      */
25294     getNode : function(nodeInfo){
25295         if(typeof nodeInfo == "string"){
25296             return document.getElementById(nodeInfo);
25297         }else if(typeof nodeInfo == "number"){
25298             return this.nodes[nodeInfo];
25299         }
25300         return nodeInfo;
25301     },
25302
25303     /**
25304      * Gets a range template nodes.
25305      * @param {Number} startIndex
25306      * @param {Number} endIndex
25307      * @return {Array} An array of nodes
25308      */
25309     getNodes : function(start, end){
25310         var ns = this.nodes;
25311         start = start || 0;
25312         end = typeof end == "undefined" ? ns.length - 1 : end;
25313         var nodes = [];
25314         if(start <= end){
25315             for(var i = start; i <= end; i++){
25316                 nodes.push(ns[i]);
25317             }
25318         } else{
25319             for(var i = start; i >= end; i--){
25320                 nodes.push(ns[i]);
25321             }
25322         }
25323         return nodes;
25324     },
25325
25326     /**
25327      * Finds the index of the passed node
25328      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25329      * @return {Number} The index of the node or -1
25330      */
25331     indexOf : function(node){
25332         node = this.getNode(node);
25333         if(typeof node.nodeIndex == "number"){
25334             return node.nodeIndex;
25335         }
25336         var ns = this.nodes;
25337         for(var i = 0, len = ns.length; i < len; i++){
25338             if(ns[i] == node){
25339                 return i;
25340             }
25341         }
25342         return -1;
25343     }
25344 });
25345 /*
25346  * Based on:
25347  * Ext JS Library 1.1.1
25348  * Copyright(c) 2006-2007, Ext JS, LLC.
25349  *
25350  * Originally Released Under LGPL - original licence link has changed is not relivant.
25351  *
25352  * Fork - LGPL
25353  * <script type="text/javascript">
25354  */
25355
25356 /**
25357  * @class Roo.JsonView
25358  * @extends Roo.View
25359  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25360 <pre><code>
25361 var view = new Roo.JsonView({
25362     container: "my-element",
25363     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25364     multiSelect: true, 
25365     jsonRoot: "data" 
25366 });
25367
25368 // listen for node click?
25369 view.on("click", function(vw, index, node, e){
25370     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25371 });
25372
25373 // direct load of JSON data
25374 view.load("foobar.php");
25375
25376 // Example from my blog list
25377 var tpl = new Roo.Template(
25378     '&lt;div class="entry"&gt;' +
25379     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25380     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25381     "&lt;/div&gt;&lt;hr /&gt;"
25382 );
25383
25384 var moreView = new Roo.JsonView({
25385     container :  "entry-list", 
25386     template : tpl,
25387     jsonRoot: "posts"
25388 });
25389 moreView.on("beforerender", this.sortEntries, this);
25390 moreView.load({
25391     url: "/blog/get-posts.php",
25392     params: "allposts=true",
25393     text: "Loading Blog Entries..."
25394 });
25395 </code></pre>
25396
25397 * Note: old code is supported with arguments : (container, template, config)
25398
25399
25400  * @constructor
25401  * Create a new JsonView
25402  * 
25403  * @param {Object} config The config object
25404  * 
25405  */
25406 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25407     
25408     
25409     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25410
25411     var um = this.el.getUpdateManager();
25412     um.setRenderer(this);
25413     um.on("update", this.onLoad, this);
25414     um.on("failure", this.onLoadException, this);
25415
25416     /**
25417      * @event beforerender
25418      * Fires before rendering of the downloaded JSON data.
25419      * @param {Roo.JsonView} this
25420      * @param {Object} data The JSON data loaded
25421      */
25422     /**
25423      * @event load
25424      * Fires when data is loaded.
25425      * @param {Roo.JsonView} this
25426      * @param {Object} data The JSON data loaded
25427      * @param {Object} response The raw Connect response object
25428      */
25429     /**
25430      * @event loadexception
25431      * Fires when loading fails.
25432      * @param {Roo.JsonView} this
25433      * @param {Object} response The raw Connect response object
25434      */
25435     this.addEvents({
25436         'beforerender' : true,
25437         'load' : true,
25438         'loadexception' : true
25439     });
25440 };
25441 Roo.extend(Roo.JsonView, Roo.View, {
25442     /**
25443      * @type {String} The root property in the loaded JSON object that contains the data
25444      */
25445     jsonRoot : "",
25446
25447     /**
25448      * Refreshes the view.
25449      */
25450     refresh : function(){
25451         this.clearSelections();
25452         this.el.update("");
25453         var html = [];
25454         var o = this.jsonData;
25455         if(o && o.length > 0){
25456             for(var i = 0, len = o.length; i < len; i++){
25457                 var data = this.prepareData(o[i], i, o);
25458                 html[html.length] = this.tpl.apply(data);
25459             }
25460         }else{
25461             html.push(this.emptyText);
25462         }
25463         this.el.update(html.join(""));
25464         this.nodes = this.el.dom.childNodes;
25465         this.updateIndexes(0);
25466     },
25467
25468     /**
25469      * 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.
25470      * @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:
25471      <pre><code>
25472      view.load({
25473          url: "your-url.php",
25474          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25475          callback: yourFunction,
25476          scope: yourObject, //(optional scope)
25477          discardUrl: false,
25478          nocache: false,
25479          text: "Loading...",
25480          timeout: 30,
25481          scripts: false
25482      });
25483      </code></pre>
25484      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25485      * 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.
25486      * @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}
25487      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25488      * @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.
25489      */
25490     load : function(){
25491         var um = this.el.getUpdateManager();
25492         um.update.apply(um, arguments);
25493     },
25494
25495     render : function(el, response){
25496         this.clearSelections();
25497         this.el.update("");
25498         var o;
25499         try{
25500             o = Roo.util.JSON.decode(response.responseText);
25501             if(this.jsonRoot){
25502                 
25503                 o = o[this.jsonRoot];
25504             }
25505         } catch(e){
25506         }
25507         /**
25508          * The current JSON data or null
25509          */
25510         this.jsonData = o;
25511         this.beforeRender();
25512         this.refresh();
25513     },
25514
25515 /**
25516  * Get the number of records in the current JSON dataset
25517  * @return {Number}
25518  */
25519     getCount : function(){
25520         return this.jsonData ? this.jsonData.length : 0;
25521     },
25522
25523 /**
25524  * Returns the JSON object for the specified node(s)
25525  * @param {HTMLElement/Array} node The node or an array of nodes
25526  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25527  * you get the JSON object for the node
25528  */
25529     getNodeData : function(node){
25530         if(node instanceof Array){
25531             var data = [];
25532             for(var i = 0, len = node.length; i < len; i++){
25533                 data.push(this.getNodeData(node[i]));
25534             }
25535             return data;
25536         }
25537         return this.jsonData[this.indexOf(node)] || null;
25538     },
25539
25540     beforeRender : function(){
25541         this.snapshot = this.jsonData;
25542         if(this.sortInfo){
25543             this.sort.apply(this, this.sortInfo);
25544         }
25545         this.fireEvent("beforerender", this, this.jsonData);
25546     },
25547
25548     onLoad : function(el, o){
25549         this.fireEvent("load", this, this.jsonData, o);
25550     },
25551
25552     onLoadException : function(el, o){
25553         this.fireEvent("loadexception", this, o);
25554     },
25555
25556 /**
25557  * Filter the data by a specific property.
25558  * @param {String} property A property on your JSON objects
25559  * @param {String/RegExp} value Either string that the property values
25560  * should start with, or a RegExp to test against the property
25561  */
25562     filter : function(property, value){
25563         if(this.jsonData){
25564             var data = [];
25565             var ss = this.snapshot;
25566             if(typeof value == "string"){
25567                 var vlen = value.length;
25568                 if(vlen == 0){
25569                     this.clearFilter();
25570                     return;
25571                 }
25572                 value = value.toLowerCase();
25573                 for(var i = 0, len = ss.length; i < len; i++){
25574                     var o = ss[i];
25575                     if(o[property].substr(0, vlen).toLowerCase() == value){
25576                         data.push(o);
25577                     }
25578                 }
25579             } else if(value.exec){ // regex?
25580                 for(var i = 0, len = ss.length; i < len; i++){
25581                     var o = ss[i];
25582                     if(value.test(o[property])){
25583                         data.push(o);
25584                     }
25585                 }
25586             } else{
25587                 return;
25588             }
25589             this.jsonData = data;
25590             this.refresh();
25591         }
25592     },
25593
25594 /**
25595  * Filter by a function. The passed function will be called with each
25596  * object in the current dataset. If the function returns true the value is kept,
25597  * otherwise it is filtered.
25598  * @param {Function} fn
25599  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25600  */
25601     filterBy : function(fn, scope){
25602         if(this.jsonData){
25603             var data = [];
25604             var ss = this.snapshot;
25605             for(var i = 0, len = ss.length; i < len; i++){
25606                 var o = ss[i];
25607                 if(fn.call(scope || this, o)){
25608                     data.push(o);
25609                 }
25610             }
25611             this.jsonData = data;
25612             this.refresh();
25613         }
25614     },
25615
25616 /**
25617  * Clears the current filter.
25618  */
25619     clearFilter : function(){
25620         if(this.snapshot && this.jsonData != this.snapshot){
25621             this.jsonData = this.snapshot;
25622             this.refresh();
25623         }
25624     },
25625
25626
25627 /**
25628  * Sorts the data for this view and refreshes it.
25629  * @param {String} property A property on your JSON objects to sort on
25630  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25631  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25632  */
25633     sort : function(property, dir, sortType){
25634         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25635         if(this.jsonData){
25636             var p = property;
25637             var dsc = dir && dir.toLowerCase() == "desc";
25638             var f = function(o1, o2){
25639                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25640                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25641                 ;
25642                 if(v1 < v2){
25643                     return dsc ? +1 : -1;
25644                 } else if(v1 > v2){
25645                     return dsc ? -1 : +1;
25646                 } else{
25647                     return 0;
25648                 }
25649             };
25650             this.jsonData.sort(f);
25651             this.refresh();
25652             if(this.jsonData != this.snapshot){
25653                 this.snapshot.sort(f);
25654             }
25655         }
25656     }
25657 });/*
25658  * Based on:
25659  * Ext JS Library 1.1.1
25660  * Copyright(c) 2006-2007, Ext JS, LLC.
25661  *
25662  * Originally Released Under LGPL - original licence link has changed is not relivant.
25663  *
25664  * Fork - LGPL
25665  * <script type="text/javascript">
25666  */
25667  
25668
25669 /**
25670  * @class Roo.ColorPalette
25671  * @extends Roo.Component
25672  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25673  * Here's an example of typical usage:
25674  * <pre><code>
25675 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25676 cp.render('my-div');
25677
25678 cp.on('select', function(palette, selColor){
25679     // do something with selColor
25680 });
25681 </code></pre>
25682  * @constructor
25683  * Create a new ColorPalette
25684  * @param {Object} config The config object
25685  */
25686 Roo.ColorPalette = function(config){
25687     Roo.ColorPalette.superclass.constructor.call(this, config);
25688     this.addEvents({
25689         /**
25690              * @event select
25691              * Fires when a color is selected
25692              * @param {ColorPalette} this
25693              * @param {String} color The 6-digit color hex code (without the # symbol)
25694              */
25695         select: true
25696     });
25697
25698     if(this.handler){
25699         this.on("select", this.handler, this.scope, true);
25700     }
25701 };
25702 Roo.extend(Roo.ColorPalette, Roo.Component, {
25703     /**
25704      * @cfg {String} itemCls
25705      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25706      */
25707     itemCls : "x-color-palette",
25708     /**
25709      * @cfg {String} value
25710      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25711      * the hex codes are case-sensitive.
25712      */
25713     value : null,
25714     clickEvent:'click',
25715     // private
25716     ctype: "Roo.ColorPalette",
25717
25718     /**
25719      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25720      */
25721     allowReselect : false,
25722
25723     /**
25724      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25725      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25726      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25727      * of colors with the width setting until the box is symmetrical.</p>
25728      * <p>You can override individual colors if needed:</p>
25729      * <pre><code>
25730 var cp = new Roo.ColorPalette();
25731 cp.colors[0] = "FF0000";  // change the first box to red
25732 </code></pre>
25733
25734 Or you can provide a custom array of your own for complete control:
25735 <pre><code>
25736 var cp = new Roo.ColorPalette();
25737 cp.colors = ["000000", "993300", "333300"];
25738 </code></pre>
25739      * @type Array
25740      */
25741     colors : [
25742         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25743         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25744         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25745         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25746         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25747     ],
25748
25749     // private
25750     onRender : function(container, position){
25751         var t = new Roo.MasterTemplate(
25752             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25753         );
25754         var c = this.colors;
25755         for(var i = 0, len = c.length; i < len; i++){
25756             t.add([c[i]]);
25757         }
25758         var el = document.createElement("div");
25759         el.className = this.itemCls;
25760         t.overwrite(el);
25761         container.dom.insertBefore(el, position);
25762         this.el = Roo.get(el);
25763         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25764         if(this.clickEvent != 'click'){
25765             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25766         }
25767     },
25768
25769     // private
25770     afterRender : function(){
25771         Roo.ColorPalette.superclass.afterRender.call(this);
25772         if(this.value){
25773             var s = this.value;
25774             this.value = null;
25775             this.select(s);
25776         }
25777     },
25778
25779     // private
25780     handleClick : function(e, t){
25781         e.preventDefault();
25782         if(!this.disabled){
25783             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25784             this.select(c.toUpperCase());
25785         }
25786     },
25787
25788     /**
25789      * Selects the specified color in the palette (fires the select event)
25790      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25791      */
25792     select : function(color){
25793         color = color.replace("#", "");
25794         if(color != this.value || this.allowReselect){
25795             var el = this.el;
25796             if(this.value){
25797                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25798             }
25799             el.child("a.color-"+color).addClass("x-color-palette-sel");
25800             this.value = color;
25801             this.fireEvent("select", this, color);
25802         }
25803     }
25804 });/*
25805  * Based on:
25806  * Ext JS Library 1.1.1
25807  * Copyright(c) 2006-2007, Ext JS, LLC.
25808  *
25809  * Originally Released Under LGPL - original licence link has changed is not relivant.
25810  *
25811  * Fork - LGPL
25812  * <script type="text/javascript">
25813  */
25814  
25815 /**
25816  * @class Roo.DatePicker
25817  * @extends Roo.Component
25818  * Simple date picker class.
25819  * @constructor
25820  * Create a new DatePicker
25821  * @param {Object} config The config object
25822  */
25823 Roo.DatePicker = function(config){
25824     Roo.DatePicker.superclass.constructor.call(this, config);
25825
25826     this.value = config && config.value ?
25827                  config.value.clearTime() : new Date().clearTime();
25828
25829     this.addEvents({
25830         /**
25831              * @event select
25832              * Fires when a date is selected
25833              * @param {DatePicker} this
25834              * @param {Date} date The selected date
25835              */
25836         'select': true,
25837         /**
25838              * @event monthchange
25839              * Fires when the displayed month changes 
25840              * @param {DatePicker} this
25841              * @param {Date} date The selected month
25842              */
25843         'monthchange': true
25844     });
25845
25846     if(this.handler){
25847         this.on("select", this.handler,  this.scope || this);
25848     }
25849     // build the disabledDatesRE
25850     if(!this.disabledDatesRE && this.disabledDates){
25851         var dd = this.disabledDates;
25852         var re = "(?:";
25853         for(var i = 0; i < dd.length; i++){
25854             re += dd[i];
25855             if(i != dd.length-1) re += "|";
25856         }
25857         this.disabledDatesRE = new RegExp(re + ")");
25858     }
25859 };
25860
25861 Roo.extend(Roo.DatePicker, Roo.Component, {
25862     /**
25863      * @cfg {String} todayText
25864      * The text to display on the button that selects the current date (defaults to "Today")
25865      */
25866     todayText : "Today",
25867     /**
25868      * @cfg {String} okText
25869      * The text to display on the ok button
25870      */
25871     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25872     /**
25873      * @cfg {String} cancelText
25874      * The text to display on the cancel button
25875      */
25876     cancelText : "Cancel",
25877     /**
25878      * @cfg {String} todayTip
25879      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25880      */
25881     todayTip : "{0} (Spacebar)",
25882     /**
25883      * @cfg {Date} minDate
25884      * Minimum allowable date (JavaScript date object, defaults to null)
25885      */
25886     minDate : null,
25887     /**
25888      * @cfg {Date} maxDate
25889      * Maximum allowable date (JavaScript date object, defaults to null)
25890      */
25891     maxDate : null,
25892     /**
25893      * @cfg {String} minText
25894      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25895      */
25896     minText : "This date is before the minimum date",
25897     /**
25898      * @cfg {String} maxText
25899      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25900      */
25901     maxText : "This date is after the maximum date",
25902     /**
25903      * @cfg {String} format
25904      * The default date format string which can be overriden for localization support.  The format must be
25905      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25906      */
25907     format : "m/d/y",
25908     /**
25909      * @cfg {Array} disabledDays
25910      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25911      */
25912     disabledDays : null,
25913     /**
25914      * @cfg {String} disabledDaysText
25915      * The tooltip to display when the date falls on a disabled day (defaults to "")
25916      */
25917     disabledDaysText : "",
25918     /**
25919      * @cfg {RegExp} disabledDatesRE
25920      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25921      */
25922     disabledDatesRE : null,
25923     /**
25924      * @cfg {String} disabledDatesText
25925      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25926      */
25927     disabledDatesText : "",
25928     /**
25929      * @cfg {Boolean} constrainToViewport
25930      * True to constrain the date picker to the viewport (defaults to true)
25931      */
25932     constrainToViewport : true,
25933     /**
25934      * @cfg {Array} monthNames
25935      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25936      */
25937     monthNames : Date.monthNames,
25938     /**
25939      * @cfg {Array} dayNames
25940      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25941      */
25942     dayNames : Date.dayNames,
25943     /**
25944      * @cfg {String} nextText
25945      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25946      */
25947     nextText: 'Next Month (Control+Right)',
25948     /**
25949      * @cfg {String} prevText
25950      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25951      */
25952     prevText: 'Previous Month (Control+Left)',
25953     /**
25954      * @cfg {String} monthYearText
25955      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25956      */
25957     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25958     /**
25959      * @cfg {Number} startDay
25960      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25961      */
25962     startDay : 0,
25963     /**
25964      * @cfg {Bool} showClear
25965      * Show a clear button (usefull for date form elements that can be blank.)
25966      */
25967     
25968     showClear: false,
25969     
25970     /**
25971      * Sets the value of the date field
25972      * @param {Date} value The date to set
25973      */
25974     setValue : function(value){
25975         var old = this.value;
25976         
25977         if (typeof(value) == 'string') {
25978          
25979             value = Date.parseDate(value, this.format);
25980         }
25981         if (!value) {
25982             value = new Date();
25983         }
25984         
25985         this.value = value.clearTime(true);
25986         if(this.el){
25987             this.update(this.value);
25988         }
25989     },
25990
25991     /**
25992      * Gets the current selected value of the date field
25993      * @return {Date} The selected date
25994      */
25995     getValue : function(){
25996         return this.value;
25997     },
25998
25999     // private
26000     focus : function(){
26001         if(this.el){
26002             this.update(this.activeDate);
26003         }
26004     },
26005
26006     // privateval
26007     onRender : function(container, position){
26008         
26009         var m = [
26010              '<table cellspacing="0">',
26011                 '<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>',
26012                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26013         var dn = this.dayNames;
26014         for(var i = 0; i < 7; i++){
26015             var d = this.startDay+i;
26016             if(d > 6){
26017                 d = d-7;
26018             }
26019             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26020         }
26021         m[m.length] = "</tr></thead><tbody><tr>";
26022         for(var i = 0; i < 42; i++) {
26023             if(i % 7 == 0 && i != 0){
26024                 m[m.length] = "</tr><tr>";
26025             }
26026             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26027         }
26028         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26029             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26030
26031         var el = document.createElement("div");
26032         el.className = "x-date-picker";
26033         el.innerHTML = m.join("");
26034
26035         container.dom.insertBefore(el, position);
26036
26037         this.el = Roo.get(el);
26038         this.eventEl = Roo.get(el.firstChild);
26039
26040         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26041             handler: this.showPrevMonth,
26042             scope: this,
26043             preventDefault:true,
26044             stopDefault:true
26045         });
26046
26047         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26048             handler: this.showNextMonth,
26049             scope: this,
26050             preventDefault:true,
26051             stopDefault:true
26052         });
26053
26054         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26055
26056         this.monthPicker = this.el.down('div.x-date-mp');
26057         this.monthPicker.enableDisplayMode('block');
26058         
26059         var kn = new Roo.KeyNav(this.eventEl, {
26060             "left" : function(e){
26061                 e.ctrlKey ?
26062                     this.showPrevMonth() :
26063                     this.update(this.activeDate.add("d", -1));
26064             },
26065
26066             "right" : function(e){
26067                 e.ctrlKey ?
26068                     this.showNextMonth() :
26069                     this.update(this.activeDate.add("d", 1));
26070             },
26071
26072             "up" : function(e){
26073                 e.ctrlKey ?
26074                     this.showNextYear() :
26075                     this.update(this.activeDate.add("d", -7));
26076             },
26077
26078             "down" : function(e){
26079                 e.ctrlKey ?
26080                     this.showPrevYear() :
26081                     this.update(this.activeDate.add("d", 7));
26082             },
26083
26084             "pageUp" : function(e){
26085                 this.showNextMonth();
26086             },
26087
26088             "pageDown" : function(e){
26089                 this.showPrevMonth();
26090             },
26091
26092             "enter" : function(e){
26093                 e.stopPropagation();
26094                 return true;
26095             },
26096
26097             scope : this
26098         });
26099
26100         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26101
26102         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26103
26104         this.el.unselectable();
26105         
26106         this.cells = this.el.select("table.x-date-inner tbody td");
26107         this.textNodes = this.el.query("table.x-date-inner tbody span");
26108
26109         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26110             text: "&#160;",
26111             tooltip: this.monthYearText
26112         });
26113
26114         this.mbtn.on('click', this.showMonthPicker, this);
26115         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26116
26117
26118         var today = (new Date()).dateFormat(this.format);
26119         
26120         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26121         if (this.showClear) {
26122             baseTb.add( new Roo.Toolbar.Fill());
26123         }
26124         baseTb.add({
26125             text: String.format(this.todayText, today),
26126             tooltip: String.format(this.todayTip, today),
26127             handler: this.selectToday,
26128             scope: this
26129         });
26130         
26131         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26132             
26133         //});
26134         if (this.showClear) {
26135             
26136             baseTb.add( new Roo.Toolbar.Fill());
26137             baseTb.add({
26138                 text: '&#160;',
26139                 cls: 'x-btn-icon x-btn-clear',
26140                 handler: function() {
26141                     //this.value = '';
26142                     this.fireEvent("select", this, '');
26143                 },
26144                 scope: this
26145             });
26146         }
26147         
26148         
26149         if(Roo.isIE){
26150             this.el.repaint();
26151         }
26152         this.update(this.value);
26153     },
26154
26155     createMonthPicker : function(){
26156         if(!this.monthPicker.dom.firstChild){
26157             var buf = ['<table border="0" cellspacing="0">'];
26158             for(var i = 0; i < 6; i++){
26159                 buf.push(
26160                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26161                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26162                     i == 0 ?
26163                     '<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>' :
26164                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26165                 );
26166             }
26167             buf.push(
26168                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26169                     this.okText,
26170                     '</button><button type="button" class="x-date-mp-cancel">',
26171                     this.cancelText,
26172                     '</button></td></tr>',
26173                 '</table>'
26174             );
26175             this.monthPicker.update(buf.join(''));
26176             this.monthPicker.on('click', this.onMonthClick, this);
26177             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26178
26179             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26180             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26181
26182             this.mpMonths.each(function(m, a, i){
26183                 i += 1;
26184                 if((i%2) == 0){
26185                     m.dom.xmonth = 5 + Math.round(i * .5);
26186                 }else{
26187                     m.dom.xmonth = Math.round((i-1) * .5);
26188                 }
26189             });
26190         }
26191     },
26192
26193     showMonthPicker : function(){
26194         this.createMonthPicker();
26195         var size = this.el.getSize();
26196         this.monthPicker.setSize(size);
26197         this.monthPicker.child('table').setSize(size);
26198
26199         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26200         this.updateMPMonth(this.mpSelMonth);
26201         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26202         this.updateMPYear(this.mpSelYear);
26203
26204         this.monthPicker.slideIn('t', {duration:.2});
26205     },
26206
26207     updateMPYear : function(y){
26208         this.mpyear = y;
26209         var ys = this.mpYears.elements;
26210         for(var i = 1; i <= 10; i++){
26211             var td = ys[i-1], y2;
26212             if((i%2) == 0){
26213                 y2 = y + Math.round(i * .5);
26214                 td.firstChild.innerHTML = y2;
26215                 td.xyear = y2;
26216             }else{
26217                 y2 = y - (5-Math.round(i * .5));
26218                 td.firstChild.innerHTML = y2;
26219                 td.xyear = y2;
26220             }
26221             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26222         }
26223     },
26224
26225     updateMPMonth : function(sm){
26226         this.mpMonths.each(function(m, a, i){
26227             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26228         });
26229     },
26230
26231     selectMPMonth: function(m){
26232         
26233     },
26234
26235     onMonthClick : function(e, t){
26236         e.stopEvent();
26237         var el = new Roo.Element(t), pn;
26238         if(el.is('button.x-date-mp-cancel')){
26239             this.hideMonthPicker();
26240         }
26241         else if(el.is('button.x-date-mp-ok')){
26242             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26243             this.hideMonthPicker();
26244         }
26245         else if(pn = el.up('td.x-date-mp-month', 2)){
26246             this.mpMonths.removeClass('x-date-mp-sel');
26247             pn.addClass('x-date-mp-sel');
26248             this.mpSelMonth = pn.dom.xmonth;
26249         }
26250         else if(pn = el.up('td.x-date-mp-year', 2)){
26251             this.mpYears.removeClass('x-date-mp-sel');
26252             pn.addClass('x-date-mp-sel');
26253             this.mpSelYear = pn.dom.xyear;
26254         }
26255         else if(el.is('a.x-date-mp-prev')){
26256             this.updateMPYear(this.mpyear-10);
26257         }
26258         else if(el.is('a.x-date-mp-next')){
26259             this.updateMPYear(this.mpyear+10);
26260         }
26261     },
26262
26263     onMonthDblClick : function(e, t){
26264         e.stopEvent();
26265         var el = new Roo.Element(t), pn;
26266         if(pn = el.up('td.x-date-mp-month', 2)){
26267             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26268             this.hideMonthPicker();
26269         }
26270         else if(pn = el.up('td.x-date-mp-year', 2)){
26271             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26272             this.hideMonthPicker();
26273         }
26274     },
26275
26276     hideMonthPicker : function(disableAnim){
26277         if(this.monthPicker){
26278             if(disableAnim === true){
26279                 this.monthPicker.hide();
26280             }else{
26281                 this.monthPicker.slideOut('t', {duration:.2});
26282             }
26283         }
26284     },
26285
26286     // private
26287     showPrevMonth : function(e){
26288         this.update(this.activeDate.add("mo", -1));
26289     },
26290
26291     // private
26292     showNextMonth : function(e){
26293         this.update(this.activeDate.add("mo", 1));
26294     },
26295
26296     // private
26297     showPrevYear : function(){
26298         this.update(this.activeDate.add("y", -1));
26299     },
26300
26301     // private
26302     showNextYear : function(){
26303         this.update(this.activeDate.add("y", 1));
26304     },
26305
26306     // private
26307     handleMouseWheel : function(e){
26308         var delta = e.getWheelDelta();
26309         if(delta > 0){
26310             this.showPrevMonth();
26311             e.stopEvent();
26312         } else if(delta < 0){
26313             this.showNextMonth();
26314             e.stopEvent();
26315         }
26316     },
26317
26318     // private
26319     handleDateClick : function(e, t){
26320         e.stopEvent();
26321         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26322             this.setValue(new Date(t.dateValue));
26323             this.fireEvent("select", this, this.value);
26324         }
26325     },
26326
26327     // private
26328     selectToday : function(){
26329         this.setValue(new Date().clearTime());
26330         this.fireEvent("select", this, this.value);
26331     },
26332
26333     // private
26334     update : function(date)
26335     {
26336         var vd = this.activeDate;
26337         this.activeDate = date;
26338         if(vd && this.el){
26339             var t = date.getTime();
26340             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26341                 this.cells.removeClass("x-date-selected");
26342                 this.cells.each(function(c){
26343                    if(c.dom.firstChild.dateValue == t){
26344                        c.addClass("x-date-selected");
26345                        setTimeout(function(){
26346                             try{c.dom.firstChild.focus();}catch(e){}
26347                        }, 50);
26348                        return false;
26349                    }
26350                 });
26351                 return;
26352             }
26353         }
26354         
26355         var days = date.getDaysInMonth();
26356         var firstOfMonth = date.getFirstDateOfMonth();
26357         var startingPos = firstOfMonth.getDay()-this.startDay;
26358
26359         if(startingPos <= this.startDay){
26360             startingPos += 7;
26361         }
26362
26363         var pm = date.add("mo", -1);
26364         var prevStart = pm.getDaysInMonth()-startingPos;
26365
26366         var cells = this.cells.elements;
26367         var textEls = this.textNodes;
26368         days += startingPos;
26369
26370         // convert everything to numbers so it's fast
26371         var day = 86400000;
26372         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26373         var today = new Date().clearTime().getTime();
26374         var sel = date.clearTime().getTime();
26375         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26376         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26377         var ddMatch = this.disabledDatesRE;
26378         var ddText = this.disabledDatesText;
26379         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26380         var ddaysText = this.disabledDaysText;
26381         var format = this.format;
26382
26383         var setCellClass = function(cal, cell){
26384             cell.title = "";
26385             var t = d.getTime();
26386             cell.firstChild.dateValue = t;
26387             if(t == today){
26388                 cell.className += " x-date-today";
26389                 cell.title = cal.todayText;
26390             }
26391             if(t == sel){
26392                 cell.className += " x-date-selected";
26393                 setTimeout(function(){
26394                     try{cell.firstChild.focus();}catch(e){}
26395                 }, 50);
26396             }
26397             // disabling
26398             if(t < min) {
26399                 cell.className = " x-date-disabled";
26400                 cell.title = cal.minText;
26401                 return;
26402             }
26403             if(t > max) {
26404                 cell.className = " x-date-disabled";
26405                 cell.title = cal.maxText;
26406                 return;
26407             }
26408             if(ddays){
26409                 if(ddays.indexOf(d.getDay()) != -1){
26410                     cell.title = ddaysText;
26411                     cell.className = " x-date-disabled";
26412                 }
26413             }
26414             if(ddMatch && format){
26415                 var fvalue = d.dateFormat(format);
26416                 if(ddMatch.test(fvalue)){
26417                     cell.title = ddText.replace("%0", fvalue);
26418                     cell.className = " x-date-disabled";
26419                 }
26420             }
26421         };
26422
26423         var i = 0;
26424         for(; i < startingPos; i++) {
26425             textEls[i].innerHTML = (++prevStart);
26426             d.setDate(d.getDate()+1);
26427             cells[i].className = "x-date-prevday";
26428             setCellClass(this, cells[i]);
26429         }
26430         for(; i < days; i++){
26431             intDay = i - startingPos + 1;
26432             textEls[i].innerHTML = (intDay);
26433             d.setDate(d.getDate()+1);
26434             cells[i].className = "x-date-active";
26435             setCellClass(this, cells[i]);
26436         }
26437         var extraDays = 0;
26438         for(; i < 42; i++) {
26439              textEls[i].innerHTML = (++extraDays);
26440              d.setDate(d.getDate()+1);
26441              cells[i].className = "x-date-nextday";
26442              setCellClass(this, cells[i]);
26443         }
26444
26445         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26446         this.fireEvent('monthchange', this, date);
26447         
26448         if(!this.internalRender){
26449             var main = this.el.dom.firstChild;
26450             var w = main.offsetWidth;
26451             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26452             Roo.fly(main).setWidth(w);
26453             this.internalRender = true;
26454             // opera does not respect the auto grow header center column
26455             // then, after it gets a width opera refuses to recalculate
26456             // without a second pass
26457             if(Roo.isOpera && !this.secondPass){
26458                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26459                 this.secondPass = true;
26460                 this.update.defer(10, this, [date]);
26461             }
26462         }
26463         
26464         
26465     }
26466 });        /*
26467  * Based on:
26468  * Ext JS Library 1.1.1
26469  * Copyright(c) 2006-2007, Ext JS, LLC.
26470  *
26471  * Originally Released Under LGPL - original licence link has changed is not relivant.
26472  *
26473  * Fork - LGPL
26474  * <script type="text/javascript">
26475  */
26476 /**
26477  * @class Roo.TabPanel
26478  * @extends Roo.util.Observable
26479  * A lightweight tab container.
26480  * <br><br>
26481  * Usage:
26482  * <pre><code>
26483 // basic tabs 1, built from existing content
26484 var tabs = new Roo.TabPanel("tabs1");
26485 tabs.addTab("script", "View Script");
26486 tabs.addTab("markup", "View Markup");
26487 tabs.activate("script");
26488
26489 // more advanced tabs, built from javascript
26490 var jtabs = new Roo.TabPanel("jtabs");
26491 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26492
26493 // set up the UpdateManager
26494 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26495 var updater = tab2.getUpdateManager();
26496 updater.setDefaultUrl("ajax1.htm");
26497 tab2.on('activate', updater.refresh, updater, true);
26498
26499 // Use setUrl for Ajax loading
26500 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26501 tab3.setUrl("ajax2.htm", null, true);
26502
26503 // Disabled tab
26504 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26505 tab4.disable();
26506
26507 jtabs.activate("jtabs-1");
26508  * </code></pre>
26509  * @constructor
26510  * Create a new TabPanel.
26511  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26512  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26513  */
26514 Roo.TabPanel = function(container, config){
26515     /**
26516     * The container element for this TabPanel.
26517     * @type Roo.Element
26518     */
26519     this.el = Roo.get(container, true);
26520     if(config){
26521         if(typeof config == "boolean"){
26522             this.tabPosition = config ? "bottom" : "top";
26523         }else{
26524             Roo.apply(this, config);
26525         }
26526     }
26527     if(this.tabPosition == "bottom"){
26528         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26529         this.el.addClass("x-tabs-bottom");
26530     }
26531     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26532     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26533     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26534     if(Roo.isIE){
26535         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26536     }
26537     if(this.tabPosition != "bottom"){
26538         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26539          * @type Roo.Element
26540          */
26541         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26542         this.el.addClass("x-tabs-top");
26543     }
26544     this.items = [];
26545
26546     this.bodyEl.setStyle("position", "relative");
26547
26548     this.active = null;
26549     this.activateDelegate = this.activate.createDelegate(this);
26550
26551     this.addEvents({
26552         /**
26553          * @event tabchange
26554          * Fires when the active tab changes
26555          * @param {Roo.TabPanel} this
26556          * @param {Roo.TabPanelItem} activePanel The new active tab
26557          */
26558         "tabchange": true,
26559         /**
26560          * @event beforetabchange
26561          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26562          * @param {Roo.TabPanel} this
26563          * @param {Object} e Set cancel to true on this object to cancel the tab change
26564          * @param {Roo.TabPanelItem} tab The tab being changed to
26565          */
26566         "beforetabchange" : true
26567     });
26568
26569     Roo.EventManager.onWindowResize(this.onResize, this);
26570     this.cpad = this.el.getPadding("lr");
26571     this.hiddenCount = 0;
26572
26573
26574     // toolbar on the tabbar support...
26575     if (this.toolbar) {
26576         var tcfg = this.toolbar;
26577         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26578         this.toolbar = new Roo.Toolbar(tcfg);
26579         if (Roo.isSafari) {
26580             var tbl = tcfg.container.child('table', true);
26581             tbl.setAttribute('width', '100%');
26582         }
26583         
26584     }
26585    
26586
26587
26588     Roo.TabPanel.superclass.constructor.call(this);
26589 };
26590
26591 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26592     /*
26593      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26594      */
26595     tabPosition : "top",
26596     /*
26597      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26598      */
26599     currentTabWidth : 0,
26600     /*
26601      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26602      */
26603     minTabWidth : 40,
26604     /*
26605      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26606      */
26607     maxTabWidth : 250,
26608     /*
26609      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26610      */
26611     preferredTabWidth : 175,
26612     /*
26613      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26614      */
26615     resizeTabs : false,
26616     /*
26617      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26618      */
26619     monitorResize : true,
26620     /*
26621      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26622      */
26623     toolbar : false,
26624
26625     /**
26626      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26627      * @param {String} id The id of the div to use <b>or create</b>
26628      * @param {String} text The text for the tab
26629      * @param {String} content (optional) Content to put in the TabPanelItem body
26630      * @param {Boolean} closable (optional) True to create a close icon on the tab
26631      * @return {Roo.TabPanelItem} The created TabPanelItem
26632      */
26633     addTab : function(id, text, content, closable){
26634         var item = new Roo.TabPanelItem(this, id, text, closable);
26635         this.addTabItem(item);
26636         if(content){
26637             item.setContent(content);
26638         }
26639         return item;
26640     },
26641
26642     /**
26643      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26644      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26645      * @return {Roo.TabPanelItem}
26646      */
26647     getTab : function(id){
26648         return this.items[id];
26649     },
26650
26651     /**
26652      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26653      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26654      */
26655     hideTab : function(id){
26656         var t = this.items[id];
26657         if(!t.isHidden()){
26658            t.setHidden(true);
26659            this.hiddenCount++;
26660            this.autoSizeTabs();
26661         }
26662     },
26663
26664     /**
26665      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26666      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26667      */
26668     unhideTab : function(id){
26669         var t = this.items[id];
26670         if(t.isHidden()){
26671            t.setHidden(false);
26672            this.hiddenCount--;
26673            this.autoSizeTabs();
26674         }
26675     },
26676
26677     /**
26678      * Adds an existing {@link Roo.TabPanelItem}.
26679      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26680      */
26681     addTabItem : function(item){
26682         this.items[item.id] = item;
26683         this.items.push(item);
26684         if(this.resizeTabs){
26685            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26686            this.autoSizeTabs();
26687         }else{
26688             item.autoSize();
26689         }
26690     },
26691
26692     /**
26693      * Removes a {@link Roo.TabPanelItem}.
26694      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26695      */
26696     removeTab : function(id){
26697         var items = this.items;
26698         var tab = items[id];
26699         if(!tab) { return; }
26700         var index = items.indexOf(tab);
26701         if(this.active == tab && items.length > 1){
26702             var newTab = this.getNextAvailable(index);
26703             if(newTab) {
26704                 newTab.activate();
26705             }
26706         }
26707         this.stripEl.dom.removeChild(tab.pnode.dom);
26708         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26709             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26710         }
26711         items.splice(index, 1);
26712         delete this.items[tab.id];
26713         tab.fireEvent("close", tab);
26714         tab.purgeListeners();
26715         this.autoSizeTabs();
26716     },
26717
26718     getNextAvailable : function(start){
26719         var items = this.items;
26720         var index = start;
26721         // look for a next tab that will slide over to
26722         // replace the one being removed
26723         while(index < items.length){
26724             var item = items[++index];
26725             if(item && !item.isHidden()){
26726                 return item;
26727             }
26728         }
26729         // if one isn't found select the previous tab (on the left)
26730         index = start;
26731         while(index >= 0){
26732             var item = items[--index];
26733             if(item && !item.isHidden()){
26734                 return item;
26735             }
26736         }
26737         return null;
26738     },
26739
26740     /**
26741      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26742      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26743      */
26744     disableTab : function(id){
26745         var tab = this.items[id];
26746         if(tab && this.active != tab){
26747             tab.disable();
26748         }
26749     },
26750
26751     /**
26752      * Enables a {@link Roo.TabPanelItem} that is disabled.
26753      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26754      */
26755     enableTab : function(id){
26756         var tab = this.items[id];
26757         tab.enable();
26758     },
26759
26760     /**
26761      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26762      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26763      * @return {Roo.TabPanelItem} The TabPanelItem.
26764      */
26765     activate : function(id){
26766         var tab = this.items[id];
26767         if(!tab){
26768             return null;
26769         }
26770         if(tab == this.active || tab.disabled){
26771             return tab;
26772         }
26773         var e = {};
26774         this.fireEvent("beforetabchange", this, e, tab);
26775         if(e.cancel !== true && !tab.disabled){
26776             if(this.active){
26777                 this.active.hide();
26778             }
26779             this.active = this.items[id];
26780             this.active.show();
26781             this.fireEvent("tabchange", this, this.active);
26782         }
26783         return tab;
26784     },
26785
26786     /**
26787      * Gets the active {@link Roo.TabPanelItem}.
26788      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26789      */
26790     getActiveTab : function(){
26791         return this.active;
26792     },
26793
26794     /**
26795      * Updates the tab body element to fit the height of the container element
26796      * for overflow scrolling
26797      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26798      */
26799     syncHeight : function(targetHeight){
26800         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26801         var bm = this.bodyEl.getMargins();
26802         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26803         this.bodyEl.setHeight(newHeight);
26804         return newHeight;
26805     },
26806
26807     onResize : function(){
26808         if(this.monitorResize){
26809             this.autoSizeTabs();
26810         }
26811     },
26812
26813     /**
26814      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26815      */
26816     beginUpdate : function(){
26817         this.updating = true;
26818     },
26819
26820     /**
26821      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26822      */
26823     endUpdate : function(){
26824         this.updating = false;
26825         this.autoSizeTabs();
26826     },
26827
26828     /**
26829      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26830      */
26831     autoSizeTabs : function(){
26832         var count = this.items.length;
26833         var vcount = count - this.hiddenCount;
26834         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26835         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26836         var availWidth = Math.floor(w / vcount);
26837         var b = this.stripBody;
26838         if(b.getWidth() > w){
26839             var tabs = this.items;
26840             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26841             if(availWidth < this.minTabWidth){
26842                 /*if(!this.sleft){    // incomplete scrolling code
26843                     this.createScrollButtons();
26844                 }
26845                 this.showScroll();
26846                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26847             }
26848         }else{
26849             if(this.currentTabWidth < this.preferredTabWidth){
26850                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26851             }
26852         }
26853     },
26854
26855     /**
26856      * Returns the number of tabs in this TabPanel.
26857      * @return {Number}
26858      */
26859      getCount : function(){
26860          return this.items.length;
26861      },
26862
26863     /**
26864      * Resizes all the tabs to the passed width
26865      * @param {Number} The new width
26866      */
26867     setTabWidth : function(width){
26868         this.currentTabWidth = width;
26869         for(var i = 0, len = this.items.length; i < len; i++) {
26870                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26871         }
26872     },
26873
26874     /**
26875      * Destroys this TabPanel
26876      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26877      */
26878     destroy : function(removeEl){
26879         Roo.EventManager.removeResizeListener(this.onResize, this);
26880         for(var i = 0, len = this.items.length; i < len; i++){
26881             this.items[i].purgeListeners();
26882         }
26883         if(removeEl === true){
26884             this.el.update("");
26885             this.el.remove();
26886         }
26887     }
26888 });
26889
26890 /**
26891  * @class Roo.TabPanelItem
26892  * @extends Roo.util.Observable
26893  * Represents an individual item (tab plus body) in a TabPanel.
26894  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26895  * @param {String} id The id of this TabPanelItem
26896  * @param {String} text The text for the tab of this TabPanelItem
26897  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26898  */
26899 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26900     /**
26901      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26902      * @type Roo.TabPanel
26903      */
26904     this.tabPanel = tabPanel;
26905     /**
26906      * The id for this TabPanelItem
26907      * @type String
26908      */
26909     this.id = id;
26910     /** @private */
26911     this.disabled = false;
26912     /** @private */
26913     this.text = text;
26914     /** @private */
26915     this.loaded = false;
26916     this.closable = closable;
26917
26918     /**
26919      * The body element for this TabPanelItem.
26920      * @type Roo.Element
26921      */
26922     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26923     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26924     this.bodyEl.setStyle("display", "block");
26925     this.bodyEl.setStyle("zoom", "1");
26926     this.hideAction();
26927
26928     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26929     /** @private */
26930     this.el = Roo.get(els.el, true);
26931     this.inner = Roo.get(els.inner, true);
26932     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26933     this.pnode = Roo.get(els.el.parentNode, true);
26934     this.el.on("mousedown", this.onTabMouseDown, this);
26935     this.el.on("click", this.onTabClick, this);
26936     /** @private */
26937     if(closable){
26938         var c = Roo.get(els.close, true);
26939         c.dom.title = this.closeText;
26940         c.addClassOnOver("close-over");
26941         c.on("click", this.closeClick, this);
26942      }
26943
26944     this.addEvents({
26945          /**
26946          * @event activate
26947          * Fires when this tab becomes the active tab.
26948          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26949          * @param {Roo.TabPanelItem} this
26950          */
26951         "activate": true,
26952         /**
26953          * @event beforeclose
26954          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26955          * @param {Roo.TabPanelItem} this
26956          * @param {Object} e Set cancel to true on this object to cancel the close.
26957          */
26958         "beforeclose": true,
26959         /**
26960          * @event close
26961          * Fires when this tab is closed.
26962          * @param {Roo.TabPanelItem} this
26963          */
26964          "close": true,
26965         /**
26966          * @event deactivate
26967          * Fires when this tab is no longer the active tab.
26968          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26969          * @param {Roo.TabPanelItem} this
26970          */
26971          "deactivate" : true
26972     });
26973     this.hidden = false;
26974
26975     Roo.TabPanelItem.superclass.constructor.call(this);
26976 };
26977
26978 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26979     purgeListeners : function(){
26980        Roo.util.Observable.prototype.purgeListeners.call(this);
26981        this.el.removeAllListeners();
26982     },
26983     /**
26984      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26985      */
26986     show : function(){
26987         this.pnode.addClass("on");
26988         this.showAction();
26989         if(Roo.isOpera){
26990             this.tabPanel.stripWrap.repaint();
26991         }
26992         this.fireEvent("activate", this.tabPanel, this);
26993     },
26994
26995     /**
26996      * Returns true if this tab is the active tab.
26997      * @return {Boolean}
26998      */
26999     isActive : function(){
27000         return this.tabPanel.getActiveTab() == this;
27001     },
27002
27003     /**
27004      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27005      */
27006     hide : function(){
27007         this.pnode.removeClass("on");
27008         this.hideAction();
27009         this.fireEvent("deactivate", this.tabPanel, this);
27010     },
27011
27012     hideAction : function(){
27013         this.bodyEl.hide();
27014         this.bodyEl.setStyle("position", "absolute");
27015         this.bodyEl.setLeft("-20000px");
27016         this.bodyEl.setTop("-20000px");
27017     },
27018
27019     showAction : function(){
27020         this.bodyEl.setStyle("position", "relative");
27021         this.bodyEl.setTop("");
27022         this.bodyEl.setLeft("");
27023         this.bodyEl.show();
27024     },
27025
27026     /**
27027      * Set the tooltip for the tab.
27028      * @param {String} tooltip The tab's tooltip
27029      */
27030     setTooltip : function(text){
27031         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27032             this.textEl.dom.qtip = text;
27033             this.textEl.dom.removeAttribute('title');
27034         }else{
27035             this.textEl.dom.title = text;
27036         }
27037     },
27038
27039     onTabClick : function(e){
27040         e.preventDefault();
27041         this.tabPanel.activate(this.id);
27042     },
27043
27044     onTabMouseDown : function(e){
27045         e.preventDefault();
27046         this.tabPanel.activate(this.id);
27047     },
27048
27049     getWidth : function(){
27050         return this.inner.getWidth();
27051     },
27052
27053     setWidth : function(width){
27054         var iwidth = width - this.pnode.getPadding("lr");
27055         this.inner.setWidth(iwidth);
27056         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27057         this.pnode.setWidth(width);
27058     },
27059
27060     /**
27061      * Show or hide the tab
27062      * @param {Boolean} hidden True to hide or false to show.
27063      */
27064     setHidden : function(hidden){
27065         this.hidden = hidden;
27066         this.pnode.setStyle("display", hidden ? "none" : "");
27067     },
27068
27069     /**
27070      * Returns true if this tab is "hidden"
27071      * @return {Boolean}
27072      */
27073     isHidden : function(){
27074         return this.hidden;
27075     },
27076
27077     /**
27078      * Returns the text for this tab
27079      * @return {String}
27080      */
27081     getText : function(){
27082         return this.text;
27083     },
27084
27085     autoSize : function(){
27086         //this.el.beginMeasure();
27087         this.textEl.setWidth(1);
27088         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27089         //this.el.endMeasure();
27090     },
27091
27092     /**
27093      * Sets the text for the tab (Note: this also sets the tooltip text)
27094      * @param {String} text The tab's text and tooltip
27095      */
27096     setText : function(text){
27097         this.text = text;
27098         this.textEl.update(text);
27099         this.setTooltip(text);
27100         if(!this.tabPanel.resizeTabs){
27101             this.autoSize();
27102         }
27103     },
27104     /**
27105      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27106      */
27107     activate : function(){
27108         this.tabPanel.activate(this.id);
27109     },
27110
27111     /**
27112      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27113      */
27114     disable : function(){
27115         if(this.tabPanel.active != this){
27116             this.disabled = true;
27117             this.pnode.addClass("disabled");
27118         }
27119     },
27120
27121     /**
27122      * Enables this TabPanelItem if it was previously disabled.
27123      */
27124     enable : function(){
27125         this.disabled = false;
27126         this.pnode.removeClass("disabled");
27127     },
27128
27129     /**
27130      * Sets the content for this TabPanelItem.
27131      * @param {String} content The content
27132      * @param {Boolean} loadScripts true to look for and load scripts
27133      */
27134     setContent : function(content, loadScripts){
27135         this.bodyEl.update(content, loadScripts);
27136     },
27137
27138     /**
27139      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27140      * @return {Roo.UpdateManager} The UpdateManager
27141      */
27142     getUpdateManager : function(){
27143         return this.bodyEl.getUpdateManager();
27144     },
27145
27146     /**
27147      * Set a URL to be used to load the content for this TabPanelItem.
27148      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27149      * @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)
27150      * @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)
27151      * @return {Roo.UpdateManager} The UpdateManager
27152      */
27153     setUrl : function(url, params, loadOnce){
27154         if(this.refreshDelegate){
27155             this.un('activate', this.refreshDelegate);
27156         }
27157         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27158         this.on("activate", this.refreshDelegate);
27159         return this.bodyEl.getUpdateManager();
27160     },
27161
27162     /** @private */
27163     _handleRefresh : function(url, params, loadOnce){
27164         if(!loadOnce || !this.loaded){
27165             var updater = this.bodyEl.getUpdateManager();
27166             updater.update(url, params, this._setLoaded.createDelegate(this));
27167         }
27168     },
27169
27170     /**
27171      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27172      *   Will fail silently if the setUrl method has not been called.
27173      *   This does not activate the panel, just updates its content.
27174      */
27175     refresh : function(){
27176         if(this.refreshDelegate){
27177            this.loaded = false;
27178            this.refreshDelegate();
27179         }
27180     },
27181
27182     /** @private */
27183     _setLoaded : function(){
27184         this.loaded = true;
27185     },
27186
27187     /** @private */
27188     closeClick : function(e){
27189         var o = {};
27190         e.stopEvent();
27191         this.fireEvent("beforeclose", this, o);
27192         if(o.cancel !== true){
27193             this.tabPanel.removeTab(this.id);
27194         }
27195     },
27196     /**
27197      * The text displayed in the tooltip for the close icon.
27198      * @type String
27199      */
27200     closeText : "Close this tab"
27201 });
27202
27203 /** @private */
27204 Roo.TabPanel.prototype.createStrip = function(container){
27205     var strip = document.createElement("div");
27206     strip.className = "x-tabs-wrap";
27207     container.appendChild(strip);
27208     return strip;
27209 };
27210 /** @private */
27211 Roo.TabPanel.prototype.createStripList = function(strip){
27212     // div wrapper for retard IE
27213     // returns the "tr" element.
27214     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27215         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27216         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27217     return strip.firstChild.firstChild.firstChild.firstChild;
27218 };
27219 /** @private */
27220 Roo.TabPanel.prototype.createBody = function(container){
27221     var body = document.createElement("div");
27222     Roo.id(body, "tab-body");
27223     Roo.fly(body).addClass("x-tabs-body");
27224     container.appendChild(body);
27225     return body;
27226 };
27227 /** @private */
27228 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27229     var body = Roo.getDom(id);
27230     if(!body){
27231         body = document.createElement("div");
27232         body.id = id;
27233     }
27234     Roo.fly(body).addClass("x-tabs-item-body");
27235     bodyEl.insertBefore(body, bodyEl.firstChild);
27236     return body;
27237 };
27238 /** @private */
27239 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27240     var td = document.createElement("td");
27241     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27242     //stripEl.appendChild(td);
27243     if(closable){
27244         td.className = "x-tabs-closable";
27245         if(!this.closeTpl){
27246             this.closeTpl = new Roo.Template(
27247                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27248                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27249                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27250             );
27251         }
27252         var el = this.closeTpl.overwrite(td, {"text": text});
27253         var close = el.getElementsByTagName("div")[0];
27254         var inner = el.getElementsByTagName("em")[0];
27255         return {"el": el, "close": close, "inner": inner};
27256     } else {
27257         if(!this.tabTpl){
27258             this.tabTpl = new Roo.Template(
27259                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27260                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27261             );
27262         }
27263         var el = this.tabTpl.overwrite(td, {"text": text});
27264         var inner = el.getElementsByTagName("em")[0];
27265         return {"el": el, "inner": inner};
27266     }
27267 };/*
27268  * Based on:
27269  * Ext JS Library 1.1.1
27270  * Copyright(c) 2006-2007, Ext JS, LLC.
27271  *
27272  * Originally Released Under LGPL - original licence link has changed is not relivant.
27273  *
27274  * Fork - LGPL
27275  * <script type="text/javascript">
27276  */
27277
27278 /**
27279  * @class Roo.Button
27280  * @extends Roo.util.Observable
27281  * Simple Button class
27282  * @cfg {String} text The button text
27283  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27284  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27285  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27286  * @cfg {Object} scope The scope of the handler
27287  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27288  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27289  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27290  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27291  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27292  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27293    applies if enableToggle = true)
27294  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27295  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27296   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27297  * @constructor
27298  * Create a new button
27299  * @param {Object} config The config object
27300  */
27301 Roo.Button = function(renderTo, config)
27302 {
27303     if (!config) {
27304         config = renderTo;
27305         renderTo = config.renderTo || false;
27306     }
27307     
27308     Roo.apply(this, config);
27309     this.addEvents({
27310         /**
27311              * @event click
27312              * Fires when this button is clicked
27313              * @param {Button} this
27314              * @param {EventObject} e The click event
27315              */
27316             "click" : true,
27317         /**
27318              * @event toggle
27319              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27320              * @param {Button} this
27321              * @param {Boolean} pressed
27322              */
27323             "toggle" : true,
27324         /**
27325              * @event mouseover
27326              * Fires when the mouse hovers over the button
27327              * @param {Button} this
27328              * @param {Event} e The event object
27329              */
27330         'mouseover' : true,
27331         /**
27332              * @event mouseout
27333              * Fires when the mouse exits the button
27334              * @param {Button} this
27335              * @param {Event} e The event object
27336              */
27337         'mouseout': true,
27338          /**
27339              * @event render
27340              * Fires when the button is rendered
27341              * @param {Button} this
27342              */
27343         'render': true
27344     });
27345     if(this.menu){
27346         this.menu = Roo.menu.MenuMgr.get(this.menu);
27347     }
27348     // register listeners first!!  - so render can be captured..
27349     Roo.util.Observable.call(this);
27350     if(renderTo){
27351         this.render(renderTo);
27352     }
27353     
27354   
27355 };
27356
27357 Roo.extend(Roo.Button, Roo.util.Observable, {
27358     /**
27359      * 
27360      */
27361     
27362     /**
27363      * Read-only. True if this button is hidden
27364      * @type Boolean
27365      */
27366     hidden : false,
27367     /**
27368      * Read-only. True if this button is disabled
27369      * @type Boolean
27370      */
27371     disabled : false,
27372     /**
27373      * Read-only. True if this button is pressed (only if enableToggle = true)
27374      * @type Boolean
27375      */
27376     pressed : false,
27377
27378     /**
27379      * @cfg {Number} tabIndex 
27380      * The DOM tabIndex for this button (defaults to undefined)
27381      */
27382     tabIndex : undefined,
27383
27384     /**
27385      * @cfg {Boolean} enableToggle
27386      * True to enable pressed/not pressed toggling (defaults to false)
27387      */
27388     enableToggle: false,
27389     /**
27390      * @cfg {Mixed} menu
27391      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27392      */
27393     menu : undefined,
27394     /**
27395      * @cfg {String} menuAlign
27396      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27397      */
27398     menuAlign : "tl-bl?",
27399
27400     /**
27401      * @cfg {String} iconCls
27402      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27403      */
27404     iconCls : undefined,
27405     /**
27406      * @cfg {String} type
27407      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27408      */
27409     type : 'button',
27410
27411     // private
27412     menuClassTarget: 'tr',
27413
27414     /**
27415      * @cfg {String} clickEvent
27416      * The type of event to map to the button's event handler (defaults to 'click')
27417      */
27418     clickEvent : 'click',
27419
27420     /**
27421      * @cfg {Boolean} handleMouseEvents
27422      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27423      */
27424     handleMouseEvents : true,
27425
27426     /**
27427      * @cfg {String} tooltipType
27428      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27429      */
27430     tooltipType : 'qtip',
27431
27432     /**
27433      * @cfg {String} cls
27434      * A CSS class to apply to the button's main element.
27435      */
27436     
27437     /**
27438      * @cfg {Roo.Template} template (Optional)
27439      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27440      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27441      * require code modifications if required elements (e.g. a button) aren't present.
27442      */
27443
27444     // private
27445     render : function(renderTo){
27446         var btn;
27447         if(this.hideParent){
27448             this.parentEl = Roo.get(renderTo);
27449         }
27450         if(!this.dhconfig){
27451             if(!this.template){
27452                 if(!Roo.Button.buttonTemplate){
27453                     // hideous table template
27454                     Roo.Button.buttonTemplate = new Roo.Template(
27455                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27456                         '<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>',
27457                         "</tr></tbody></table>");
27458                 }
27459                 this.template = Roo.Button.buttonTemplate;
27460             }
27461             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27462             var btnEl = btn.child("button:first");
27463             btnEl.on('focus', this.onFocus, this);
27464             btnEl.on('blur', this.onBlur, this);
27465             if(this.cls){
27466                 btn.addClass(this.cls);
27467             }
27468             if(this.icon){
27469                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27470             }
27471             if(this.iconCls){
27472                 btnEl.addClass(this.iconCls);
27473                 if(!this.cls){
27474                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27475                 }
27476             }
27477             if(this.tabIndex !== undefined){
27478                 btnEl.dom.tabIndex = this.tabIndex;
27479             }
27480             if(this.tooltip){
27481                 if(typeof this.tooltip == 'object'){
27482                     Roo.QuickTips.tips(Roo.apply({
27483                           target: btnEl.id
27484                     }, this.tooltip));
27485                 } else {
27486                     btnEl.dom[this.tooltipType] = this.tooltip;
27487                 }
27488             }
27489         }else{
27490             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27491         }
27492         this.el = btn;
27493         if(this.id){
27494             this.el.dom.id = this.el.id = this.id;
27495         }
27496         if(this.menu){
27497             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27498             this.menu.on("show", this.onMenuShow, this);
27499             this.menu.on("hide", this.onMenuHide, this);
27500         }
27501         btn.addClass("x-btn");
27502         if(Roo.isIE && !Roo.isIE7){
27503             this.autoWidth.defer(1, this);
27504         }else{
27505             this.autoWidth();
27506         }
27507         if(this.handleMouseEvents){
27508             btn.on("mouseover", this.onMouseOver, this);
27509             btn.on("mouseout", this.onMouseOut, this);
27510             btn.on("mousedown", this.onMouseDown, this);
27511         }
27512         btn.on(this.clickEvent, this.onClick, this);
27513         //btn.on("mouseup", this.onMouseUp, this);
27514         if(this.hidden){
27515             this.hide();
27516         }
27517         if(this.disabled){
27518             this.disable();
27519         }
27520         Roo.ButtonToggleMgr.register(this);
27521         if(this.pressed){
27522             this.el.addClass("x-btn-pressed");
27523         }
27524         if(this.repeat){
27525             var repeater = new Roo.util.ClickRepeater(btn,
27526                 typeof this.repeat == "object" ? this.repeat : {}
27527             );
27528             repeater.on("click", this.onClick,  this);
27529         }
27530         
27531         this.fireEvent('render', this);
27532         
27533     },
27534     /**
27535      * Returns the button's underlying element
27536      * @return {Roo.Element} The element
27537      */
27538     getEl : function(){
27539         return this.el;  
27540     },
27541     
27542     /**
27543      * Destroys this Button and removes any listeners.
27544      */
27545     destroy : function(){
27546         Roo.ButtonToggleMgr.unregister(this);
27547         this.el.removeAllListeners();
27548         this.purgeListeners();
27549         this.el.remove();
27550     },
27551
27552     // private
27553     autoWidth : function(){
27554         if(this.el){
27555             this.el.setWidth("auto");
27556             if(Roo.isIE7 && Roo.isStrict){
27557                 var ib = this.el.child('button');
27558                 if(ib && ib.getWidth() > 20){
27559                     ib.clip();
27560                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27561                 }
27562             }
27563             if(this.minWidth){
27564                 if(this.hidden){
27565                     this.el.beginMeasure();
27566                 }
27567                 if(this.el.getWidth() < this.minWidth){
27568                     this.el.setWidth(this.minWidth);
27569                 }
27570                 if(this.hidden){
27571                     this.el.endMeasure();
27572                 }
27573             }
27574         }
27575     },
27576
27577     /**
27578      * Assigns this button's click handler
27579      * @param {Function} handler The function to call when the button is clicked
27580      * @param {Object} scope (optional) Scope for the function passed in
27581      */
27582     setHandler : function(handler, scope){
27583         this.handler = handler;
27584         this.scope = scope;  
27585     },
27586     
27587     /**
27588      * Sets this button's text
27589      * @param {String} text The button text
27590      */
27591     setText : function(text){
27592         this.text = text;
27593         if(this.el){
27594             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27595         }
27596         this.autoWidth();
27597     },
27598     
27599     /**
27600      * Gets the text for this button
27601      * @return {String} The button text
27602      */
27603     getText : function(){
27604         return this.text;  
27605     },
27606     
27607     /**
27608      * Show this button
27609      */
27610     show: function(){
27611         this.hidden = false;
27612         if(this.el){
27613             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27614         }
27615     },
27616     
27617     /**
27618      * Hide this button
27619      */
27620     hide: function(){
27621         this.hidden = true;
27622         if(this.el){
27623             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27624         }
27625     },
27626     
27627     /**
27628      * Convenience function for boolean show/hide
27629      * @param {Boolean} visible True to show, false to hide
27630      */
27631     setVisible: function(visible){
27632         if(visible) {
27633             this.show();
27634         }else{
27635             this.hide();
27636         }
27637     },
27638     
27639     /**
27640      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27641      * @param {Boolean} state (optional) Force a particular state
27642      */
27643     toggle : function(state){
27644         state = state === undefined ? !this.pressed : state;
27645         if(state != this.pressed){
27646             if(state){
27647                 this.el.addClass("x-btn-pressed");
27648                 this.pressed = true;
27649                 this.fireEvent("toggle", this, true);
27650             }else{
27651                 this.el.removeClass("x-btn-pressed");
27652                 this.pressed = false;
27653                 this.fireEvent("toggle", this, false);
27654             }
27655             if(this.toggleHandler){
27656                 this.toggleHandler.call(this.scope || this, this, state);
27657             }
27658         }
27659     },
27660     
27661     /**
27662      * Focus the button
27663      */
27664     focus : function(){
27665         this.el.child('button:first').focus();
27666     },
27667     
27668     /**
27669      * Disable this button
27670      */
27671     disable : function(){
27672         if(this.el){
27673             this.el.addClass("x-btn-disabled");
27674         }
27675         this.disabled = true;
27676     },
27677     
27678     /**
27679      * Enable this button
27680      */
27681     enable : function(){
27682         if(this.el){
27683             this.el.removeClass("x-btn-disabled");
27684         }
27685         this.disabled = false;
27686     },
27687
27688     /**
27689      * Convenience function for boolean enable/disable
27690      * @param {Boolean} enabled True to enable, false to disable
27691      */
27692     setDisabled : function(v){
27693         this[v !== true ? "enable" : "disable"]();
27694     },
27695
27696     // private
27697     onClick : function(e){
27698         if(e){
27699             e.preventDefault();
27700         }
27701         if(e.button != 0){
27702             return;
27703         }
27704         if(!this.disabled){
27705             if(this.enableToggle){
27706                 this.toggle();
27707             }
27708             if(this.menu && !this.menu.isVisible()){
27709                 this.menu.show(this.el, this.menuAlign);
27710             }
27711             this.fireEvent("click", this, e);
27712             if(this.handler){
27713                 this.el.removeClass("x-btn-over");
27714                 this.handler.call(this.scope || this, this, e);
27715             }
27716         }
27717     },
27718     // private
27719     onMouseOver : function(e){
27720         if(!this.disabled){
27721             this.el.addClass("x-btn-over");
27722             this.fireEvent('mouseover', this, e);
27723         }
27724     },
27725     // private
27726     onMouseOut : function(e){
27727         if(!e.within(this.el,  true)){
27728             this.el.removeClass("x-btn-over");
27729             this.fireEvent('mouseout', this, e);
27730         }
27731     },
27732     // private
27733     onFocus : function(e){
27734         if(!this.disabled){
27735             this.el.addClass("x-btn-focus");
27736         }
27737     },
27738     // private
27739     onBlur : function(e){
27740         this.el.removeClass("x-btn-focus");
27741     },
27742     // private
27743     onMouseDown : function(e){
27744         if(!this.disabled && e.button == 0){
27745             this.el.addClass("x-btn-click");
27746             Roo.get(document).on('mouseup', this.onMouseUp, this);
27747         }
27748     },
27749     // private
27750     onMouseUp : function(e){
27751         if(e.button == 0){
27752             this.el.removeClass("x-btn-click");
27753             Roo.get(document).un('mouseup', this.onMouseUp, this);
27754         }
27755     },
27756     // private
27757     onMenuShow : function(e){
27758         this.el.addClass("x-btn-menu-active");
27759     },
27760     // private
27761     onMenuHide : function(e){
27762         this.el.removeClass("x-btn-menu-active");
27763     }   
27764 });
27765
27766 // Private utility class used by Button
27767 Roo.ButtonToggleMgr = function(){
27768    var groups = {};
27769    
27770    function toggleGroup(btn, state){
27771        if(state){
27772            var g = groups[btn.toggleGroup];
27773            for(var i = 0, l = g.length; i < l; i++){
27774                if(g[i] != btn){
27775                    g[i].toggle(false);
27776                }
27777            }
27778        }
27779    }
27780    
27781    return {
27782        register : function(btn){
27783            if(!btn.toggleGroup){
27784                return;
27785            }
27786            var g = groups[btn.toggleGroup];
27787            if(!g){
27788                g = groups[btn.toggleGroup] = [];
27789            }
27790            g.push(btn);
27791            btn.on("toggle", toggleGroup);
27792        },
27793        
27794        unregister : function(btn){
27795            if(!btn.toggleGroup){
27796                return;
27797            }
27798            var g = groups[btn.toggleGroup];
27799            if(g){
27800                g.remove(btn);
27801                btn.un("toggle", toggleGroup);
27802            }
27803        }
27804    };
27805 }();/*
27806  * Based on:
27807  * Ext JS Library 1.1.1
27808  * Copyright(c) 2006-2007, Ext JS, LLC.
27809  *
27810  * Originally Released Under LGPL - original licence link has changed is not relivant.
27811  *
27812  * Fork - LGPL
27813  * <script type="text/javascript">
27814  */
27815  
27816 /**
27817  * @class Roo.SplitButton
27818  * @extends Roo.Button
27819  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27820  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27821  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27822  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27823  * @cfg {String} arrowTooltip The title attribute of the arrow
27824  * @constructor
27825  * Create a new menu button
27826  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27827  * @param {Object} config The config object
27828  */
27829 Roo.SplitButton = function(renderTo, config){
27830     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27831     /**
27832      * @event arrowclick
27833      * Fires when this button's arrow is clicked
27834      * @param {SplitButton} this
27835      * @param {EventObject} e The click event
27836      */
27837     this.addEvents({"arrowclick":true});
27838 };
27839
27840 Roo.extend(Roo.SplitButton, Roo.Button, {
27841     render : function(renderTo){
27842         // this is one sweet looking template!
27843         var tpl = new Roo.Template(
27844             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27845             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27846             '<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>',
27847             "</tbody></table></td><td>",
27848             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27849             '<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>',
27850             "</tbody></table></td></tr></table>"
27851         );
27852         var btn = tpl.append(renderTo, [this.text, this.type], true);
27853         var btnEl = btn.child("button");
27854         if(this.cls){
27855             btn.addClass(this.cls);
27856         }
27857         if(this.icon){
27858             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27859         }
27860         if(this.iconCls){
27861             btnEl.addClass(this.iconCls);
27862             if(!this.cls){
27863                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27864             }
27865         }
27866         this.el = btn;
27867         if(this.handleMouseEvents){
27868             btn.on("mouseover", this.onMouseOver, this);
27869             btn.on("mouseout", this.onMouseOut, this);
27870             btn.on("mousedown", this.onMouseDown, this);
27871             btn.on("mouseup", this.onMouseUp, this);
27872         }
27873         btn.on(this.clickEvent, this.onClick, this);
27874         if(this.tooltip){
27875             if(typeof this.tooltip == 'object'){
27876                 Roo.QuickTips.tips(Roo.apply({
27877                       target: btnEl.id
27878                 }, this.tooltip));
27879             } else {
27880                 btnEl.dom[this.tooltipType] = this.tooltip;
27881             }
27882         }
27883         if(this.arrowTooltip){
27884             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27885         }
27886         if(this.hidden){
27887             this.hide();
27888         }
27889         if(this.disabled){
27890             this.disable();
27891         }
27892         if(this.pressed){
27893             this.el.addClass("x-btn-pressed");
27894         }
27895         if(Roo.isIE && !Roo.isIE7){
27896             this.autoWidth.defer(1, this);
27897         }else{
27898             this.autoWidth();
27899         }
27900         if(this.menu){
27901             this.menu.on("show", this.onMenuShow, this);
27902             this.menu.on("hide", this.onMenuHide, this);
27903         }
27904         this.fireEvent('render', this);
27905     },
27906
27907     // private
27908     autoWidth : function(){
27909         if(this.el){
27910             var tbl = this.el.child("table:first");
27911             var tbl2 = this.el.child("table:last");
27912             this.el.setWidth("auto");
27913             tbl.setWidth("auto");
27914             if(Roo.isIE7 && Roo.isStrict){
27915                 var ib = this.el.child('button:first');
27916                 if(ib && ib.getWidth() > 20){
27917                     ib.clip();
27918                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27919                 }
27920             }
27921             if(this.minWidth){
27922                 if(this.hidden){
27923                     this.el.beginMeasure();
27924                 }
27925                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27926                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27927                 }
27928                 if(this.hidden){
27929                     this.el.endMeasure();
27930                 }
27931             }
27932             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27933         } 
27934     },
27935     /**
27936      * Sets this button's click handler
27937      * @param {Function} handler The function to call when the button is clicked
27938      * @param {Object} scope (optional) Scope for the function passed above
27939      */
27940     setHandler : function(handler, scope){
27941         this.handler = handler;
27942         this.scope = scope;  
27943     },
27944     
27945     /**
27946      * Sets this button's arrow click handler
27947      * @param {Function} handler The function to call when the arrow is clicked
27948      * @param {Object} scope (optional) Scope for the function passed above
27949      */
27950     setArrowHandler : function(handler, scope){
27951         this.arrowHandler = handler;
27952         this.scope = scope;  
27953     },
27954     
27955     /**
27956      * Focus the button
27957      */
27958     focus : function(){
27959         if(this.el){
27960             this.el.child("button:first").focus();
27961         }
27962     },
27963
27964     // private
27965     onClick : function(e){
27966         e.preventDefault();
27967         if(!this.disabled){
27968             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27969                 if(this.menu && !this.menu.isVisible()){
27970                     this.menu.show(this.el, this.menuAlign);
27971                 }
27972                 this.fireEvent("arrowclick", this, e);
27973                 if(this.arrowHandler){
27974                     this.arrowHandler.call(this.scope || this, this, e);
27975                 }
27976             }else{
27977                 this.fireEvent("click", this, e);
27978                 if(this.handler){
27979                     this.handler.call(this.scope || this, this, e);
27980                 }
27981             }
27982         }
27983     },
27984     // private
27985     onMouseDown : function(e){
27986         if(!this.disabled){
27987             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27988         }
27989     },
27990     // private
27991     onMouseUp : function(e){
27992         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27993     }   
27994 });
27995
27996
27997 // backwards compat
27998 Roo.MenuButton = Roo.SplitButton;/*
27999  * Based on:
28000  * Ext JS Library 1.1.1
28001  * Copyright(c) 2006-2007, Ext JS, LLC.
28002  *
28003  * Originally Released Under LGPL - original licence link has changed is not relivant.
28004  *
28005  * Fork - LGPL
28006  * <script type="text/javascript">
28007  */
28008
28009 /**
28010  * @class Roo.Toolbar
28011  * Basic Toolbar class.
28012  * @constructor
28013  * Creates a new Toolbar
28014  * @param {Object} container The config object
28015  */ 
28016 Roo.Toolbar = function(container, buttons, config)
28017 {
28018     /// old consturctor format still supported..
28019     if(container instanceof Array){ // omit the container for later rendering
28020         buttons = container;
28021         config = buttons;
28022         container = null;
28023     }
28024     if (typeof(container) == 'object' && container.xtype) {
28025         config = container;
28026         container = config.container;
28027         buttons = config.buttons || []; // not really - use items!!
28028     }
28029     var xitems = [];
28030     if (config && config.items) {
28031         xitems = config.items;
28032         delete config.items;
28033     }
28034     Roo.apply(this, config);
28035     this.buttons = buttons;
28036     
28037     if(container){
28038         this.render(container);
28039     }
28040     this.xitems = xitems;
28041     Roo.each(xitems, function(b) {
28042         this.add(b);
28043     }, this);
28044     
28045 };
28046
28047 Roo.Toolbar.prototype = {
28048     /**
28049      * @cfg {Array} items
28050      * array of button configs or elements to add (will be converted to a MixedCollection)
28051      */
28052     
28053     /**
28054      * @cfg {String/HTMLElement/Element} container
28055      * The id or element that will contain the toolbar
28056      */
28057     // private
28058     render : function(ct){
28059         this.el = Roo.get(ct);
28060         if(this.cls){
28061             this.el.addClass(this.cls);
28062         }
28063         // using a table allows for vertical alignment
28064         // 100% width is needed by Safari...
28065         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28066         this.tr = this.el.child("tr", true);
28067         var autoId = 0;
28068         this.items = new Roo.util.MixedCollection(false, function(o){
28069             return o.id || ("item" + (++autoId));
28070         });
28071         if(this.buttons){
28072             this.add.apply(this, this.buttons);
28073             delete this.buttons;
28074         }
28075     },
28076
28077     /**
28078      * Adds element(s) to the toolbar -- this function takes a variable number of 
28079      * arguments of mixed type and adds them to the toolbar.
28080      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28081      * <ul>
28082      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28083      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28084      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28085      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28086      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28087      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28088      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28089      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28090      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28091      * </ul>
28092      * @param {Mixed} arg2
28093      * @param {Mixed} etc.
28094      */
28095     add : function(){
28096         var a = arguments, l = a.length;
28097         for(var i = 0; i < l; i++){
28098             this._add(a[i]);
28099         }
28100     },
28101     // private..
28102     _add : function(el) {
28103         
28104         if (el.xtype) {
28105             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28106         }
28107         
28108         if (el.applyTo){ // some kind of form field
28109             return this.addField(el);
28110         } 
28111         if (el.render){ // some kind of Toolbar.Item
28112             return this.addItem(el);
28113         }
28114         if (typeof el == "string"){ // string
28115             if(el == "separator" || el == "-"){
28116                 return this.addSeparator();
28117             }
28118             if (el == " "){
28119                 return this.addSpacer();
28120             }
28121             if(el == "->"){
28122                 return this.addFill();
28123             }
28124             return this.addText(el);
28125             
28126         }
28127         if(el.tagName){ // element
28128             return this.addElement(el);
28129         }
28130         if(typeof el == "object"){ // must be button config?
28131             return this.addButton(el);
28132         }
28133         // and now what?!?!
28134         return false;
28135         
28136     },
28137     
28138     /**
28139      * Add an Xtype element
28140      * @param {Object} xtype Xtype Object
28141      * @return {Object} created Object
28142      */
28143     addxtype : function(e){
28144         return this.add(e);  
28145     },
28146     
28147     /**
28148      * Returns the Element for this toolbar.
28149      * @return {Roo.Element}
28150      */
28151     getEl : function(){
28152         return this.el;  
28153     },
28154     
28155     /**
28156      * Adds a separator
28157      * @return {Roo.Toolbar.Item} The separator item
28158      */
28159     addSeparator : function(){
28160         return this.addItem(new Roo.Toolbar.Separator());
28161     },
28162
28163     /**
28164      * Adds a spacer element
28165      * @return {Roo.Toolbar.Spacer} The spacer item
28166      */
28167     addSpacer : function(){
28168         return this.addItem(new Roo.Toolbar.Spacer());
28169     },
28170
28171     /**
28172      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28173      * @return {Roo.Toolbar.Fill} The fill item
28174      */
28175     addFill : function(){
28176         return this.addItem(new Roo.Toolbar.Fill());
28177     },
28178
28179     /**
28180      * Adds any standard HTML element to the toolbar
28181      * @param {String/HTMLElement/Element} el The element or id of the element to add
28182      * @return {Roo.Toolbar.Item} The element's item
28183      */
28184     addElement : function(el){
28185         return this.addItem(new Roo.Toolbar.Item(el));
28186     },
28187     /**
28188      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28189      * @type Roo.util.MixedCollection  
28190      */
28191     items : false,
28192      
28193     /**
28194      * Adds any Toolbar.Item or subclass
28195      * @param {Roo.Toolbar.Item} item
28196      * @return {Roo.Toolbar.Item} The item
28197      */
28198     addItem : function(item){
28199         var td = this.nextBlock();
28200         item.render(td);
28201         this.items.add(item);
28202         return item;
28203     },
28204     
28205     /**
28206      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28207      * @param {Object/Array} config A button config or array of configs
28208      * @return {Roo.Toolbar.Button/Array}
28209      */
28210     addButton : function(config){
28211         if(config instanceof Array){
28212             var buttons = [];
28213             for(var i = 0, len = config.length; i < len; i++) {
28214                 buttons.push(this.addButton(config[i]));
28215             }
28216             return buttons;
28217         }
28218         var b = config;
28219         if(!(config instanceof Roo.Toolbar.Button)){
28220             b = config.split ?
28221                 new Roo.Toolbar.SplitButton(config) :
28222                 new Roo.Toolbar.Button(config);
28223         }
28224         var td = this.nextBlock();
28225         b.render(td);
28226         this.items.add(b);
28227         return b;
28228     },
28229     
28230     /**
28231      * Adds text to the toolbar
28232      * @param {String} text The text to add
28233      * @return {Roo.Toolbar.Item} The element's item
28234      */
28235     addText : function(text){
28236         return this.addItem(new Roo.Toolbar.TextItem(text));
28237     },
28238     
28239     /**
28240      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28241      * @param {Number} index The index where the item is to be inserted
28242      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28243      * @return {Roo.Toolbar.Button/Item}
28244      */
28245     insertButton : function(index, item){
28246         if(item instanceof Array){
28247             var buttons = [];
28248             for(var i = 0, len = item.length; i < len; i++) {
28249                buttons.push(this.insertButton(index + i, item[i]));
28250             }
28251             return buttons;
28252         }
28253         if (!(item instanceof Roo.Toolbar.Button)){
28254            item = new Roo.Toolbar.Button(item);
28255         }
28256         var td = document.createElement("td");
28257         this.tr.insertBefore(td, this.tr.childNodes[index]);
28258         item.render(td);
28259         this.items.insert(index, item);
28260         return item;
28261     },
28262     
28263     /**
28264      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28265      * @param {Object} config
28266      * @return {Roo.Toolbar.Item} The element's item
28267      */
28268     addDom : function(config, returnEl){
28269         var td = this.nextBlock();
28270         Roo.DomHelper.overwrite(td, config);
28271         var ti = new Roo.Toolbar.Item(td.firstChild);
28272         ti.render(td);
28273         this.items.add(ti);
28274         return ti;
28275     },
28276
28277     /**
28278      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28279      * @type Roo.util.MixedCollection  
28280      */
28281     fields : false,
28282     
28283     /**
28284      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28285      * Note: the field should not have been rendered yet. For a field that has already been
28286      * rendered, use {@link #addElement}.
28287      * @param {Roo.form.Field} field
28288      * @return {Roo.ToolbarItem}
28289      */
28290      
28291       
28292     addField : function(field) {
28293         if (!this.fields) {
28294             var autoId = 0;
28295             this.fields = new Roo.util.MixedCollection(false, function(o){
28296                 return o.id || ("item" + (++autoId));
28297             });
28298
28299         }
28300         
28301         var td = this.nextBlock();
28302         field.render(td);
28303         var ti = new Roo.Toolbar.Item(td.firstChild);
28304         ti.render(td);
28305         this.items.add(ti);
28306         this.fields.add(field);
28307         return ti;
28308     },
28309     /**
28310      * Hide the toolbar
28311      * @method hide
28312      */
28313      
28314       
28315     hide : function()
28316     {
28317         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28318         this.el.child('div').hide();
28319     },
28320     /**
28321      * Show the toolbar
28322      * @method show
28323      */
28324     show : function()
28325     {
28326         this.el.child('div').show();
28327     },
28328       
28329     // private
28330     nextBlock : function(){
28331         var td = document.createElement("td");
28332         this.tr.appendChild(td);
28333         return td;
28334     },
28335
28336     // private
28337     destroy : function(){
28338         if(this.items){ // rendered?
28339             Roo.destroy.apply(Roo, this.items.items);
28340         }
28341         if(this.fields){ // rendered?
28342             Roo.destroy.apply(Roo, this.fields.items);
28343         }
28344         Roo.Element.uncache(this.el, this.tr);
28345     }
28346 };
28347
28348 /**
28349  * @class Roo.Toolbar.Item
28350  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28351  * @constructor
28352  * Creates a new Item
28353  * @param {HTMLElement} el 
28354  */
28355 Roo.Toolbar.Item = function(el){
28356     this.el = Roo.getDom(el);
28357     this.id = Roo.id(this.el);
28358     this.hidden = false;
28359 };
28360
28361 Roo.Toolbar.Item.prototype = {
28362     
28363     /**
28364      * Get this item's HTML Element
28365      * @return {HTMLElement}
28366      */
28367     getEl : function(){
28368        return this.el;  
28369     },
28370
28371     // private
28372     render : function(td){
28373         this.td = td;
28374         td.appendChild(this.el);
28375     },
28376     
28377     /**
28378      * Removes and destroys this item.
28379      */
28380     destroy : function(){
28381         this.td.parentNode.removeChild(this.td);
28382     },
28383     
28384     /**
28385      * Shows this item.
28386      */
28387     show: function(){
28388         this.hidden = false;
28389         this.td.style.display = "";
28390     },
28391     
28392     /**
28393      * Hides this item.
28394      */
28395     hide: function(){
28396         this.hidden = true;
28397         this.td.style.display = "none";
28398     },
28399     
28400     /**
28401      * Convenience function for boolean show/hide.
28402      * @param {Boolean} visible true to show/false to hide
28403      */
28404     setVisible: function(visible){
28405         if(visible) {
28406             this.show();
28407         }else{
28408             this.hide();
28409         }
28410     },
28411     
28412     /**
28413      * Try to focus this item.
28414      */
28415     focus : function(){
28416         Roo.fly(this.el).focus();
28417     },
28418     
28419     /**
28420      * Disables this item.
28421      */
28422     disable : function(){
28423         Roo.fly(this.td).addClass("x-item-disabled");
28424         this.disabled = true;
28425         this.el.disabled = true;
28426     },
28427     
28428     /**
28429      * Enables this item.
28430      */
28431     enable : function(){
28432         Roo.fly(this.td).removeClass("x-item-disabled");
28433         this.disabled = false;
28434         this.el.disabled = false;
28435     }
28436 };
28437
28438
28439 /**
28440  * @class Roo.Toolbar.Separator
28441  * @extends Roo.Toolbar.Item
28442  * A simple toolbar separator class
28443  * @constructor
28444  * Creates a new Separator
28445  */
28446 Roo.Toolbar.Separator = function(){
28447     var s = document.createElement("span");
28448     s.className = "ytb-sep";
28449     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28450 };
28451 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28452     enable:Roo.emptyFn,
28453     disable:Roo.emptyFn,
28454     focus:Roo.emptyFn
28455 });
28456
28457 /**
28458  * @class Roo.Toolbar.Spacer
28459  * @extends Roo.Toolbar.Item
28460  * A simple element that adds extra horizontal space to a toolbar.
28461  * @constructor
28462  * Creates a new Spacer
28463  */
28464 Roo.Toolbar.Spacer = function(){
28465     var s = document.createElement("div");
28466     s.className = "ytb-spacer";
28467     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28468 };
28469 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28470     enable:Roo.emptyFn,
28471     disable:Roo.emptyFn,
28472     focus:Roo.emptyFn
28473 });
28474
28475 /**
28476  * @class Roo.Toolbar.Fill
28477  * @extends Roo.Toolbar.Spacer
28478  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28479  * @constructor
28480  * Creates a new Spacer
28481  */
28482 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28483     // private
28484     render : function(td){
28485         td.style.width = '100%';
28486         Roo.Toolbar.Fill.superclass.render.call(this, td);
28487     }
28488 });
28489
28490 /**
28491  * @class Roo.Toolbar.TextItem
28492  * @extends Roo.Toolbar.Item
28493  * A simple class that renders text directly into a toolbar.
28494  * @constructor
28495  * Creates a new TextItem
28496  * @param {String} text
28497  */
28498 Roo.Toolbar.TextItem = function(text){
28499     if (typeof(text) == 'object') {
28500         text = text.text;
28501     }
28502     var s = document.createElement("span");
28503     s.className = "ytb-text";
28504     s.innerHTML = text;
28505     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28506 };
28507 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28508     enable:Roo.emptyFn,
28509     disable:Roo.emptyFn,
28510     focus:Roo.emptyFn
28511 });
28512
28513 /**
28514  * @class Roo.Toolbar.Button
28515  * @extends Roo.Button
28516  * A button that renders into a toolbar.
28517  * @constructor
28518  * Creates a new Button
28519  * @param {Object} config A standard {@link Roo.Button} config object
28520  */
28521 Roo.Toolbar.Button = function(config){
28522     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28523 };
28524 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28525     render : function(td){
28526         this.td = td;
28527         Roo.Toolbar.Button.superclass.render.call(this, td);
28528     },
28529     
28530     /**
28531      * Removes and destroys this button
28532      */
28533     destroy : function(){
28534         Roo.Toolbar.Button.superclass.destroy.call(this);
28535         this.td.parentNode.removeChild(this.td);
28536     },
28537     
28538     /**
28539      * Shows this button
28540      */
28541     show: function(){
28542         this.hidden = false;
28543         this.td.style.display = "";
28544     },
28545     
28546     /**
28547      * Hides this button
28548      */
28549     hide: function(){
28550         this.hidden = true;
28551         this.td.style.display = "none";
28552     },
28553
28554     /**
28555      * Disables this item
28556      */
28557     disable : function(){
28558         Roo.fly(this.td).addClass("x-item-disabled");
28559         this.disabled = true;
28560     },
28561
28562     /**
28563      * Enables this item
28564      */
28565     enable : function(){
28566         Roo.fly(this.td).removeClass("x-item-disabled");
28567         this.disabled = false;
28568     }
28569 });
28570 // backwards compat
28571 Roo.ToolbarButton = Roo.Toolbar.Button;
28572
28573 /**
28574  * @class Roo.Toolbar.SplitButton
28575  * @extends Roo.SplitButton
28576  * A menu button that renders into a toolbar.
28577  * @constructor
28578  * Creates a new SplitButton
28579  * @param {Object} config A standard {@link Roo.SplitButton} config object
28580  */
28581 Roo.Toolbar.SplitButton = function(config){
28582     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28583 };
28584 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28585     render : function(td){
28586         this.td = td;
28587         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28588     },
28589     
28590     /**
28591      * Removes and destroys this button
28592      */
28593     destroy : function(){
28594         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28595         this.td.parentNode.removeChild(this.td);
28596     },
28597     
28598     /**
28599      * Shows this button
28600      */
28601     show: function(){
28602         this.hidden = false;
28603         this.td.style.display = "";
28604     },
28605     
28606     /**
28607      * Hides this button
28608      */
28609     hide: function(){
28610         this.hidden = true;
28611         this.td.style.display = "none";
28612     }
28613 });
28614
28615 // backwards compat
28616 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28617  * Based on:
28618  * Ext JS Library 1.1.1
28619  * Copyright(c) 2006-2007, Ext JS, LLC.
28620  *
28621  * Originally Released Under LGPL - original licence link has changed is not relivant.
28622  *
28623  * Fork - LGPL
28624  * <script type="text/javascript">
28625  */
28626  
28627 /**
28628  * @class Roo.PagingToolbar
28629  * @extends Roo.Toolbar
28630  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28631  * @constructor
28632  * Create a new PagingToolbar
28633  * @param {Object} config The config object
28634  */
28635 Roo.PagingToolbar = function(el, ds, config)
28636 {
28637     // old args format still supported... - xtype is prefered..
28638     if (typeof(el) == 'object' && el.xtype) {
28639         // created from xtype...
28640         config = el;
28641         ds = el.dataSource;
28642         el = config.container;
28643     }
28644     var items = [];
28645     if (config.items) {
28646         items = config.items;
28647         config.items = [];
28648     }
28649     
28650     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28651     this.ds = ds;
28652     this.cursor = 0;
28653     this.renderButtons(this.el);
28654     this.bind(ds);
28655     
28656     // supprot items array.
28657    
28658     Roo.each(items, function(e) {
28659         this.add(Roo.factory(e));
28660     },this);
28661     
28662 };
28663
28664 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28665     /**
28666      * @cfg {Roo.data.Store} dataSource
28667      * The underlying data store providing the paged data
28668      */
28669     /**
28670      * @cfg {String/HTMLElement/Element} container
28671      * container The id or element that will contain the toolbar
28672      */
28673     /**
28674      * @cfg {Boolean} displayInfo
28675      * True to display the displayMsg (defaults to false)
28676      */
28677     /**
28678      * @cfg {Number} pageSize
28679      * The number of records to display per page (defaults to 20)
28680      */
28681     pageSize: 20,
28682     /**
28683      * @cfg {String} displayMsg
28684      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28685      */
28686     displayMsg : 'Displaying {0} - {1} of {2}',
28687     /**
28688      * @cfg {String} emptyMsg
28689      * The message to display when no records are found (defaults to "No data to display")
28690      */
28691     emptyMsg : 'No data to display',
28692     /**
28693      * Customizable piece of the default paging text (defaults to "Page")
28694      * @type String
28695      */
28696     beforePageText : "Page",
28697     /**
28698      * Customizable piece of the default paging text (defaults to "of %0")
28699      * @type String
28700      */
28701     afterPageText : "of {0}",
28702     /**
28703      * Customizable piece of the default paging text (defaults to "First Page")
28704      * @type String
28705      */
28706     firstText : "First Page",
28707     /**
28708      * Customizable piece of the default paging text (defaults to "Previous Page")
28709      * @type String
28710      */
28711     prevText : "Previous Page",
28712     /**
28713      * Customizable piece of the default paging text (defaults to "Next Page")
28714      * @type String
28715      */
28716     nextText : "Next Page",
28717     /**
28718      * Customizable piece of the default paging text (defaults to "Last Page")
28719      * @type String
28720      */
28721     lastText : "Last Page",
28722     /**
28723      * Customizable piece of the default paging text (defaults to "Refresh")
28724      * @type String
28725      */
28726     refreshText : "Refresh",
28727
28728     // private
28729     renderButtons : function(el){
28730         Roo.PagingToolbar.superclass.render.call(this, el);
28731         this.first = this.addButton({
28732             tooltip: this.firstText,
28733             cls: "x-btn-icon x-grid-page-first",
28734             disabled: true,
28735             handler: this.onClick.createDelegate(this, ["first"])
28736         });
28737         this.prev = this.addButton({
28738             tooltip: this.prevText,
28739             cls: "x-btn-icon x-grid-page-prev",
28740             disabled: true,
28741             handler: this.onClick.createDelegate(this, ["prev"])
28742         });
28743         //this.addSeparator();
28744         this.add(this.beforePageText);
28745         this.field = Roo.get(this.addDom({
28746            tag: "input",
28747            type: "text",
28748            size: "3",
28749            value: "1",
28750            cls: "x-grid-page-number"
28751         }).el);
28752         this.field.on("keydown", this.onPagingKeydown, this);
28753         this.field.on("focus", function(){this.dom.select();});
28754         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28755         this.field.setHeight(18);
28756         //this.addSeparator();
28757         this.next = this.addButton({
28758             tooltip: this.nextText,
28759             cls: "x-btn-icon x-grid-page-next",
28760             disabled: true,
28761             handler: this.onClick.createDelegate(this, ["next"])
28762         });
28763         this.last = this.addButton({
28764             tooltip: this.lastText,
28765             cls: "x-btn-icon x-grid-page-last",
28766             disabled: true,
28767             handler: this.onClick.createDelegate(this, ["last"])
28768         });
28769         //this.addSeparator();
28770         this.loading = this.addButton({
28771             tooltip: this.refreshText,
28772             cls: "x-btn-icon x-grid-loading",
28773             handler: this.onClick.createDelegate(this, ["refresh"])
28774         });
28775
28776         if(this.displayInfo){
28777             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28778         }
28779     },
28780
28781     // private
28782     updateInfo : function(){
28783         if(this.displayEl){
28784             var count = this.ds.getCount();
28785             var msg = count == 0 ?
28786                 this.emptyMsg :
28787                 String.format(
28788                     this.displayMsg,
28789                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28790                 );
28791             this.displayEl.update(msg);
28792         }
28793     },
28794
28795     // private
28796     onLoad : function(ds, r, o){
28797        this.cursor = o.params ? o.params.start : 0;
28798        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28799
28800        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28801        this.field.dom.value = ap;
28802        this.first.setDisabled(ap == 1);
28803        this.prev.setDisabled(ap == 1);
28804        this.next.setDisabled(ap == ps);
28805        this.last.setDisabled(ap == ps);
28806        this.loading.enable();
28807        this.updateInfo();
28808     },
28809
28810     // private
28811     getPageData : function(){
28812         var total = this.ds.getTotalCount();
28813         return {
28814             total : total,
28815             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28816             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28817         };
28818     },
28819
28820     // private
28821     onLoadError : function(){
28822         this.loading.enable();
28823     },
28824
28825     // private
28826     onPagingKeydown : function(e){
28827         var k = e.getKey();
28828         var d = this.getPageData();
28829         if(k == e.RETURN){
28830             var v = this.field.dom.value, pageNum;
28831             if(!v || isNaN(pageNum = parseInt(v, 10))){
28832                 this.field.dom.value = d.activePage;
28833                 return;
28834             }
28835             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28836             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28837             e.stopEvent();
28838         }
28839         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))
28840         {
28841           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28842           this.field.dom.value = pageNum;
28843           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28844           e.stopEvent();
28845         }
28846         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28847         {
28848           var v = this.field.dom.value, pageNum; 
28849           var increment = (e.shiftKey) ? 10 : 1;
28850           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28851             increment *= -1;
28852           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28853             this.field.dom.value = d.activePage;
28854             return;
28855           }
28856           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28857           {
28858             this.field.dom.value = parseInt(v, 10) + increment;
28859             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28860             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28861           }
28862           e.stopEvent();
28863         }
28864     },
28865
28866     // private
28867     beforeLoad : function(){
28868         if(this.loading){
28869             this.loading.disable();
28870         }
28871     },
28872
28873     // private
28874     onClick : function(which){
28875         var ds = this.ds;
28876         switch(which){
28877             case "first":
28878                 ds.load({params:{start: 0, limit: this.pageSize}});
28879             break;
28880             case "prev":
28881                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28882             break;
28883             case "next":
28884                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28885             break;
28886             case "last":
28887                 var total = ds.getTotalCount();
28888                 var extra = total % this.pageSize;
28889                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28890                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28891             break;
28892             case "refresh":
28893                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28894             break;
28895         }
28896     },
28897
28898     /**
28899      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28900      * @param {Roo.data.Store} store The data store to unbind
28901      */
28902     unbind : function(ds){
28903         ds.un("beforeload", this.beforeLoad, this);
28904         ds.un("load", this.onLoad, this);
28905         ds.un("loadexception", this.onLoadError, this);
28906         ds.un("remove", this.updateInfo, this);
28907         ds.un("add", this.updateInfo, this);
28908         this.ds = undefined;
28909     },
28910
28911     /**
28912      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28913      * @param {Roo.data.Store} store The data store to bind
28914      */
28915     bind : function(ds){
28916         ds.on("beforeload", this.beforeLoad, this);
28917         ds.on("load", this.onLoad, this);
28918         ds.on("loadexception", this.onLoadError, this);
28919         ds.on("remove", this.updateInfo, this);
28920         ds.on("add", this.updateInfo, this);
28921         this.ds = ds;
28922     }
28923 });/*
28924  * Based on:
28925  * Ext JS Library 1.1.1
28926  * Copyright(c) 2006-2007, Ext JS, LLC.
28927  *
28928  * Originally Released Under LGPL - original licence link has changed is not relivant.
28929  *
28930  * Fork - LGPL
28931  * <script type="text/javascript">
28932  */
28933
28934 /**
28935  * @class Roo.Resizable
28936  * @extends Roo.util.Observable
28937  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28938  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28939  * 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
28940  * the element will be wrapped for you automatically.</p>
28941  * <p>Here is the list of valid resize handles:</p>
28942  * <pre>
28943 Value   Description
28944 ------  -------------------
28945  'n'     north
28946  's'     south
28947  'e'     east
28948  'w'     west
28949  'nw'    northwest
28950  'sw'    southwest
28951  'se'    southeast
28952  'ne'    northeast
28953  'hd'    horizontal drag
28954  'all'   all
28955 </pre>
28956  * <p>Here's an example showing the creation of a typical Resizable:</p>
28957  * <pre><code>
28958 var resizer = new Roo.Resizable("element-id", {
28959     handles: 'all',
28960     minWidth: 200,
28961     minHeight: 100,
28962     maxWidth: 500,
28963     maxHeight: 400,
28964     pinned: true
28965 });
28966 resizer.on("resize", myHandler);
28967 </code></pre>
28968  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28969  * resizer.east.setDisplayed(false);</p>
28970  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28971  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28972  * resize operation's new size (defaults to [0, 0])
28973  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28974  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28975  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28976  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28977  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28978  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28979  * @cfg {Number} width The width of the element in pixels (defaults to null)
28980  * @cfg {Number} height The height of the element in pixels (defaults to null)
28981  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28982  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28983  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28984  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28985  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28986  * in favor of the handles config option (defaults to false)
28987  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28988  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28989  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28990  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28991  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28992  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28993  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28994  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28995  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28996  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28997  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28998  * @constructor
28999  * Create a new resizable component
29000  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29001  * @param {Object} config configuration options
29002   */
29003 Roo.Resizable = function(el, config)
29004 {
29005     this.el = Roo.get(el);
29006
29007     if(config && config.wrap){
29008         config.resizeChild = this.el;
29009         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29010         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29011         this.el.setStyle("overflow", "hidden");
29012         this.el.setPositioning(config.resizeChild.getPositioning());
29013         config.resizeChild.clearPositioning();
29014         if(!config.width || !config.height){
29015             var csize = config.resizeChild.getSize();
29016             this.el.setSize(csize.width, csize.height);
29017         }
29018         if(config.pinned && !config.adjustments){
29019             config.adjustments = "auto";
29020         }
29021     }
29022
29023     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29024     this.proxy.unselectable();
29025     this.proxy.enableDisplayMode('block');
29026
29027     Roo.apply(this, config);
29028
29029     if(this.pinned){
29030         this.disableTrackOver = true;
29031         this.el.addClass("x-resizable-pinned");
29032     }
29033     // if the element isn't positioned, make it relative
29034     var position = this.el.getStyle("position");
29035     if(position != "absolute" && position != "fixed"){
29036         this.el.setStyle("position", "relative");
29037     }
29038     if(!this.handles){ // no handles passed, must be legacy style
29039         this.handles = 's,e,se';
29040         if(this.multiDirectional){
29041             this.handles += ',n,w';
29042         }
29043     }
29044     if(this.handles == "all"){
29045         this.handles = "n s e w ne nw se sw";
29046     }
29047     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29048     var ps = Roo.Resizable.positions;
29049     for(var i = 0, len = hs.length; i < len; i++){
29050         if(hs[i] && ps[hs[i]]){
29051             var pos = ps[hs[i]];
29052             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29053         }
29054     }
29055     // legacy
29056     this.corner = this.southeast;
29057     
29058     // updateBox = the box can move..
29059     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29060         this.updateBox = true;
29061     }
29062
29063     this.activeHandle = null;
29064
29065     if(this.resizeChild){
29066         if(typeof this.resizeChild == "boolean"){
29067             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29068         }else{
29069             this.resizeChild = Roo.get(this.resizeChild, true);
29070         }
29071     }
29072     
29073     if(this.adjustments == "auto"){
29074         var rc = this.resizeChild;
29075         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29076         if(rc && (hw || hn)){
29077             rc.position("relative");
29078             rc.setLeft(hw ? hw.el.getWidth() : 0);
29079             rc.setTop(hn ? hn.el.getHeight() : 0);
29080         }
29081         this.adjustments = [
29082             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29083             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29084         ];
29085     }
29086
29087     if(this.draggable){
29088         this.dd = this.dynamic ?
29089             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29090         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29091     }
29092
29093     // public events
29094     this.addEvents({
29095         /**
29096          * @event beforeresize
29097          * Fired before resize is allowed. Set enabled to false to cancel resize.
29098          * @param {Roo.Resizable} this
29099          * @param {Roo.EventObject} e The mousedown event
29100          */
29101         "beforeresize" : true,
29102         /**
29103          * @event resizing
29104          * Fired a resizing.
29105          * @param {Roo.Resizable} this
29106          * @param {Number} x The new x position
29107          * @param {Number} y The new y position
29108          * @param {Number} w The new w width
29109          * @param {Number} h The new h hight
29110          * @param {Roo.EventObject} e The mouseup event
29111          */
29112         "resizing" : true,
29113         /**
29114          * @event resize
29115          * Fired after a resize.
29116          * @param {Roo.Resizable} this
29117          * @param {Number} width The new width
29118          * @param {Number} height The new height
29119          * @param {Roo.EventObject} e The mouseup event
29120          */
29121         "resize" : true
29122     });
29123
29124     if(this.width !== null && this.height !== null){
29125         this.resizeTo(this.width, this.height);
29126     }else{
29127         this.updateChildSize();
29128     }
29129     if(Roo.isIE){
29130         this.el.dom.style.zoom = 1;
29131     }
29132     Roo.Resizable.superclass.constructor.call(this);
29133 };
29134
29135 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29136         resizeChild : false,
29137         adjustments : [0, 0],
29138         minWidth : 5,
29139         minHeight : 5,
29140         maxWidth : 10000,
29141         maxHeight : 10000,
29142         enabled : true,
29143         animate : false,
29144         duration : .35,
29145         dynamic : false,
29146         handles : false,
29147         multiDirectional : false,
29148         disableTrackOver : false,
29149         easing : 'easeOutStrong',
29150         widthIncrement : 0,
29151         heightIncrement : 0,
29152         pinned : false,
29153         width : null,
29154         height : null,
29155         preserveRatio : false,
29156         transparent: false,
29157         minX: 0,
29158         minY: 0,
29159         draggable: false,
29160
29161         /**
29162          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29163          */
29164         constrainTo: undefined,
29165         /**
29166          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29167          */
29168         resizeRegion: undefined,
29169
29170
29171     /**
29172      * Perform a manual resize
29173      * @param {Number} width
29174      * @param {Number} height
29175      */
29176     resizeTo : function(width, height){
29177         this.el.setSize(width, height);
29178         this.updateChildSize();
29179         this.fireEvent("resize", this, width, height, null);
29180     },
29181
29182     // private
29183     startSizing : function(e, handle){
29184         this.fireEvent("beforeresize", this, e);
29185         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29186
29187             if(!this.overlay){
29188                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29189                 this.overlay.unselectable();
29190                 this.overlay.enableDisplayMode("block");
29191                 this.overlay.on("mousemove", this.onMouseMove, this);
29192                 this.overlay.on("mouseup", this.onMouseUp, this);
29193             }
29194             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29195
29196             this.resizing = true;
29197             this.startBox = this.el.getBox();
29198             this.startPoint = e.getXY();
29199             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29200                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29201
29202             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29203             this.overlay.show();
29204
29205             if(this.constrainTo) {
29206                 var ct = Roo.get(this.constrainTo);
29207                 this.resizeRegion = ct.getRegion().adjust(
29208                     ct.getFrameWidth('t'),
29209                     ct.getFrameWidth('l'),
29210                     -ct.getFrameWidth('b'),
29211                     -ct.getFrameWidth('r')
29212                 );
29213             }
29214
29215             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29216             this.proxy.show();
29217             this.proxy.setBox(this.startBox);
29218             if(!this.dynamic){
29219                 this.proxy.setStyle('visibility', 'visible');
29220             }
29221         }
29222     },
29223
29224     // private
29225     onMouseDown : function(handle, e){
29226         if(this.enabled){
29227             e.stopEvent();
29228             this.activeHandle = handle;
29229             this.startSizing(e, handle);
29230         }
29231     },
29232
29233     // private
29234     onMouseUp : function(e){
29235         var size = this.resizeElement();
29236         this.resizing = false;
29237         this.handleOut();
29238         this.overlay.hide();
29239         this.proxy.hide();
29240         this.fireEvent("resize", this, size.width, size.height, e);
29241     },
29242
29243     // private
29244     updateChildSize : function(){
29245         
29246         if(this.resizeChild){
29247             var el = this.el;
29248             var child = this.resizeChild;
29249             var adj = this.adjustments;
29250             if(el.dom.offsetWidth){
29251                 var b = el.getSize(true);
29252                 child.setSize(b.width+adj[0], b.height+adj[1]);
29253             }
29254             // Second call here for IE
29255             // The first call enables instant resizing and
29256             // the second call corrects scroll bars if they
29257             // exist
29258             if(Roo.isIE){
29259                 setTimeout(function(){
29260                     if(el.dom.offsetWidth){
29261                         var b = el.getSize(true);
29262                         child.setSize(b.width+adj[0], b.height+adj[1]);
29263                     }
29264                 }, 10);
29265             }
29266         }
29267     },
29268
29269     // private
29270     snap : function(value, inc, min){
29271         if(!inc || !value) return value;
29272         var newValue = value;
29273         var m = value % inc;
29274         if(m > 0){
29275             if(m > (inc/2)){
29276                 newValue = value + (inc-m);
29277             }else{
29278                 newValue = value - m;
29279             }
29280         }
29281         return Math.max(min, newValue);
29282     },
29283
29284     // private
29285     resizeElement : function(){
29286         var box = this.proxy.getBox();
29287         if(this.updateBox){
29288             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29289         }else{
29290             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29291         }
29292         this.updateChildSize();
29293         if(!this.dynamic){
29294             this.proxy.hide();
29295         }
29296         return box;
29297     },
29298
29299     // private
29300     constrain : function(v, diff, m, mx){
29301         if(v - diff < m){
29302             diff = v - m;
29303         }else if(v - diff > mx){
29304             diff = mx - v;
29305         }
29306         return diff;
29307     },
29308
29309     // private
29310     onMouseMove : function(e){
29311         
29312         if(this.enabled){
29313             try{// try catch so if something goes wrong the user doesn't get hung
29314
29315             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29316                 return;
29317             }
29318
29319             //var curXY = this.startPoint;
29320             var curSize = this.curSize || this.startBox;
29321             var x = this.startBox.x, y = this.startBox.y;
29322             var ox = x, oy = y;
29323             var w = curSize.width, h = curSize.height;
29324             var ow = w, oh = h;
29325             var mw = this.minWidth, mh = this.minHeight;
29326             var mxw = this.maxWidth, mxh = this.maxHeight;
29327             var wi = this.widthIncrement;
29328             var hi = this.heightIncrement;
29329
29330             var eventXY = e.getXY();
29331             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29332             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29333
29334             var pos = this.activeHandle.position;
29335
29336             switch(pos){
29337                 case "east":
29338                     w += diffX;
29339                     w = Math.min(Math.max(mw, w), mxw);
29340                     break;
29341              
29342                 case "south":
29343                     h += diffY;
29344                     h = Math.min(Math.max(mh, h), mxh);
29345                     break;
29346                 case "southeast":
29347                     w += diffX;
29348                     h += diffY;
29349                     w = Math.min(Math.max(mw, w), mxw);
29350                     h = Math.min(Math.max(mh, h), mxh);
29351                     break;
29352                 case "north":
29353                     diffY = this.constrain(h, diffY, mh, mxh);
29354                     y += diffY;
29355                     h -= diffY;
29356                     break;
29357                 case "hdrag":
29358                     
29359                     if (wi) {
29360                         var adiffX = Math.abs(diffX);
29361                         var sub = (adiffX % wi); // how much 
29362                         if (sub > (wi/2)) { // far enough to snap
29363                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29364                         } else {
29365                             // remove difference.. 
29366                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29367                         }
29368                     }
29369                     x += diffX;
29370                     x = Math.max(this.minX, x);
29371                     break;
29372                 case "west":
29373                     diffX = this.constrain(w, diffX, mw, mxw);
29374                     x += diffX;
29375                     w -= diffX;
29376                     break;
29377                 case "northeast":
29378                     w += diffX;
29379                     w = Math.min(Math.max(mw, w), mxw);
29380                     diffY = this.constrain(h, diffY, mh, mxh);
29381                     y += diffY;
29382                     h -= diffY;
29383                     break;
29384                 case "northwest":
29385                     diffX = this.constrain(w, diffX, mw, mxw);
29386                     diffY = this.constrain(h, diffY, mh, mxh);
29387                     y += diffY;
29388                     h -= diffY;
29389                     x += diffX;
29390                     w -= diffX;
29391                     break;
29392                case "southwest":
29393                     diffX = this.constrain(w, diffX, mw, mxw);
29394                     h += diffY;
29395                     h = Math.min(Math.max(mh, h), mxh);
29396                     x += diffX;
29397                     w -= diffX;
29398                     break;
29399             }
29400
29401             var sw = this.snap(w, wi, mw);
29402             var sh = this.snap(h, hi, mh);
29403             if(sw != w || sh != h){
29404                 switch(pos){
29405                     case "northeast":
29406                         y -= sh - h;
29407                     break;
29408                     case "north":
29409                         y -= sh - h;
29410                         break;
29411                     case "southwest":
29412                         x -= sw - w;
29413                     break;
29414                     case "west":
29415                         x -= sw - w;
29416                         break;
29417                     case "northwest":
29418                         x -= sw - w;
29419                         y -= sh - h;
29420                     break;
29421                 }
29422                 w = sw;
29423                 h = sh;
29424             }
29425
29426             if(this.preserveRatio){
29427                 switch(pos){
29428                     case "southeast":
29429                     case "east":
29430                         h = oh * (w/ow);
29431                         h = Math.min(Math.max(mh, h), mxh);
29432                         w = ow * (h/oh);
29433                        break;
29434                     case "south":
29435                         w = ow * (h/oh);
29436                         w = Math.min(Math.max(mw, w), mxw);
29437                         h = oh * (w/ow);
29438                         break;
29439                     case "northeast":
29440                         w = ow * (h/oh);
29441                         w = Math.min(Math.max(mw, w), mxw);
29442                         h = oh * (w/ow);
29443                     break;
29444                     case "north":
29445                         var tw = w;
29446                         w = ow * (h/oh);
29447                         w = Math.min(Math.max(mw, w), mxw);
29448                         h = oh * (w/ow);
29449                         x += (tw - w) / 2;
29450                         break;
29451                     case "southwest":
29452                         h = oh * (w/ow);
29453                         h = Math.min(Math.max(mh, h), mxh);
29454                         var tw = w;
29455                         w = ow * (h/oh);
29456                         x += tw - w;
29457                         break;
29458                     case "west":
29459                         var th = h;
29460                         h = oh * (w/ow);
29461                         h = Math.min(Math.max(mh, h), mxh);
29462                         y += (th - h) / 2;
29463                         var tw = w;
29464                         w = ow * (h/oh);
29465                         x += tw - w;
29466                        break;
29467                     case "northwest":
29468                         var tw = w;
29469                         var th = h;
29470                         h = oh * (w/ow);
29471                         h = Math.min(Math.max(mh, h), mxh);
29472                         w = ow * (h/oh);
29473                         y += th - h;
29474                         x += tw - w;
29475                        break;
29476
29477                 }
29478             }
29479             if (pos == 'hdrag') {
29480                 w = ow;
29481             }
29482             this.proxy.setBounds(x, y, w, h);
29483             if(this.dynamic){
29484                 this.resizeElement();
29485             }
29486             }catch(e){}
29487         }
29488         this.fireEvent("resizing", this, x, y, w, h, e);
29489     },
29490
29491     // private
29492     handleOver : function(){
29493         if(this.enabled){
29494             this.el.addClass("x-resizable-over");
29495         }
29496     },
29497
29498     // private
29499     handleOut : function(){
29500         if(!this.resizing){
29501             this.el.removeClass("x-resizable-over");
29502         }
29503     },
29504
29505     /**
29506      * Returns the element this component is bound to.
29507      * @return {Roo.Element}
29508      */
29509     getEl : function(){
29510         return this.el;
29511     },
29512
29513     /**
29514      * Returns the resizeChild element (or null).
29515      * @return {Roo.Element}
29516      */
29517     getResizeChild : function(){
29518         return this.resizeChild;
29519     },
29520     groupHandler : function()
29521     {
29522         
29523     },
29524     /**
29525      * Destroys this resizable. If the element was wrapped and
29526      * removeEl is not true then the element remains.
29527      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29528      */
29529     destroy : function(removeEl){
29530         this.proxy.remove();
29531         if(this.overlay){
29532             this.overlay.removeAllListeners();
29533             this.overlay.remove();
29534         }
29535         var ps = Roo.Resizable.positions;
29536         for(var k in ps){
29537             if(typeof ps[k] != "function" && this[ps[k]]){
29538                 var h = this[ps[k]];
29539                 h.el.removeAllListeners();
29540                 h.el.remove();
29541             }
29542         }
29543         if(removeEl){
29544             this.el.update("");
29545             this.el.remove();
29546         }
29547     }
29548 });
29549
29550 // private
29551 // hash to map config positions to true positions
29552 Roo.Resizable.positions = {
29553     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29554     hd: "hdrag"
29555 };
29556
29557 // private
29558 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29559     if(!this.tpl){
29560         // only initialize the template if resizable is used
29561         var tpl = Roo.DomHelper.createTemplate(
29562             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29563         );
29564         tpl.compile();
29565         Roo.Resizable.Handle.prototype.tpl = tpl;
29566     }
29567     this.position = pos;
29568     this.rz = rz;
29569     // show north drag fro topdra
29570     var handlepos = pos == 'hdrag' ? 'north' : pos;
29571     
29572     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29573     if (pos == 'hdrag') {
29574         this.el.setStyle('cursor', 'pointer');
29575     }
29576     this.el.unselectable();
29577     if(transparent){
29578         this.el.setOpacity(0);
29579     }
29580     this.el.on("mousedown", this.onMouseDown, this);
29581     if(!disableTrackOver){
29582         this.el.on("mouseover", this.onMouseOver, this);
29583         this.el.on("mouseout", this.onMouseOut, this);
29584     }
29585 };
29586
29587 // private
29588 Roo.Resizable.Handle.prototype = {
29589     afterResize : function(rz){
29590         Roo.log('after?');
29591         // do nothing
29592     },
29593     // private
29594     onMouseDown : function(e){
29595         this.rz.onMouseDown(this, e);
29596     },
29597     // private
29598     onMouseOver : function(e){
29599         this.rz.handleOver(this, e);
29600     },
29601     // private
29602     onMouseOut : function(e){
29603         this.rz.handleOut(this, e);
29604     }
29605 };/*
29606  * Based on:
29607  * Ext JS Library 1.1.1
29608  * Copyright(c) 2006-2007, Ext JS, LLC.
29609  *
29610  * Originally Released Under LGPL - original licence link has changed is not relivant.
29611  *
29612  * Fork - LGPL
29613  * <script type="text/javascript">
29614  */
29615
29616 /**
29617  * @class Roo.Editor
29618  * @extends Roo.Component
29619  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29620  * @constructor
29621  * Create a new Editor
29622  * @param {Roo.form.Field} field The Field object (or descendant)
29623  * @param {Object} config The config object
29624  */
29625 Roo.Editor = function(field, config){
29626     Roo.Editor.superclass.constructor.call(this, config);
29627     this.field = field;
29628     this.addEvents({
29629         /**
29630              * @event beforestartedit
29631              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29632              * false from the handler of this event.
29633              * @param {Editor} this
29634              * @param {Roo.Element} boundEl The underlying element bound to this editor
29635              * @param {Mixed} value The field value being set
29636              */
29637         "beforestartedit" : true,
29638         /**
29639              * @event startedit
29640              * Fires when this editor is displayed
29641              * @param {Roo.Element} boundEl The underlying element bound to this editor
29642              * @param {Mixed} value The starting field value
29643              */
29644         "startedit" : true,
29645         /**
29646              * @event beforecomplete
29647              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29648              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29649              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29650              * event will not fire since no edit actually occurred.
29651              * @param {Editor} this
29652              * @param {Mixed} value The current field value
29653              * @param {Mixed} startValue The original field value
29654              */
29655         "beforecomplete" : true,
29656         /**
29657              * @event complete
29658              * Fires after editing is complete and any changed value has been written to the underlying field.
29659              * @param {Editor} this
29660              * @param {Mixed} value The current field value
29661              * @param {Mixed} startValue The original field value
29662              */
29663         "complete" : true,
29664         /**
29665          * @event specialkey
29666          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29667          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29668          * @param {Roo.form.Field} this
29669          * @param {Roo.EventObject} e The event object
29670          */
29671         "specialkey" : true
29672     });
29673 };
29674
29675 Roo.extend(Roo.Editor, Roo.Component, {
29676     /**
29677      * @cfg {Boolean/String} autosize
29678      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29679      * or "height" to adopt the height only (defaults to false)
29680      */
29681     /**
29682      * @cfg {Boolean} revertInvalid
29683      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29684      * validation fails (defaults to true)
29685      */
29686     /**
29687      * @cfg {Boolean} ignoreNoChange
29688      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29689      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29690      * will never be ignored.
29691      */
29692     /**
29693      * @cfg {Boolean} hideEl
29694      * False to keep the bound element visible while the editor is displayed (defaults to true)
29695      */
29696     /**
29697      * @cfg {Mixed} value
29698      * The data value of the underlying field (defaults to "")
29699      */
29700     value : "",
29701     /**
29702      * @cfg {String} alignment
29703      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29704      */
29705     alignment: "c-c?",
29706     /**
29707      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29708      * for bottom-right shadow (defaults to "frame")
29709      */
29710     shadow : "frame",
29711     /**
29712      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29713      */
29714     constrain : false,
29715     /**
29716      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29717      */
29718     completeOnEnter : false,
29719     /**
29720      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29721      */
29722     cancelOnEsc : false,
29723     /**
29724      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29725      */
29726     updateEl : false,
29727
29728     // private
29729     onRender : function(ct, position){
29730         this.el = new Roo.Layer({
29731             shadow: this.shadow,
29732             cls: "x-editor",
29733             parentEl : ct,
29734             shim : this.shim,
29735             shadowOffset:4,
29736             id: this.id,
29737             constrain: this.constrain
29738         });
29739         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29740         if(this.field.msgTarget != 'title'){
29741             this.field.msgTarget = 'qtip';
29742         }
29743         this.field.render(this.el);
29744         if(Roo.isGecko){
29745             this.field.el.dom.setAttribute('autocomplete', 'off');
29746         }
29747         this.field.on("specialkey", this.onSpecialKey, this);
29748         if(this.swallowKeys){
29749             this.field.el.swallowEvent(['keydown','keypress']);
29750         }
29751         this.field.show();
29752         this.field.on("blur", this.onBlur, this);
29753         if(this.field.grow){
29754             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29755         }
29756     },
29757
29758     onSpecialKey : function(field, e)
29759     {
29760         //Roo.log('editor onSpecialKey');
29761         if(this.completeOnEnter && e.getKey() == e.ENTER){
29762             e.stopEvent();
29763             this.completeEdit();
29764             return;
29765         }
29766         // do not fire special key otherwise it might hide close the editor...
29767         if(e.getKey() == e.ENTER){    
29768             return;
29769         }
29770         if(this.cancelOnEsc && e.getKey() == e.ESC){
29771             this.cancelEdit();
29772             return;
29773         } 
29774         this.fireEvent('specialkey', field, e);
29775     
29776     },
29777
29778     /**
29779      * Starts the editing process and shows the editor.
29780      * @param {String/HTMLElement/Element} el The element to edit
29781      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29782       * to the innerHTML of el.
29783      */
29784     startEdit : function(el, value){
29785         if(this.editing){
29786             this.completeEdit();
29787         }
29788         this.boundEl = Roo.get(el);
29789         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29790         if(!this.rendered){
29791             this.render(this.parentEl || document.body);
29792         }
29793         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29794             return;
29795         }
29796         this.startValue = v;
29797         this.field.setValue(v);
29798         if(this.autoSize){
29799             var sz = this.boundEl.getSize();
29800             switch(this.autoSize){
29801                 case "width":
29802                 this.setSize(sz.width,  "");
29803                 break;
29804                 case "height":
29805                 this.setSize("",  sz.height);
29806                 break;
29807                 default:
29808                 this.setSize(sz.width,  sz.height);
29809             }
29810         }
29811         this.el.alignTo(this.boundEl, this.alignment);
29812         this.editing = true;
29813         if(Roo.QuickTips){
29814             Roo.QuickTips.disable();
29815         }
29816         this.show();
29817     },
29818
29819     /**
29820      * Sets the height and width of this editor.
29821      * @param {Number} width The new width
29822      * @param {Number} height The new height
29823      */
29824     setSize : function(w, h){
29825         this.field.setSize(w, h);
29826         if(this.el){
29827             this.el.sync();
29828         }
29829     },
29830
29831     /**
29832      * Realigns the editor to the bound field based on the current alignment config value.
29833      */
29834     realign : function(){
29835         this.el.alignTo(this.boundEl, this.alignment);
29836     },
29837
29838     /**
29839      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29840      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29841      */
29842     completeEdit : function(remainVisible){
29843         if(!this.editing){
29844             return;
29845         }
29846         var v = this.getValue();
29847         if(this.revertInvalid !== false && !this.field.isValid()){
29848             v = this.startValue;
29849             this.cancelEdit(true);
29850         }
29851         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29852             this.editing = false;
29853             this.hide();
29854             return;
29855         }
29856         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29857             this.editing = false;
29858             if(this.updateEl && this.boundEl){
29859                 this.boundEl.update(v);
29860             }
29861             if(remainVisible !== true){
29862                 this.hide();
29863             }
29864             this.fireEvent("complete", this, v, this.startValue);
29865         }
29866     },
29867
29868     // private
29869     onShow : function(){
29870         this.el.show();
29871         if(this.hideEl !== false){
29872             this.boundEl.hide();
29873         }
29874         this.field.show();
29875         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29876             this.fixIEFocus = true;
29877             this.deferredFocus.defer(50, this);
29878         }else{
29879             this.field.focus();
29880         }
29881         this.fireEvent("startedit", this.boundEl, this.startValue);
29882     },
29883
29884     deferredFocus : function(){
29885         if(this.editing){
29886             this.field.focus();
29887         }
29888     },
29889
29890     /**
29891      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29892      * reverted to the original starting value.
29893      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29894      * cancel (defaults to false)
29895      */
29896     cancelEdit : function(remainVisible){
29897         if(this.editing){
29898             this.setValue(this.startValue);
29899             if(remainVisible !== true){
29900                 this.hide();
29901             }
29902         }
29903     },
29904
29905     // private
29906     onBlur : function(){
29907         if(this.allowBlur !== true && this.editing){
29908             this.completeEdit();
29909         }
29910     },
29911
29912     // private
29913     onHide : function(){
29914         if(this.editing){
29915             this.completeEdit();
29916             return;
29917         }
29918         this.field.blur();
29919         if(this.field.collapse){
29920             this.field.collapse();
29921         }
29922         this.el.hide();
29923         if(this.hideEl !== false){
29924             this.boundEl.show();
29925         }
29926         if(Roo.QuickTips){
29927             Roo.QuickTips.enable();
29928         }
29929     },
29930
29931     /**
29932      * Sets the data value of the editor
29933      * @param {Mixed} value Any valid value supported by the underlying field
29934      */
29935     setValue : function(v){
29936         this.field.setValue(v);
29937     },
29938
29939     /**
29940      * Gets the data value of the editor
29941      * @return {Mixed} The data value
29942      */
29943     getValue : function(){
29944         return this.field.getValue();
29945     }
29946 });/*
29947  * Based on:
29948  * Ext JS Library 1.1.1
29949  * Copyright(c) 2006-2007, Ext JS, LLC.
29950  *
29951  * Originally Released Under LGPL - original licence link has changed is not relivant.
29952  *
29953  * Fork - LGPL
29954  * <script type="text/javascript">
29955  */
29956  
29957 /**
29958  * @class Roo.BasicDialog
29959  * @extends Roo.util.Observable
29960  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29961  * <pre><code>
29962 var dlg = new Roo.BasicDialog("my-dlg", {
29963     height: 200,
29964     width: 300,
29965     minHeight: 100,
29966     minWidth: 150,
29967     modal: true,
29968     proxyDrag: true,
29969     shadow: true
29970 });
29971 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29972 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29973 dlg.addButton('Cancel', dlg.hide, dlg);
29974 dlg.show();
29975 </code></pre>
29976   <b>A Dialog should always be a direct child of the body element.</b>
29977  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29978  * @cfg {String} title Default text to display in the title bar (defaults to null)
29979  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29980  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29981  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29982  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29983  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29984  * (defaults to null with no animation)
29985  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29986  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29987  * property for valid values (defaults to 'all')
29988  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29989  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29990  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29991  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29992  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29993  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29994  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29995  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29996  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29997  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29998  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29999  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30000  * draggable = true (defaults to false)
30001  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30002  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30003  * shadow (defaults to false)
30004  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30005  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30006  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30007  * @cfg {Array} buttons Array of buttons
30008  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30009  * @constructor
30010  * Create a new BasicDialog.
30011  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30012  * @param {Object} config Configuration options
30013  */
30014 Roo.BasicDialog = function(el, config){
30015     this.el = Roo.get(el);
30016     var dh = Roo.DomHelper;
30017     if(!this.el && config && config.autoCreate){
30018         if(typeof config.autoCreate == "object"){
30019             if(!config.autoCreate.id){
30020                 config.autoCreate.id = el;
30021             }
30022             this.el = dh.append(document.body,
30023                         config.autoCreate, true);
30024         }else{
30025             this.el = dh.append(document.body,
30026                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30027         }
30028     }
30029     el = this.el;
30030     el.setDisplayed(true);
30031     el.hide = this.hideAction;
30032     this.id = el.id;
30033     el.addClass("x-dlg");
30034
30035     Roo.apply(this, config);
30036
30037     this.proxy = el.createProxy("x-dlg-proxy");
30038     this.proxy.hide = this.hideAction;
30039     this.proxy.setOpacity(.5);
30040     this.proxy.hide();
30041
30042     if(config.width){
30043         el.setWidth(config.width);
30044     }
30045     if(config.height){
30046         el.setHeight(config.height);
30047     }
30048     this.size = el.getSize();
30049     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30050         this.xy = [config.x,config.y];
30051     }else{
30052         this.xy = el.getCenterXY(true);
30053     }
30054     /** The header element @type Roo.Element */
30055     this.header = el.child("> .x-dlg-hd");
30056     /** The body element @type Roo.Element */
30057     this.body = el.child("> .x-dlg-bd");
30058     /** The footer element @type Roo.Element */
30059     this.footer = el.child("> .x-dlg-ft");
30060
30061     if(!this.header){
30062         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30063     }
30064     if(!this.body){
30065         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30066     }
30067
30068     this.header.unselectable();
30069     if(this.title){
30070         this.header.update(this.title);
30071     }
30072     // this element allows the dialog to be focused for keyboard event
30073     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30074     this.focusEl.swallowEvent("click", true);
30075
30076     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30077
30078     // wrap the body and footer for special rendering
30079     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30080     if(this.footer){
30081         this.bwrap.dom.appendChild(this.footer.dom);
30082     }
30083
30084     this.bg = this.el.createChild({
30085         tag: "div", cls:"x-dlg-bg",
30086         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30087     });
30088     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30089
30090
30091     if(this.autoScroll !== false && !this.autoTabs){
30092         this.body.setStyle("overflow", "auto");
30093     }
30094
30095     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30096
30097     if(this.closable !== false){
30098         this.el.addClass("x-dlg-closable");
30099         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30100         this.close.on("click", this.closeClick, this);
30101         this.close.addClassOnOver("x-dlg-close-over");
30102     }
30103     if(this.collapsible !== false){
30104         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30105         this.collapseBtn.on("click", this.collapseClick, this);
30106         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30107         this.header.on("dblclick", this.collapseClick, this);
30108     }
30109     if(this.resizable !== false){
30110         this.el.addClass("x-dlg-resizable");
30111         this.resizer = new Roo.Resizable(el, {
30112             minWidth: this.minWidth || 80,
30113             minHeight:this.minHeight || 80,
30114             handles: this.resizeHandles || "all",
30115             pinned: true
30116         });
30117         this.resizer.on("beforeresize", this.beforeResize, this);
30118         this.resizer.on("resize", this.onResize, this);
30119     }
30120     if(this.draggable !== false){
30121         el.addClass("x-dlg-draggable");
30122         if (!this.proxyDrag) {
30123             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30124         }
30125         else {
30126             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30127         }
30128         dd.setHandleElId(this.header.id);
30129         dd.endDrag = this.endMove.createDelegate(this);
30130         dd.startDrag = this.startMove.createDelegate(this);
30131         dd.onDrag = this.onDrag.createDelegate(this);
30132         dd.scroll = false;
30133         this.dd = dd;
30134     }
30135     if(this.modal){
30136         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30137         this.mask.enableDisplayMode("block");
30138         this.mask.hide();
30139         this.el.addClass("x-dlg-modal");
30140     }
30141     if(this.shadow){
30142         this.shadow = new Roo.Shadow({
30143             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30144             offset : this.shadowOffset
30145         });
30146     }else{
30147         this.shadowOffset = 0;
30148     }
30149     if(Roo.useShims && this.shim !== false){
30150         this.shim = this.el.createShim();
30151         this.shim.hide = this.hideAction;
30152         this.shim.hide();
30153     }else{
30154         this.shim = false;
30155     }
30156     if(this.autoTabs){
30157         this.initTabs();
30158     }
30159     if (this.buttons) { 
30160         var bts= this.buttons;
30161         this.buttons = [];
30162         Roo.each(bts, function(b) {
30163             this.addButton(b);
30164         }, this);
30165     }
30166     
30167     
30168     this.addEvents({
30169         /**
30170          * @event keydown
30171          * Fires when a key is pressed
30172          * @param {Roo.BasicDialog} this
30173          * @param {Roo.EventObject} e
30174          */
30175         "keydown" : true,
30176         /**
30177          * @event move
30178          * Fires when this dialog is moved by the user.
30179          * @param {Roo.BasicDialog} this
30180          * @param {Number} x The new page X
30181          * @param {Number} y The new page Y
30182          */
30183         "move" : true,
30184         /**
30185          * @event resize
30186          * Fires when this dialog is resized by the user.
30187          * @param {Roo.BasicDialog} this
30188          * @param {Number} width The new width
30189          * @param {Number} height The new height
30190          */
30191         "resize" : true,
30192         /**
30193          * @event beforehide
30194          * Fires before this dialog is hidden.
30195          * @param {Roo.BasicDialog} this
30196          */
30197         "beforehide" : true,
30198         /**
30199          * @event hide
30200          * Fires when this dialog is hidden.
30201          * @param {Roo.BasicDialog} this
30202          */
30203         "hide" : true,
30204         /**
30205          * @event beforeshow
30206          * Fires before this dialog is shown.
30207          * @param {Roo.BasicDialog} this
30208          */
30209         "beforeshow" : true,
30210         /**
30211          * @event show
30212          * Fires when this dialog is shown.
30213          * @param {Roo.BasicDialog} this
30214          */
30215         "show" : true
30216     });
30217     el.on("keydown", this.onKeyDown, this);
30218     el.on("mousedown", this.toFront, this);
30219     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30220     this.el.hide();
30221     Roo.DialogManager.register(this);
30222     Roo.BasicDialog.superclass.constructor.call(this);
30223 };
30224
30225 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30226     shadowOffset: Roo.isIE ? 6 : 5,
30227     minHeight: 80,
30228     minWidth: 200,
30229     minButtonWidth: 75,
30230     defaultButton: null,
30231     buttonAlign: "right",
30232     tabTag: 'div',
30233     firstShow: true,
30234
30235     /**
30236      * Sets the dialog title text
30237      * @param {String} text The title text to display
30238      * @return {Roo.BasicDialog} this
30239      */
30240     setTitle : function(text){
30241         this.header.update(text);
30242         return this;
30243     },
30244
30245     // private
30246     closeClick : function(){
30247         this.hide();
30248     },
30249
30250     // private
30251     collapseClick : function(){
30252         this[this.collapsed ? "expand" : "collapse"]();
30253     },
30254
30255     /**
30256      * Collapses the dialog to its minimized state (only the title bar is visible).
30257      * Equivalent to the user clicking the collapse dialog button.
30258      */
30259     collapse : function(){
30260         if(!this.collapsed){
30261             this.collapsed = true;
30262             this.el.addClass("x-dlg-collapsed");
30263             this.restoreHeight = this.el.getHeight();
30264             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30265         }
30266     },
30267
30268     /**
30269      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30270      * clicking the expand dialog button.
30271      */
30272     expand : function(){
30273         if(this.collapsed){
30274             this.collapsed = false;
30275             this.el.removeClass("x-dlg-collapsed");
30276             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30277         }
30278     },
30279
30280     /**
30281      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30282      * @return {Roo.TabPanel} The tabs component
30283      */
30284     initTabs : function(){
30285         var tabs = this.getTabs();
30286         while(tabs.getTab(0)){
30287             tabs.removeTab(0);
30288         }
30289         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30290             var dom = el.dom;
30291             tabs.addTab(Roo.id(dom), dom.title);
30292             dom.title = "";
30293         });
30294         tabs.activate(0);
30295         return tabs;
30296     },
30297
30298     // private
30299     beforeResize : function(){
30300         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30301     },
30302
30303     // private
30304     onResize : function(){
30305         this.refreshSize();
30306         this.syncBodyHeight();
30307         this.adjustAssets();
30308         this.focus();
30309         this.fireEvent("resize", this, this.size.width, this.size.height);
30310     },
30311
30312     // private
30313     onKeyDown : function(e){
30314         if(this.isVisible()){
30315             this.fireEvent("keydown", this, e);
30316         }
30317     },
30318
30319     /**
30320      * Resizes the dialog.
30321      * @param {Number} width
30322      * @param {Number} height
30323      * @return {Roo.BasicDialog} this
30324      */
30325     resizeTo : function(width, height){
30326         this.el.setSize(width, height);
30327         this.size = {width: width, height: height};
30328         this.syncBodyHeight();
30329         if(this.fixedcenter){
30330             this.center();
30331         }
30332         if(this.isVisible()){
30333             this.constrainXY();
30334             this.adjustAssets();
30335         }
30336         this.fireEvent("resize", this, width, height);
30337         return this;
30338     },
30339
30340
30341     /**
30342      * Resizes the dialog to fit the specified content size.
30343      * @param {Number} width
30344      * @param {Number} height
30345      * @return {Roo.BasicDialog} this
30346      */
30347     setContentSize : function(w, h){
30348         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30349         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30350         //if(!this.el.isBorderBox()){
30351             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30352             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30353         //}
30354         if(this.tabs){
30355             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30356             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30357         }
30358         this.resizeTo(w, h);
30359         return this;
30360     },
30361
30362     /**
30363      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30364      * executed in response to a particular key being pressed while the dialog is active.
30365      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30366      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30367      * @param {Function} fn The function to call
30368      * @param {Object} scope (optional) The scope of the function
30369      * @return {Roo.BasicDialog} this
30370      */
30371     addKeyListener : function(key, fn, scope){
30372         var keyCode, shift, ctrl, alt;
30373         if(typeof key == "object" && !(key instanceof Array)){
30374             keyCode = key["key"];
30375             shift = key["shift"];
30376             ctrl = key["ctrl"];
30377             alt = key["alt"];
30378         }else{
30379             keyCode = key;
30380         }
30381         var handler = function(dlg, e){
30382             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30383                 var k = e.getKey();
30384                 if(keyCode instanceof Array){
30385                     for(var i = 0, len = keyCode.length; i < len; i++){
30386                         if(keyCode[i] == k){
30387                           fn.call(scope || window, dlg, k, e);
30388                           return;
30389                         }
30390                     }
30391                 }else{
30392                     if(k == keyCode){
30393                         fn.call(scope || window, dlg, k, e);
30394                     }
30395                 }
30396             }
30397         };
30398         this.on("keydown", handler);
30399         return this;
30400     },
30401
30402     /**
30403      * Returns the TabPanel component (creates it if it doesn't exist).
30404      * Note: If you wish to simply check for the existence of tabs without creating them,
30405      * check for a null 'tabs' property.
30406      * @return {Roo.TabPanel} The tabs component
30407      */
30408     getTabs : function(){
30409         if(!this.tabs){
30410             this.el.addClass("x-dlg-auto-tabs");
30411             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30412             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30413         }
30414         return this.tabs;
30415     },
30416
30417     /**
30418      * Adds a button to the footer section of the dialog.
30419      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30420      * object or a valid Roo.DomHelper element config
30421      * @param {Function} handler The function called when the button is clicked
30422      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30423      * @return {Roo.Button} The new button
30424      */
30425     addButton : function(config, handler, scope){
30426         var dh = Roo.DomHelper;
30427         if(!this.footer){
30428             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30429         }
30430         if(!this.btnContainer){
30431             var tb = this.footer.createChild({
30432
30433                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30434                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30435             }, null, true);
30436             this.btnContainer = tb.firstChild.firstChild.firstChild;
30437         }
30438         var bconfig = {
30439             handler: handler,
30440             scope: scope,
30441             minWidth: this.minButtonWidth,
30442             hideParent:true
30443         };
30444         if(typeof config == "string"){
30445             bconfig.text = config;
30446         }else{
30447             if(config.tag){
30448                 bconfig.dhconfig = config;
30449             }else{
30450                 Roo.apply(bconfig, config);
30451             }
30452         }
30453         var fc = false;
30454         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30455             bconfig.position = Math.max(0, bconfig.position);
30456             fc = this.btnContainer.childNodes[bconfig.position];
30457         }
30458          
30459         var btn = new Roo.Button(
30460             fc ? 
30461                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30462                 : this.btnContainer.appendChild(document.createElement("td")),
30463             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30464             bconfig
30465         );
30466         this.syncBodyHeight();
30467         if(!this.buttons){
30468             /**
30469              * Array of all the buttons that have been added to this dialog via addButton
30470              * @type Array
30471              */
30472             this.buttons = [];
30473         }
30474         this.buttons.push(btn);
30475         return btn;
30476     },
30477
30478     /**
30479      * Sets the default button to be focused when the dialog is displayed.
30480      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30481      * @return {Roo.BasicDialog} this
30482      */
30483     setDefaultButton : function(btn){
30484         this.defaultButton = btn;
30485         return this;
30486     },
30487
30488     // private
30489     getHeaderFooterHeight : function(safe){
30490         var height = 0;
30491         if(this.header){
30492            height += this.header.getHeight();
30493         }
30494         if(this.footer){
30495            var fm = this.footer.getMargins();
30496             height += (this.footer.getHeight()+fm.top+fm.bottom);
30497         }
30498         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30499         height += this.centerBg.getPadding("tb");
30500         return height;
30501     },
30502
30503     // private
30504     syncBodyHeight : function()
30505     {
30506         var bd = this.body, // the text
30507             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30508             bw = this.bwrap;
30509         var height = this.size.height - this.getHeaderFooterHeight(false);
30510         bd.setHeight(height-bd.getMargins("tb"));
30511         var hh = this.header.getHeight();
30512         var h = this.size.height-hh;
30513         cb.setHeight(h);
30514         
30515         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30516         bw.setHeight(h-cb.getPadding("tb"));
30517         
30518         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30519         bd.setWidth(bw.getWidth(true));
30520         if(this.tabs){
30521             this.tabs.syncHeight();
30522             if(Roo.isIE){
30523                 this.tabs.el.repaint();
30524             }
30525         }
30526     },
30527
30528     /**
30529      * Restores the previous state of the dialog if Roo.state is configured.
30530      * @return {Roo.BasicDialog} this
30531      */
30532     restoreState : function(){
30533         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30534         if(box && box.width){
30535             this.xy = [box.x, box.y];
30536             this.resizeTo(box.width, box.height);
30537         }
30538         return this;
30539     },
30540
30541     // private
30542     beforeShow : function(){
30543         this.expand();
30544         if(this.fixedcenter){
30545             this.xy = this.el.getCenterXY(true);
30546         }
30547         if(this.modal){
30548             Roo.get(document.body).addClass("x-body-masked");
30549             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30550             this.mask.show();
30551         }
30552         this.constrainXY();
30553     },
30554
30555     // private
30556     animShow : function(){
30557         var b = Roo.get(this.animateTarget).getBox();
30558         this.proxy.setSize(b.width, b.height);
30559         this.proxy.setLocation(b.x, b.y);
30560         this.proxy.show();
30561         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30562                     true, .35, this.showEl.createDelegate(this));
30563     },
30564
30565     /**
30566      * Shows the dialog.
30567      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30568      * @return {Roo.BasicDialog} this
30569      */
30570     show : function(animateTarget){
30571         if (this.fireEvent("beforeshow", this) === false){
30572             return;
30573         }
30574         if(this.syncHeightBeforeShow){
30575             this.syncBodyHeight();
30576         }else if(this.firstShow){
30577             this.firstShow = false;
30578             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30579         }
30580         this.animateTarget = animateTarget || this.animateTarget;
30581         if(!this.el.isVisible()){
30582             this.beforeShow();
30583             if(this.animateTarget && Roo.get(this.animateTarget)){
30584                 this.animShow();
30585             }else{
30586                 this.showEl();
30587             }
30588         }
30589         return this;
30590     },
30591
30592     // private
30593     showEl : function(){
30594         this.proxy.hide();
30595         this.el.setXY(this.xy);
30596         this.el.show();
30597         this.adjustAssets(true);
30598         this.toFront();
30599         this.focus();
30600         // IE peekaboo bug - fix found by Dave Fenwick
30601         if(Roo.isIE){
30602             this.el.repaint();
30603         }
30604         this.fireEvent("show", this);
30605     },
30606
30607     /**
30608      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30609      * dialog itself will receive focus.
30610      */
30611     focus : function(){
30612         if(this.defaultButton){
30613             this.defaultButton.focus();
30614         }else{
30615             this.focusEl.focus();
30616         }
30617     },
30618
30619     // private
30620     constrainXY : function(){
30621         if(this.constraintoviewport !== false){
30622             if(!this.viewSize){
30623                 if(this.container){
30624                     var s = this.container.getSize();
30625                     this.viewSize = [s.width, s.height];
30626                 }else{
30627                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30628                 }
30629             }
30630             var s = Roo.get(this.container||document).getScroll();
30631
30632             var x = this.xy[0], y = this.xy[1];
30633             var w = this.size.width, h = this.size.height;
30634             var vw = this.viewSize[0], vh = this.viewSize[1];
30635             // only move it if it needs it
30636             var moved = false;
30637             // first validate right/bottom
30638             if(x + w > vw+s.left){
30639                 x = vw - w;
30640                 moved = true;
30641             }
30642             if(y + h > vh+s.top){
30643                 y = vh - h;
30644                 moved = true;
30645             }
30646             // then make sure top/left isn't negative
30647             if(x < s.left){
30648                 x = s.left;
30649                 moved = true;
30650             }
30651             if(y < s.top){
30652                 y = s.top;
30653                 moved = true;
30654             }
30655             if(moved){
30656                 // cache xy
30657                 this.xy = [x, y];
30658                 if(this.isVisible()){
30659                     this.el.setLocation(x, y);
30660                     this.adjustAssets();
30661                 }
30662             }
30663         }
30664     },
30665
30666     // private
30667     onDrag : function(){
30668         if(!this.proxyDrag){
30669             this.xy = this.el.getXY();
30670             this.adjustAssets();
30671         }
30672     },
30673
30674     // private
30675     adjustAssets : function(doShow){
30676         var x = this.xy[0], y = this.xy[1];
30677         var w = this.size.width, h = this.size.height;
30678         if(doShow === true){
30679             if(this.shadow){
30680                 this.shadow.show(this.el);
30681             }
30682             if(this.shim){
30683                 this.shim.show();
30684             }
30685         }
30686         if(this.shadow && this.shadow.isVisible()){
30687             this.shadow.show(this.el);
30688         }
30689         if(this.shim && this.shim.isVisible()){
30690             this.shim.setBounds(x, y, w, h);
30691         }
30692     },
30693
30694     // private
30695     adjustViewport : function(w, h){
30696         if(!w || !h){
30697             w = Roo.lib.Dom.getViewWidth();
30698             h = Roo.lib.Dom.getViewHeight();
30699         }
30700         // cache the size
30701         this.viewSize = [w, h];
30702         if(this.modal && this.mask.isVisible()){
30703             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30704             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30705         }
30706         if(this.isVisible()){
30707             this.constrainXY();
30708         }
30709     },
30710
30711     /**
30712      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30713      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30714      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30715      */
30716     destroy : function(removeEl){
30717         if(this.isVisible()){
30718             this.animateTarget = null;
30719             this.hide();
30720         }
30721         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30722         if(this.tabs){
30723             this.tabs.destroy(removeEl);
30724         }
30725         Roo.destroy(
30726              this.shim,
30727              this.proxy,
30728              this.resizer,
30729              this.close,
30730              this.mask
30731         );
30732         if(this.dd){
30733             this.dd.unreg();
30734         }
30735         if(this.buttons){
30736            for(var i = 0, len = this.buttons.length; i < len; i++){
30737                this.buttons[i].destroy();
30738            }
30739         }
30740         this.el.removeAllListeners();
30741         if(removeEl === true){
30742             this.el.update("");
30743             this.el.remove();
30744         }
30745         Roo.DialogManager.unregister(this);
30746     },
30747
30748     // private
30749     startMove : function(){
30750         if(this.proxyDrag){
30751             this.proxy.show();
30752         }
30753         if(this.constraintoviewport !== false){
30754             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30755         }
30756     },
30757
30758     // private
30759     endMove : function(){
30760         if(!this.proxyDrag){
30761             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30762         }else{
30763             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30764             this.proxy.hide();
30765         }
30766         this.refreshSize();
30767         this.adjustAssets();
30768         this.focus();
30769         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30770     },
30771
30772     /**
30773      * Brings this dialog to the front of any other visible dialogs
30774      * @return {Roo.BasicDialog} this
30775      */
30776     toFront : function(){
30777         Roo.DialogManager.bringToFront(this);
30778         return this;
30779     },
30780
30781     /**
30782      * Sends this dialog to the back (under) of any other visible dialogs
30783      * @return {Roo.BasicDialog} this
30784      */
30785     toBack : function(){
30786         Roo.DialogManager.sendToBack(this);
30787         return this;
30788     },
30789
30790     /**
30791      * Centers this dialog in the viewport
30792      * @return {Roo.BasicDialog} this
30793      */
30794     center : function(){
30795         var xy = this.el.getCenterXY(true);
30796         this.moveTo(xy[0], xy[1]);
30797         return this;
30798     },
30799
30800     /**
30801      * Moves the dialog's top-left corner to the specified point
30802      * @param {Number} x
30803      * @param {Number} y
30804      * @return {Roo.BasicDialog} this
30805      */
30806     moveTo : function(x, y){
30807         this.xy = [x,y];
30808         if(this.isVisible()){
30809             this.el.setXY(this.xy);
30810             this.adjustAssets();
30811         }
30812         return this;
30813     },
30814
30815     /**
30816      * Aligns the dialog to the specified element
30817      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30818      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30819      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30820      * @return {Roo.BasicDialog} this
30821      */
30822     alignTo : function(element, position, offsets){
30823         this.xy = this.el.getAlignToXY(element, position, offsets);
30824         if(this.isVisible()){
30825             this.el.setXY(this.xy);
30826             this.adjustAssets();
30827         }
30828         return this;
30829     },
30830
30831     /**
30832      * Anchors an element to another element and realigns it when the window is resized.
30833      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30834      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30835      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30836      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30837      * is a number, it is used as the buffer delay (defaults to 50ms).
30838      * @return {Roo.BasicDialog} this
30839      */
30840     anchorTo : function(el, alignment, offsets, monitorScroll){
30841         var action = function(){
30842             this.alignTo(el, alignment, offsets);
30843         };
30844         Roo.EventManager.onWindowResize(action, this);
30845         var tm = typeof monitorScroll;
30846         if(tm != 'undefined'){
30847             Roo.EventManager.on(window, 'scroll', action, this,
30848                 {buffer: tm == 'number' ? monitorScroll : 50});
30849         }
30850         action.call(this);
30851         return this;
30852     },
30853
30854     /**
30855      * Returns true if the dialog is visible
30856      * @return {Boolean}
30857      */
30858     isVisible : function(){
30859         return this.el.isVisible();
30860     },
30861
30862     // private
30863     animHide : function(callback){
30864         var b = Roo.get(this.animateTarget).getBox();
30865         this.proxy.show();
30866         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30867         this.el.hide();
30868         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30869                     this.hideEl.createDelegate(this, [callback]));
30870     },
30871
30872     /**
30873      * Hides the dialog.
30874      * @param {Function} callback (optional) Function to call when the dialog is hidden
30875      * @return {Roo.BasicDialog} this
30876      */
30877     hide : function(callback){
30878         if (this.fireEvent("beforehide", this) === false){
30879             return;
30880         }
30881         if(this.shadow){
30882             this.shadow.hide();
30883         }
30884         if(this.shim) {
30885           this.shim.hide();
30886         }
30887         // sometimes animateTarget seems to get set.. causing problems...
30888         // this just double checks..
30889         if(this.animateTarget && Roo.get(this.animateTarget)) {
30890            this.animHide(callback);
30891         }else{
30892             this.el.hide();
30893             this.hideEl(callback);
30894         }
30895         return this;
30896     },
30897
30898     // private
30899     hideEl : function(callback){
30900         this.proxy.hide();
30901         if(this.modal){
30902             this.mask.hide();
30903             Roo.get(document.body).removeClass("x-body-masked");
30904         }
30905         this.fireEvent("hide", this);
30906         if(typeof callback == "function"){
30907             callback();
30908         }
30909     },
30910
30911     // private
30912     hideAction : function(){
30913         this.setLeft("-10000px");
30914         this.setTop("-10000px");
30915         this.setStyle("visibility", "hidden");
30916     },
30917
30918     // private
30919     refreshSize : function(){
30920         this.size = this.el.getSize();
30921         this.xy = this.el.getXY();
30922         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30923     },
30924
30925     // private
30926     // z-index is managed by the DialogManager and may be overwritten at any time
30927     setZIndex : function(index){
30928         if(this.modal){
30929             this.mask.setStyle("z-index", index);
30930         }
30931         if(this.shim){
30932             this.shim.setStyle("z-index", ++index);
30933         }
30934         if(this.shadow){
30935             this.shadow.setZIndex(++index);
30936         }
30937         this.el.setStyle("z-index", ++index);
30938         if(this.proxy){
30939             this.proxy.setStyle("z-index", ++index);
30940         }
30941         if(this.resizer){
30942             this.resizer.proxy.setStyle("z-index", ++index);
30943         }
30944
30945         this.lastZIndex = index;
30946     },
30947
30948     /**
30949      * Returns the element for this dialog
30950      * @return {Roo.Element} The underlying dialog Element
30951      */
30952     getEl : function(){
30953         return this.el;
30954     }
30955 });
30956
30957 /**
30958  * @class Roo.DialogManager
30959  * Provides global access to BasicDialogs that have been created and
30960  * support for z-indexing (layering) multiple open dialogs.
30961  */
30962 Roo.DialogManager = function(){
30963     var list = {};
30964     var accessList = [];
30965     var front = null;
30966
30967     // private
30968     var sortDialogs = function(d1, d2){
30969         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30970     };
30971
30972     // private
30973     var orderDialogs = function(){
30974         accessList.sort(sortDialogs);
30975         var seed = Roo.DialogManager.zseed;
30976         for(var i = 0, len = accessList.length; i < len; i++){
30977             var dlg = accessList[i];
30978             if(dlg){
30979                 dlg.setZIndex(seed + (i*10));
30980             }
30981         }
30982     };
30983
30984     return {
30985         /**
30986          * The starting z-index for BasicDialogs (defaults to 9000)
30987          * @type Number The z-index value
30988          */
30989         zseed : 9000,
30990
30991         // private
30992         register : function(dlg){
30993             list[dlg.id] = dlg;
30994             accessList.push(dlg);
30995         },
30996
30997         // private
30998         unregister : function(dlg){
30999             delete list[dlg.id];
31000             var i=0;
31001             var len=0;
31002             if(!accessList.indexOf){
31003                 for(  i = 0, len = accessList.length; i < len; i++){
31004                     if(accessList[i] == dlg){
31005                         accessList.splice(i, 1);
31006                         return;
31007                     }
31008                 }
31009             }else{
31010                  i = accessList.indexOf(dlg);
31011                 if(i != -1){
31012                     accessList.splice(i, 1);
31013                 }
31014             }
31015         },
31016
31017         /**
31018          * Gets a registered dialog by id
31019          * @param {String/Object} id The id of the dialog or a dialog
31020          * @return {Roo.BasicDialog} this
31021          */
31022         get : function(id){
31023             return typeof id == "object" ? id : list[id];
31024         },
31025
31026         /**
31027          * Brings the specified dialog to the front
31028          * @param {String/Object} dlg The id of the dialog or a dialog
31029          * @return {Roo.BasicDialog} this
31030          */
31031         bringToFront : function(dlg){
31032             dlg = this.get(dlg);
31033             if(dlg != front){
31034                 front = dlg;
31035                 dlg._lastAccess = new Date().getTime();
31036                 orderDialogs();
31037             }
31038             return dlg;
31039         },
31040
31041         /**
31042          * Sends the specified dialog to the back
31043          * @param {String/Object} dlg The id of the dialog or a dialog
31044          * @return {Roo.BasicDialog} this
31045          */
31046         sendToBack : function(dlg){
31047             dlg = this.get(dlg);
31048             dlg._lastAccess = -(new Date().getTime());
31049             orderDialogs();
31050             return dlg;
31051         },
31052
31053         /**
31054          * Hides all dialogs
31055          */
31056         hideAll : function(){
31057             for(var id in list){
31058                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31059                     list[id].hide();
31060                 }
31061             }
31062         }
31063     };
31064 }();
31065
31066 /**
31067  * @class Roo.LayoutDialog
31068  * @extends Roo.BasicDialog
31069  * Dialog which provides adjustments for working with a layout in a Dialog.
31070  * Add your necessary layout config options to the dialog's config.<br>
31071  * Example usage (including a nested layout):
31072  * <pre><code>
31073 if(!dialog){
31074     dialog = new Roo.LayoutDialog("download-dlg", {
31075         modal: true,
31076         width:600,
31077         height:450,
31078         shadow:true,
31079         minWidth:500,
31080         minHeight:350,
31081         autoTabs:true,
31082         proxyDrag:true,
31083         // layout config merges with the dialog config
31084         center:{
31085             tabPosition: "top",
31086             alwaysShowTabs: true
31087         }
31088     });
31089     dialog.addKeyListener(27, dialog.hide, dialog);
31090     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31091     dialog.addButton("Build It!", this.getDownload, this);
31092
31093     // we can even add nested layouts
31094     var innerLayout = new Roo.BorderLayout("dl-inner", {
31095         east: {
31096             initialSize: 200,
31097             autoScroll:true,
31098             split:true
31099         },
31100         center: {
31101             autoScroll:true
31102         }
31103     });
31104     innerLayout.beginUpdate();
31105     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31106     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31107     innerLayout.endUpdate(true);
31108
31109     var layout = dialog.getLayout();
31110     layout.beginUpdate();
31111     layout.add("center", new Roo.ContentPanel("standard-panel",
31112                         {title: "Download the Source", fitToFrame:true}));
31113     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31114                {title: "Build your own roo.js"}));
31115     layout.getRegion("center").showPanel(sp);
31116     layout.endUpdate();
31117 }
31118 </code></pre>
31119     * @constructor
31120     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31121     * @param {Object} config configuration options
31122   */
31123 Roo.LayoutDialog = function(el, cfg){
31124     
31125     var config=  cfg;
31126     if (typeof(cfg) == 'undefined') {
31127         config = Roo.apply({}, el);
31128         // not sure why we use documentElement here.. - it should always be body.
31129         // IE7 borks horribly if we use documentElement.
31130         // webkit also does not like documentElement - it creates a body element...
31131         el = Roo.get( document.body || document.documentElement ).createChild();
31132         //config.autoCreate = true;
31133     }
31134     
31135     
31136     config.autoTabs = false;
31137     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31138     this.body.setStyle({overflow:"hidden", position:"relative"});
31139     this.layout = new Roo.BorderLayout(this.body.dom, config);
31140     this.layout.monitorWindowResize = false;
31141     this.el.addClass("x-dlg-auto-layout");
31142     // fix case when center region overwrites center function
31143     this.center = Roo.BasicDialog.prototype.center;
31144     this.on("show", this.layout.layout, this.layout, true);
31145     if (config.items) {
31146         var xitems = config.items;
31147         delete config.items;
31148         Roo.each(xitems, this.addxtype, this);
31149     }
31150     
31151     
31152 };
31153 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31154     /**
31155      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31156      * @deprecated
31157      */
31158     endUpdate : function(){
31159         this.layout.endUpdate();
31160     },
31161
31162     /**
31163      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31164      *  @deprecated
31165      */
31166     beginUpdate : function(){
31167         this.layout.beginUpdate();
31168     },
31169
31170     /**
31171      * Get the BorderLayout for this dialog
31172      * @return {Roo.BorderLayout}
31173      */
31174     getLayout : function(){
31175         return this.layout;
31176     },
31177
31178     showEl : function(){
31179         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31180         if(Roo.isIE7){
31181             this.layout.layout();
31182         }
31183     },
31184
31185     // private
31186     // Use the syncHeightBeforeShow config option to control this automatically
31187     syncBodyHeight : function(){
31188         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31189         if(this.layout){this.layout.layout();}
31190     },
31191     
31192       /**
31193      * Add an xtype element (actually adds to the layout.)
31194      * @return {Object} xdata xtype object data.
31195      */
31196     
31197     addxtype : function(c) {
31198         return this.layout.addxtype(c);
31199     }
31200 });/*
31201  * Based on:
31202  * Ext JS Library 1.1.1
31203  * Copyright(c) 2006-2007, Ext JS, LLC.
31204  *
31205  * Originally Released Under LGPL - original licence link has changed is not relivant.
31206  *
31207  * Fork - LGPL
31208  * <script type="text/javascript">
31209  */
31210  
31211 /**
31212  * @class Roo.MessageBox
31213  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31214  * Example usage:
31215  *<pre><code>
31216 // Basic alert:
31217 Roo.Msg.alert('Status', 'Changes saved successfully.');
31218
31219 // Prompt for user data:
31220 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31221     if (btn == 'ok'){
31222         // process text value...
31223     }
31224 });
31225
31226 // Show a dialog using config options:
31227 Roo.Msg.show({
31228    title:'Save Changes?',
31229    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31230    buttons: Roo.Msg.YESNOCANCEL,
31231    fn: processResult,
31232    animEl: 'elId'
31233 });
31234 </code></pre>
31235  * @singleton
31236  */
31237 Roo.MessageBox = function(){
31238     var dlg, opt, mask, waitTimer;
31239     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31240     var buttons, activeTextEl, bwidth;
31241
31242     // private
31243     var handleButton = function(button){
31244         dlg.hide();
31245         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31246     };
31247
31248     // private
31249     var handleHide = function(){
31250         if(opt && opt.cls){
31251             dlg.el.removeClass(opt.cls);
31252         }
31253         if(waitTimer){
31254             Roo.TaskMgr.stop(waitTimer);
31255             waitTimer = null;
31256         }
31257     };
31258
31259     // private
31260     var updateButtons = function(b){
31261         var width = 0;
31262         if(!b){
31263             buttons["ok"].hide();
31264             buttons["cancel"].hide();
31265             buttons["yes"].hide();
31266             buttons["no"].hide();
31267             dlg.footer.dom.style.display = 'none';
31268             return width;
31269         }
31270         dlg.footer.dom.style.display = '';
31271         for(var k in buttons){
31272             if(typeof buttons[k] != "function"){
31273                 if(b[k]){
31274                     buttons[k].show();
31275                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31276                     width += buttons[k].el.getWidth()+15;
31277                 }else{
31278                     buttons[k].hide();
31279                 }
31280             }
31281         }
31282         return width;
31283     };
31284
31285     // private
31286     var handleEsc = function(d, k, e){
31287         if(opt && opt.closable !== false){
31288             dlg.hide();
31289         }
31290         if(e){
31291             e.stopEvent();
31292         }
31293     };
31294
31295     return {
31296         /**
31297          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31298          * @return {Roo.BasicDialog} The BasicDialog element
31299          */
31300         getDialog : function(){
31301            if(!dlg){
31302                 dlg = new Roo.BasicDialog("x-msg-box", {
31303                     autoCreate : true,
31304                     shadow: true,
31305                     draggable: true,
31306                     resizable:false,
31307                     constraintoviewport:false,
31308                     fixedcenter:true,
31309                     collapsible : false,
31310                     shim:true,
31311                     modal: true,
31312                     width:400, height:100,
31313                     buttonAlign:"center",
31314                     closeClick : function(){
31315                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31316                             handleButton("no");
31317                         }else{
31318                             handleButton("cancel");
31319                         }
31320                     }
31321                 });
31322                 dlg.on("hide", handleHide);
31323                 mask = dlg.mask;
31324                 dlg.addKeyListener(27, handleEsc);
31325                 buttons = {};
31326                 var bt = this.buttonText;
31327                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31328                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31329                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31330                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31331                 bodyEl = dlg.body.createChild({
31332
31333                     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>'
31334                 });
31335                 msgEl = bodyEl.dom.firstChild;
31336                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31337                 textboxEl.enableDisplayMode();
31338                 textboxEl.addKeyListener([10,13], function(){
31339                     if(dlg.isVisible() && opt && opt.buttons){
31340                         if(opt.buttons.ok){
31341                             handleButton("ok");
31342                         }else if(opt.buttons.yes){
31343                             handleButton("yes");
31344                         }
31345                     }
31346                 });
31347                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31348                 textareaEl.enableDisplayMode();
31349                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31350                 progressEl.enableDisplayMode();
31351                 var pf = progressEl.dom.firstChild;
31352                 if (pf) {
31353                     pp = Roo.get(pf.firstChild);
31354                     pp.setHeight(pf.offsetHeight);
31355                 }
31356                 
31357             }
31358             return dlg;
31359         },
31360
31361         /**
31362          * Updates the message box body text
31363          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31364          * the XHTML-compliant non-breaking space character '&amp;#160;')
31365          * @return {Roo.MessageBox} This message box
31366          */
31367         updateText : function(text){
31368             if(!dlg.isVisible() && !opt.width){
31369                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31370             }
31371             msgEl.innerHTML = text || '&#160;';
31372       
31373             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31374             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31375             var w = Math.max(
31376                     Math.min(opt.width || cw , this.maxWidth), 
31377                     Math.max(opt.minWidth || this.minWidth, bwidth)
31378             );
31379             if(opt.prompt){
31380                 activeTextEl.setWidth(w);
31381             }
31382             if(dlg.isVisible()){
31383                 dlg.fixedcenter = false;
31384             }
31385             // to big, make it scroll. = But as usual stupid IE does not support
31386             // !important..
31387             
31388             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31389                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31390                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31391             } else {
31392                 bodyEl.dom.style.height = '';
31393                 bodyEl.dom.style.overflowY = '';
31394             }
31395             if (cw > w) {
31396                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31397             } else {
31398                 bodyEl.dom.style.overflowX = '';
31399             }
31400             
31401             dlg.setContentSize(w, bodyEl.getHeight());
31402             if(dlg.isVisible()){
31403                 dlg.fixedcenter = true;
31404             }
31405             return this;
31406         },
31407
31408         /**
31409          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31410          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31411          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31412          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31413          * @return {Roo.MessageBox} This message box
31414          */
31415         updateProgress : function(value, text){
31416             if(text){
31417                 this.updateText(text);
31418             }
31419             if (pp) { // weird bug on my firefox - for some reason this is not defined
31420                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31421             }
31422             return this;
31423         },        
31424
31425         /**
31426          * Returns true if the message box is currently displayed
31427          * @return {Boolean} True if the message box is visible, else false
31428          */
31429         isVisible : function(){
31430             return dlg && dlg.isVisible();  
31431         },
31432
31433         /**
31434          * Hides the message box if it is displayed
31435          */
31436         hide : function(){
31437             if(this.isVisible()){
31438                 dlg.hide();
31439             }  
31440         },
31441
31442         /**
31443          * Displays a new message box, or reinitializes an existing message box, based on the config options
31444          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31445          * The following config object properties are supported:
31446          * <pre>
31447 Property    Type             Description
31448 ----------  ---------------  ------------------------------------------------------------------------------------
31449 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31450                                    closes (defaults to undefined)
31451 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31452                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31453 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31454                                    progress and wait dialogs will ignore this property and always hide the
31455                                    close button as they can only be closed programmatically.
31456 cls               String           A custom CSS class to apply to the message box element
31457 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31458                                    displayed (defaults to 75)
31459 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31460                                    function will be btn (the name of the button that was clicked, if applicable,
31461                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31462                                    Progress and wait dialogs will ignore this option since they do not respond to
31463                                    user actions and can only be closed programmatically, so any required function
31464                                    should be called by the same code after it closes the dialog.
31465 icon              String           A CSS class that provides a background image to be used as an icon for
31466                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31467 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31468 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31469 modal             Boolean          False to allow user interaction with the page while the message box is
31470                                    displayed (defaults to true)
31471 msg               String           A string that will replace the existing message box body text (defaults
31472                                    to the XHTML-compliant non-breaking space character '&#160;')
31473 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31474 progress          Boolean          True to display a progress bar (defaults to false)
31475 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31476 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31477 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31478 title             String           The title text
31479 value             String           The string value to set into the active textbox element if displayed
31480 wait              Boolean          True to display a progress bar (defaults to false)
31481 width             Number           The width of the dialog in pixels
31482 </pre>
31483          *
31484          * Example usage:
31485          * <pre><code>
31486 Roo.Msg.show({
31487    title: 'Address',
31488    msg: 'Please enter your address:',
31489    width: 300,
31490    buttons: Roo.MessageBox.OKCANCEL,
31491    multiline: true,
31492    fn: saveAddress,
31493    animEl: 'addAddressBtn'
31494 });
31495 </code></pre>
31496          * @param {Object} config Configuration options
31497          * @return {Roo.MessageBox} This message box
31498          */
31499         show : function(options)
31500         {
31501             
31502             // this causes nightmares if you show one dialog after another
31503             // especially on callbacks..
31504              
31505             if(this.isVisible()){
31506                 
31507                 this.hide();
31508                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31509                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31510                 Roo.log("New Dialog Message:" +  options.msg )
31511                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31512                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31513                 
31514             }
31515             var d = this.getDialog();
31516             opt = options;
31517             d.setTitle(opt.title || "&#160;");
31518             d.close.setDisplayed(opt.closable !== false);
31519             activeTextEl = textboxEl;
31520             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31521             if(opt.prompt){
31522                 if(opt.multiline){
31523                     textboxEl.hide();
31524                     textareaEl.show();
31525                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31526                         opt.multiline : this.defaultTextHeight);
31527                     activeTextEl = textareaEl;
31528                 }else{
31529                     textboxEl.show();
31530                     textareaEl.hide();
31531                 }
31532             }else{
31533                 textboxEl.hide();
31534                 textareaEl.hide();
31535             }
31536             progressEl.setDisplayed(opt.progress === true);
31537             this.updateProgress(0);
31538             activeTextEl.dom.value = opt.value || "";
31539             if(opt.prompt){
31540                 dlg.setDefaultButton(activeTextEl);
31541             }else{
31542                 var bs = opt.buttons;
31543                 var db = null;
31544                 if(bs && bs.ok){
31545                     db = buttons["ok"];
31546                 }else if(bs && bs.yes){
31547                     db = buttons["yes"];
31548                 }
31549                 dlg.setDefaultButton(db);
31550             }
31551             bwidth = updateButtons(opt.buttons);
31552             this.updateText(opt.msg);
31553             if(opt.cls){
31554                 d.el.addClass(opt.cls);
31555             }
31556             d.proxyDrag = opt.proxyDrag === true;
31557             d.modal = opt.modal !== false;
31558             d.mask = opt.modal !== false ? mask : false;
31559             if(!d.isVisible()){
31560                 // force it to the end of the z-index stack so it gets a cursor in FF
31561                 document.body.appendChild(dlg.el.dom);
31562                 d.animateTarget = null;
31563                 d.show(options.animEl);
31564             }
31565             return this;
31566         },
31567
31568         /**
31569          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31570          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31571          * and closing the message box when the process is complete.
31572          * @param {String} title The title bar text
31573          * @param {String} msg The message box body text
31574          * @return {Roo.MessageBox} This message box
31575          */
31576         progress : function(title, msg){
31577             this.show({
31578                 title : title,
31579                 msg : msg,
31580                 buttons: false,
31581                 progress:true,
31582                 closable:false,
31583                 minWidth: this.minProgressWidth,
31584                 modal : true
31585             });
31586             return this;
31587         },
31588
31589         /**
31590          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31591          * If a callback function is passed it will be called after the user clicks the button, and the
31592          * id of the button that was clicked will be passed as the only parameter to the callback
31593          * (could also be the top-right close button).
31594          * @param {String} title The title bar text
31595          * @param {String} msg The message box body text
31596          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31597          * @param {Object} scope (optional) The scope of the callback function
31598          * @return {Roo.MessageBox} This message box
31599          */
31600         alert : function(title, msg, fn, scope){
31601             this.show({
31602                 title : title,
31603                 msg : msg,
31604                 buttons: this.OK,
31605                 fn: fn,
31606                 scope : scope,
31607                 modal : true
31608             });
31609             return this;
31610         },
31611
31612         /**
31613          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31614          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31615          * You are responsible for closing the message box when the process is complete.
31616          * @param {String} msg The message box body text
31617          * @param {String} title (optional) The title bar text
31618          * @return {Roo.MessageBox} This message box
31619          */
31620         wait : function(msg, title){
31621             this.show({
31622                 title : title,
31623                 msg : msg,
31624                 buttons: false,
31625                 closable:false,
31626                 progress:true,
31627                 modal:true,
31628                 width:300,
31629                 wait:true
31630             });
31631             waitTimer = Roo.TaskMgr.start({
31632                 run: function(i){
31633                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31634                 },
31635                 interval: 1000
31636             });
31637             return this;
31638         },
31639
31640         /**
31641          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31642          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31643          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31644          * @param {String} title The title bar text
31645          * @param {String} msg The message box body text
31646          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31647          * @param {Object} scope (optional) The scope of the callback function
31648          * @return {Roo.MessageBox} This message box
31649          */
31650         confirm : function(title, msg, fn, scope){
31651             this.show({
31652                 title : title,
31653                 msg : msg,
31654                 buttons: this.YESNO,
31655                 fn: fn,
31656                 scope : scope,
31657                 modal : true
31658             });
31659             return this;
31660         },
31661
31662         /**
31663          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31664          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31665          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31666          * (could also be the top-right close button) and the text that was entered will be passed as the two
31667          * parameters to the callback.
31668          * @param {String} title The title bar text
31669          * @param {String} msg The message box body text
31670          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31671          * @param {Object} scope (optional) The scope of the callback function
31672          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31673          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31674          * @return {Roo.MessageBox} This message box
31675          */
31676         prompt : function(title, msg, fn, scope, multiline){
31677             this.show({
31678                 title : title,
31679                 msg : msg,
31680                 buttons: this.OKCANCEL,
31681                 fn: fn,
31682                 minWidth:250,
31683                 scope : scope,
31684                 prompt:true,
31685                 multiline: multiline,
31686                 modal : true
31687             });
31688             return this;
31689         },
31690
31691         /**
31692          * Button config that displays a single OK button
31693          * @type Object
31694          */
31695         OK : {ok:true},
31696         /**
31697          * Button config that displays Yes and No buttons
31698          * @type Object
31699          */
31700         YESNO : {yes:true, no:true},
31701         /**
31702          * Button config that displays OK and Cancel buttons
31703          * @type Object
31704          */
31705         OKCANCEL : {ok:true, cancel:true},
31706         /**
31707          * Button config that displays Yes, No and Cancel buttons
31708          * @type Object
31709          */
31710         YESNOCANCEL : {yes:true, no:true, cancel:true},
31711
31712         /**
31713          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31714          * @type Number
31715          */
31716         defaultTextHeight : 75,
31717         /**
31718          * The maximum width in pixels of the message box (defaults to 600)
31719          * @type Number
31720          */
31721         maxWidth : 600,
31722         /**
31723          * The minimum width in pixels of the message box (defaults to 100)
31724          * @type Number
31725          */
31726         minWidth : 100,
31727         /**
31728          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31729          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31730          * @type Number
31731          */
31732         minProgressWidth : 250,
31733         /**
31734          * An object containing the default button text strings that can be overriden for localized language support.
31735          * Supported properties are: ok, cancel, yes and no.
31736          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31737          * @type Object
31738          */
31739         buttonText : {
31740             ok : "OK",
31741             cancel : "Cancel",
31742             yes : "Yes",
31743             no : "No"
31744         }
31745     };
31746 }();
31747
31748 /**
31749  * Shorthand for {@link Roo.MessageBox}
31750  */
31751 Roo.Msg = Roo.MessageBox;/*
31752  * Based on:
31753  * Ext JS Library 1.1.1
31754  * Copyright(c) 2006-2007, Ext JS, LLC.
31755  *
31756  * Originally Released Under LGPL - original licence link has changed is not relivant.
31757  *
31758  * Fork - LGPL
31759  * <script type="text/javascript">
31760  */
31761 /**
31762  * @class Roo.QuickTips
31763  * Provides attractive and customizable tooltips for any element.
31764  * @singleton
31765  */
31766 Roo.QuickTips = function(){
31767     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31768     var ce, bd, xy, dd;
31769     var visible = false, disabled = true, inited = false;
31770     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31771     
31772     var onOver = function(e){
31773         if(disabled){
31774             return;
31775         }
31776         var t = e.getTarget();
31777         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31778             return;
31779         }
31780         if(ce && t == ce.el){
31781             clearTimeout(hideProc);
31782             return;
31783         }
31784         if(t && tagEls[t.id]){
31785             tagEls[t.id].el = t;
31786             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31787             return;
31788         }
31789         var ttp, et = Roo.fly(t);
31790         var ns = cfg.namespace;
31791         if(tm.interceptTitles && t.title){
31792             ttp = t.title;
31793             t.qtip = ttp;
31794             t.removeAttribute("title");
31795             e.preventDefault();
31796         }else{
31797             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31798         }
31799         if(ttp){
31800             showProc = show.defer(tm.showDelay, tm, [{
31801                 el: t, 
31802                 text: ttp, 
31803                 width: et.getAttributeNS(ns, cfg.width),
31804                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31805                 title: et.getAttributeNS(ns, cfg.title),
31806                     cls: et.getAttributeNS(ns, cfg.cls)
31807             }]);
31808         }
31809     };
31810     
31811     var onOut = function(e){
31812         clearTimeout(showProc);
31813         var t = e.getTarget();
31814         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31815             hideProc = setTimeout(hide, tm.hideDelay);
31816         }
31817     };
31818     
31819     var onMove = function(e){
31820         if(disabled){
31821             return;
31822         }
31823         xy = e.getXY();
31824         xy[1] += 18;
31825         if(tm.trackMouse && ce){
31826             el.setXY(xy);
31827         }
31828     };
31829     
31830     var onDown = function(e){
31831         clearTimeout(showProc);
31832         clearTimeout(hideProc);
31833         if(!e.within(el)){
31834             if(tm.hideOnClick){
31835                 hide();
31836                 tm.disable();
31837                 tm.enable.defer(100, tm);
31838             }
31839         }
31840     };
31841     
31842     var getPad = function(){
31843         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31844     };
31845
31846     var show = function(o){
31847         if(disabled){
31848             return;
31849         }
31850         clearTimeout(dismissProc);
31851         ce = o;
31852         if(removeCls){ // in case manually hidden
31853             el.removeClass(removeCls);
31854             removeCls = null;
31855         }
31856         if(ce.cls){
31857             el.addClass(ce.cls);
31858             removeCls = ce.cls;
31859         }
31860         if(ce.title){
31861             tipTitle.update(ce.title);
31862             tipTitle.show();
31863         }else{
31864             tipTitle.update('');
31865             tipTitle.hide();
31866         }
31867         el.dom.style.width  = tm.maxWidth+'px';
31868         //tipBody.dom.style.width = '';
31869         tipBodyText.update(o.text);
31870         var p = getPad(), w = ce.width;
31871         if(!w){
31872             var td = tipBodyText.dom;
31873             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31874             if(aw > tm.maxWidth){
31875                 w = tm.maxWidth;
31876             }else if(aw < tm.minWidth){
31877                 w = tm.minWidth;
31878             }else{
31879                 w = aw;
31880             }
31881         }
31882         //tipBody.setWidth(w);
31883         el.setWidth(parseInt(w, 10) + p);
31884         if(ce.autoHide === false){
31885             close.setDisplayed(true);
31886             if(dd){
31887                 dd.unlock();
31888             }
31889         }else{
31890             close.setDisplayed(false);
31891             if(dd){
31892                 dd.lock();
31893             }
31894         }
31895         if(xy){
31896             el.avoidY = xy[1]-18;
31897             el.setXY(xy);
31898         }
31899         if(tm.animate){
31900             el.setOpacity(.1);
31901             el.setStyle("visibility", "visible");
31902             el.fadeIn({callback: afterShow});
31903         }else{
31904             afterShow();
31905         }
31906     };
31907     
31908     var afterShow = function(){
31909         if(ce){
31910             el.show();
31911             esc.enable();
31912             if(tm.autoDismiss && ce.autoHide !== false){
31913                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31914             }
31915         }
31916     };
31917     
31918     var hide = function(noanim){
31919         clearTimeout(dismissProc);
31920         clearTimeout(hideProc);
31921         ce = null;
31922         if(el.isVisible()){
31923             esc.disable();
31924             if(noanim !== true && tm.animate){
31925                 el.fadeOut({callback: afterHide});
31926             }else{
31927                 afterHide();
31928             } 
31929         }
31930     };
31931     
31932     var afterHide = function(){
31933         el.hide();
31934         if(removeCls){
31935             el.removeClass(removeCls);
31936             removeCls = null;
31937         }
31938     };
31939     
31940     return {
31941         /**
31942         * @cfg {Number} minWidth
31943         * The minimum width of the quick tip (defaults to 40)
31944         */
31945        minWidth : 40,
31946         /**
31947         * @cfg {Number} maxWidth
31948         * The maximum width of the quick tip (defaults to 300)
31949         */
31950        maxWidth : 300,
31951         /**
31952         * @cfg {Boolean} interceptTitles
31953         * True to automatically use the element's DOM title value if available (defaults to false)
31954         */
31955        interceptTitles : false,
31956         /**
31957         * @cfg {Boolean} trackMouse
31958         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31959         */
31960        trackMouse : false,
31961         /**
31962         * @cfg {Boolean} hideOnClick
31963         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31964         */
31965        hideOnClick : true,
31966         /**
31967         * @cfg {Number} showDelay
31968         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31969         */
31970        showDelay : 500,
31971         /**
31972         * @cfg {Number} hideDelay
31973         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31974         */
31975        hideDelay : 200,
31976         /**
31977         * @cfg {Boolean} autoHide
31978         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31979         * Used in conjunction with hideDelay.
31980         */
31981        autoHide : true,
31982         /**
31983         * @cfg {Boolean}
31984         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31985         * (defaults to true).  Used in conjunction with autoDismissDelay.
31986         */
31987        autoDismiss : true,
31988         /**
31989         * @cfg {Number}
31990         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31991         */
31992        autoDismissDelay : 5000,
31993        /**
31994         * @cfg {Boolean} animate
31995         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31996         */
31997        animate : false,
31998
31999        /**
32000         * @cfg {String} title
32001         * Title text to display (defaults to '').  This can be any valid HTML markup.
32002         */
32003         title: '',
32004        /**
32005         * @cfg {String} text
32006         * Body text to display (defaults to '').  This can be any valid HTML markup.
32007         */
32008         text : '',
32009        /**
32010         * @cfg {String} cls
32011         * A CSS class to apply to the base quick tip element (defaults to '').
32012         */
32013         cls : '',
32014        /**
32015         * @cfg {Number} width
32016         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32017         * minWidth or maxWidth.
32018         */
32019         width : null,
32020
32021     /**
32022      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32023      * or display QuickTips in a page.
32024      */
32025        init : function(){
32026           tm = Roo.QuickTips;
32027           cfg = tm.tagConfig;
32028           if(!inited){
32029               if(!Roo.isReady){ // allow calling of init() before onReady
32030                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32031                   return;
32032               }
32033               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32034               el.fxDefaults = {stopFx: true};
32035               // maximum custom styling
32036               //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>');
32037               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>');              
32038               tipTitle = el.child('h3');
32039               tipTitle.enableDisplayMode("block");
32040               tipBody = el.child('div.x-tip-bd');
32041               tipBodyText = el.child('div.x-tip-bd-inner');
32042               //bdLeft = el.child('div.x-tip-bd-left');
32043               //bdRight = el.child('div.x-tip-bd-right');
32044               close = el.child('div.x-tip-close');
32045               close.enableDisplayMode("block");
32046               close.on("click", hide);
32047               var d = Roo.get(document);
32048               d.on("mousedown", onDown);
32049               d.on("mouseover", onOver);
32050               d.on("mouseout", onOut);
32051               d.on("mousemove", onMove);
32052               esc = d.addKeyListener(27, hide);
32053               esc.disable();
32054               if(Roo.dd.DD){
32055                   dd = el.initDD("default", null, {
32056                       onDrag : function(){
32057                           el.sync();  
32058                       }
32059                   });
32060                   dd.setHandleElId(tipTitle.id);
32061                   dd.lock();
32062               }
32063               inited = true;
32064           }
32065           this.enable(); 
32066        },
32067
32068     /**
32069      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32070      * are supported:
32071      * <pre>
32072 Property    Type                   Description
32073 ----------  ---------------------  ------------------------------------------------------------------------
32074 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32075      * </ul>
32076      * @param {Object} config The config object
32077      */
32078        register : function(config){
32079            var cs = config instanceof Array ? config : arguments;
32080            for(var i = 0, len = cs.length; i < len; i++) {
32081                var c = cs[i];
32082                var target = c.target;
32083                if(target){
32084                    if(target instanceof Array){
32085                        for(var j = 0, jlen = target.length; j < jlen; j++){
32086                            tagEls[target[j]] = c;
32087                        }
32088                    }else{
32089                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32090                    }
32091                }
32092            }
32093        },
32094
32095     /**
32096      * Removes this quick tip from its element and destroys it.
32097      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32098      */
32099        unregister : function(el){
32100            delete tagEls[Roo.id(el)];
32101        },
32102
32103     /**
32104      * Enable this quick tip.
32105      */
32106        enable : function(){
32107            if(inited && disabled){
32108                locks.pop();
32109                if(locks.length < 1){
32110                    disabled = false;
32111                }
32112            }
32113        },
32114
32115     /**
32116      * Disable this quick tip.
32117      */
32118        disable : function(){
32119           disabled = true;
32120           clearTimeout(showProc);
32121           clearTimeout(hideProc);
32122           clearTimeout(dismissProc);
32123           if(ce){
32124               hide(true);
32125           }
32126           locks.push(1);
32127        },
32128
32129     /**
32130      * Returns true if the quick tip is enabled, else false.
32131      */
32132        isEnabled : function(){
32133             return !disabled;
32134        },
32135
32136         // private
32137        tagConfig : {
32138            namespace : "ext",
32139            attribute : "qtip",
32140            width : "width",
32141            target : "target",
32142            title : "qtitle",
32143            hide : "hide",
32144            cls : "qclass"
32145        }
32146    };
32147 }();
32148
32149 // backwards compat
32150 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32151  * Based on:
32152  * Ext JS Library 1.1.1
32153  * Copyright(c) 2006-2007, Ext JS, LLC.
32154  *
32155  * Originally Released Under LGPL - original licence link has changed is not relivant.
32156  *
32157  * Fork - LGPL
32158  * <script type="text/javascript">
32159  */
32160  
32161
32162 /**
32163  * @class Roo.tree.TreePanel
32164  * @extends Roo.data.Tree
32165
32166  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32167  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32168  * @cfg {Boolean} enableDD true to enable drag and drop
32169  * @cfg {Boolean} enableDrag true to enable just drag
32170  * @cfg {Boolean} enableDrop true to enable just drop
32171  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32172  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32173  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32174  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32175  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32176  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32177  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32178  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32179  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32180  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32181  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32182  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32183  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32184  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32185  * @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>
32186  * @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>
32187  * 
32188  * @constructor
32189  * @param {String/HTMLElement/Element} el The container element
32190  * @param {Object} config
32191  */
32192 Roo.tree.TreePanel = function(el, config){
32193     var root = false;
32194     var loader = false;
32195     if (config.root) {
32196         root = config.root;
32197         delete config.root;
32198     }
32199     if (config.loader) {
32200         loader = config.loader;
32201         delete config.loader;
32202     }
32203     
32204     Roo.apply(this, config);
32205     Roo.tree.TreePanel.superclass.constructor.call(this);
32206     this.el = Roo.get(el);
32207     this.el.addClass('x-tree');
32208     //console.log(root);
32209     if (root) {
32210         this.setRootNode( Roo.factory(root, Roo.tree));
32211     }
32212     if (loader) {
32213         this.loader = Roo.factory(loader, Roo.tree);
32214     }
32215    /**
32216     * Read-only. The id of the container element becomes this TreePanel's id.
32217     */
32218     this.id = this.el.id;
32219     this.addEvents({
32220         /**
32221         * @event beforeload
32222         * Fires before a node is loaded, return false to cancel
32223         * @param {Node} node The node being loaded
32224         */
32225         "beforeload" : true,
32226         /**
32227         * @event load
32228         * Fires when a node is loaded
32229         * @param {Node} node The node that was loaded
32230         */
32231         "load" : true,
32232         /**
32233         * @event textchange
32234         * Fires when the text for a node is changed
32235         * @param {Node} node The node
32236         * @param {String} text The new text
32237         * @param {String} oldText The old text
32238         */
32239         "textchange" : true,
32240         /**
32241         * @event beforeexpand
32242         * Fires before a node is expanded, return false to cancel.
32243         * @param {Node} node The node
32244         * @param {Boolean} deep
32245         * @param {Boolean} anim
32246         */
32247         "beforeexpand" : true,
32248         /**
32249         * @event beforecollapse
32250         * Fires before a node is collapsed, return false to cancel.
32251         * @param {Node} node The node
32252         * @param {Boolean} deep
32253         * @param {Boolean} anim
32254         */
32255         "beforecollapse" : true,
32256         /**
32257         * @event expand
32258         * Fires when a node is expanded
32259         * @param {Node} node The node
32260         */
32261         "expand" : true,
32262         /**
32263         * @event disabledchange
32264         * Fires when the disabled status of a node changes
32265         * @param {Node} node The node
32266         * @param {Boolean} disabled
32267         */
32268         "disabledchange" : true,
32269         /**
32270         * @event collapse
32271         * Fires when a node is collapsed
32272         * @param {Node} node The node
32273         */
32274         "collapse" : true,
32275         /**
32276         * @event beforeclick
32277         * Fires before click processing on a node. Return false to cancel the default action.
32278         * @param {Node} node The node
32279         * @param {Roo.EventObject} e The event object
32280         */
32281         "beforeclick":true,
32282         /**
32283         * @event checkchange
32284         * Fires when a node with a checkbox's checked property changes
32285         * @param {Node} this This node
32286         * @param {Boolean} checked
32287         */
32288         "checkchange":true,
32289         /**
32290         * @event click
32291         * Fires when a node is clicked
32292         * @param {Node} node The node
32293         * @param {Roo.EventObject} e The event object
32294         */
32295         "click":true,
32296         /**
32297         * @event dblclick
32298         * Fires when a node is double clicked
32299         * @param {Node} node The node
32300         * @param {Roo.EventObject} e The event object
32301         */
32302         "dblclick":true,
32303         /**
32304         * @event contextmenu
32305         * Fires when a node is right clicked
32306         * @param {Node} node The node
32307         * @param {Roo.EventObject} e The event object
32308         */
32309         "contextmenu":true,
32310         /**
32311         * @event beforechildrenrendered
32312         * Fires right before the child nodes for a node are rendered
32313         * @param {Node} node The node
32314         */
32315         "beforechildrenrendered":true,
32316         /**
32317         * @event startdrag
32318         * Fires when a node starts being dragged
32319         * @param {Roo.tree.TreePanel} this
32320         * @param {Roo.tree.TreeNode} node
32321         * @param {event} e The raw browser event
32322         */ 
32323        "startdrag" : true,
32324        /**
32325         * @event enddrag
32326         * Fires when a drag operation is complete
32327         * @param {Roo.tree.TreePanel} this
32328         * @param {Roo.tree.TreeNode} node
32329         * @param {event} e The raw browser event
32330         */
32331        "enddrag" : true,
32332        /**
32333         * @event dragdrop
32334         * Fires when a dragged node is dropped on a valid DD target
32335         * @param {Roo.tree.TreePanel} this
32336         * @param {Roo.tree.TreeNode} node
32337         * @param {DD} dd The dd it was dropped on
32338         * @param {event} e The raw browser event
32339         */
32340        "dragdrop" : true,
32341        /**
32342         * @event beforenodedrop
32343         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32344         * passed to handlers has the following properties:<br />
32345         * <ul style="padding:5px;padding-left:16px;">
32346         * <li>tree - The TreePanel</li>
32347         * <li>target - The node being targeted for the drop</li>
32348         * <li>data - The drag data from the drag source</li>
32349         * <li>point - The point of the drop - append, above or below</li>
32350         * <li>source - The drag source</li>
32351         * <li>rawEvent - Raw mouse event</li>
32352         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32353         * to be inserted by setting them on this object.</li>
32354         * <li>cancel - Set this to true to cancel the drop.</li>
32355         * </ul>
32356         * @param {Object} dropEvent
32357         */
32358        "beforenodedrop" : true,
32359        /**
32360         * @event nodedrop
32361         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32362         * passed to handlers has the following properties:<br />
32363         * <ul style="padding:5px;padding-left:16px;">
32364         * <li>tree - The TreePanel</li>
32365         * <li>target - The node being targeted for the drop</li>
32366         * <li>data - The drag data from the drag source</li>
32367         * <li>point - The point of the drop - append, above or below</li>
32368         * <li>source - The drag source</li>
32369         * <li>rawEvent - Raw mouse event</li>
32370         * <li>dropNode - Dropped node(s).</li>
32371         * </ul>
32372         * @param {Object} dropEvent
32373         */
32374        "nodedrop" : true,
32375         /**
32376         * @event nodedragover
32377         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32378         * passed to handlers has the following properties:<br />
32379         * <ul style="padding:5px;padding-left:16px;">
32380         * <li>tree - The TreePanel</li>
32381         * <li>target - The node being targeted for the drop</li>
32382         * <li>data - The drag data from the drag source</li>
32383         * <li>point - The point of the drop - append, above or below</li>
32384         * <li>source - The drag source</li>
32385         * <li>rawEvent - Raw mouse event</li>
32386         * <li>dropNode - Drop node(s) provided by the source.</li>
32387         * <li>cancel - Set this to true to signal drop not allowed.</li>
32388         * </ul>
32389         * @param {Object} dragOverEvent
32390         */
32391        "nodedragover" : true
32392         
32393     });
32394     if(this.singleExpand){
32395        this.on("beforeexpand", this.restrictExpand, this);
32396     }
32397     if (this.editor) {
32398         this.editor.tree = this;
32399         this.editor = Roo.factory(this.editor, Roo.tree);
32400     }
32401     
32402     if (this.selModel) {
32403         this.selModel = Roo.factory(this.selModel, Roo.tree);
32404     }
32405    
32406 };
32407 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32408     rootVisible : true,
32409     animate: Roo.enableFx,
32410     lines : true,
32411     enableDD : false,
32412     hlDrop : Roo.enableFx,
32413   
32414     renderer: false,
32415     
32416     rendererTip: false,
32417     // private
32418     restrictExpand : function(node){
32419         var p = node.parentNode;
32420         if(p){
32421             if(p.expandedChild && p.expandedChild.parentNode == p){
32422                 p.expandedChild.collapse();
32423             }
32424             p.expandedChild = node;
32425         }
32426     },
32427
32428     // private override
32429     setRootNode : function(node){
32430         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32431         if(!this.rootVisible){
32432             node.ui = new Roo.tree.RootTreeNodeUI(node);
32433         }
32434         return node;
32435     },
32436
32437     /**
32438      * Returns the container element for this TreePanel
32439      */
32440     getEl : function(){
32441         return this.el;
32442     },
32443
32444     /**
32445      * Returns the default TreeLoader for this TreePanel
32446      */
32447     getLoader : function(){
32448         return this.loader;
32449     },
32450
32451     /**
32452      * Expand all nodes
32453      */
32454     expandAll : function(){
32455         this.root.expand(true);
32456     },
32457
32458     /**
32459      * Collapse all nodes
32460      */
32461     collapseAll : function(){
32462         this.root.collapse(true);
32463     },
32464
32465     /**
32466      * Returns the selection model used by this TreePanel
32467      */
32468     getSelectionModel : function(){
32469         if(!this.selModel){
32470             this.selModel = new Roo.tree.DefaultSelectionModel();
32471         }
32472         return this.selModel;
32473     },
32474
32475     /**
32476      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32477      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32478      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32479      * @return {Array}
32480      */
32481     getChecked : function(a, startNode){
32482         startNode = startNode || this.root;
32483         var r = [];
32484         var f = function(){
32485             if(this.attributes.checked){
32486                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32487             }
32488         }
32489         startNode.cascade(f);
32490         return r;
32491     },
32492
32493     /**
32494      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32495      * @param {String} path
32496      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32497      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32498      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32499      */
32500     expandPath : function(path, attr, callback){
32501         attr = attr || "id";
32502         var keys = path.split(this.pathSeparator);
32503         var curNode = this.root;
32504         if(curNode.attributes[attr] != keys[1]){ // invalid root
32505             if(callback){
32506                 callback(false, null);
32507             }
32508             return;
32509         }
32510         var index = 1;
32511         var f = function(){
32512             if(++index == keys.length){
32513                 if(callback){
32514                     callback(true, curNode);
32515                 }
32516                 return;
32517             }
32518             var c = curNode.findChild(attr, keys[index]);
32519             if(!c){
32520                 if(callback){
32521                     callback(false, curNode);
32522                 }
32523                 return;
32524             }
32525             curNode = c;
32526             c.expand(false, false, f);
32527         };
32528         curNode.expand(false, false, f);
32529     },
32530
32531     /**
32532      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32533      * @param {String} path
32534      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32535      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32536      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32537      */
32538     selectPath : function(path, attr, callback){
32539         attr = attr || "id";
32540         var keys = path.split(this.pathSeparator);
32541         var v = keys.pop();
32542         if(keys.length > 0){
32543             var f = function(success, node){
32544                 if(success && node){
32545                     var n = node.findChild(attr, v);
32546                     if(n){
32547                         n.select();
32548                         if(callback){
32549                             callback(true, n);
32550                         }
32551                     }else if(callback){
32552                         callback(false, n);
32553                     }
32554                 }else{
32555                     if(callback){
32556                         callback(false, n);
32557                     }
32558                 }
32559             };
32560             this.expandPath(keys.join(this.pathSeparator), attr, f);
32561         }else{
32562             this.root.select();
32563             if(callback){
32564                 callback(true, this.root);
32565             }
32566         }
32567     },
32568
32569     getTreeEl : function(){
32570         return this.el;
32571     },
32572
32573     /**
32574      * Trigger rendering of this TreePanel
32575      */
32576     render : function(){
32577         if (this.innerCt) {
32578             return this; // stop it rendering more than once!!
32579         }
32580         
32581         this.innerCt = this.el.createChild({tag:"ul",
32582                cls:"x-tree-root-ct " +
32583                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32584
32585         if(this.containerScroll){
32586             Roo.dd.ScrollManager.register(this.el);
32587         }
32588         if((this.enableDD || this.enableDrop) && !this.dropZone){
32589            /**
32590             * The dropZone used by this tree if drop is enabled
32591             * @type Roo.tree.TreeDropZone
32592             */
32593              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32594                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32595            });
32596         }
32597         if((this.enableDD || this.enableDrag) && !this.dragZone){
32598            /**
32599             * The dragZone used by this tree if drag is enabled
32600             * @type Roo.tree.TreeDragZone
32601             */
32602             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32603                ddGroup: this.ddGroup || "TreeDD",
32604                scroll: this.ddScroll
32605            });
32606         }
32607         this.getSelectionModel().init(this);
32608         if (!this.root) {
32609             Roo.log("ROOT not set in tree");
32610             return this;
32611         }
32612         this.root.render();
32613         if(!this.rootVisible){
32614             this.root.renderChildren();
32615         }
32616         return this;
32617     }
32618 });/*
32619  * Based on:
32620  * Ext JS Library 1.1.1
32621  * Copyright(c) 2006-2007, Ext JS, LLC.
32622  *
32623  * Originally Released Under LGPL - original licence link has changed is not relivant.
32624  *
32625  * Fork - LGPL
32626  * <script type="text/javascript">
32627  */
32628  
32629
32630 /**
32631  * @class Roo.tree.DefaultSelectionModel
32632  * @extends Roo.util.Observable
32633  * The default single selection for a TreePanel.
32634  * @param {Object} cfg Configuration
32635  */
32636 Roo.tree.DefaultSelectionModel = function(cfg){
32637    this.selNode = null;
32638    
32639    
32640    
32641    this.addEvents({
32642        /**
32643         * @event selectionchange
32644         * Fires when the selected node changes
32645         * @param {DefaultSelectionModel} this
32646         * @param {TreeNode} node the new selection
32647         */
32648        "selectionchange" : true,
32649
32650        /**
32651         * @event beforeselect
32652         * Fires before the selected node changes, return false to cancel the change
32653         * @param {DefaultSelectionModel} this
32654         * @param {TreeNode} node the new selection
32655         * @param {TreeNode} node the old selection
32656         */
32657        "beforeselect" : true
32658    });
32659    
32660     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32661 };
32662
32663 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32664     init : function(tree){
32665         this.tree = tree;
32666         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32667         tree.on("click", this.onNodeClick, this);
32668     },
32669     
32670     onNodeClick : function(node, e){
32671         if (e.ctrlKey && this.selNode == node)  {
32672             this.unselect(node);
32673             return;
32674         }
32675         this.select(node);
32676     },
32677     
32678     /**
32679      * Select a node.
32680      * @param {TreeNode} node The node to select
32681      * @return {TreeNode} The selected node
32682      */
32683     select : function(node){
32684         var last = this.selNode;
32685         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32686             if(last){
32687                 last.ui.onSelectedChange(false);
32688             }
32689             this.selNode = node;
32690             node.ui.onSelectedChange(true);
32691             this.fireEvent("selectionchange", this, node, last);
32692         }
32693         return node;
32694     },
32695     
32696     /**
32697      * Deselect a node.
32698      * @param {TreeNode} node The node to unselect
32699      */
32700     unselect : function(node){
32701         if(this.selNode == node){
32702             this.clearSelections();
32703         }    
32704     },
32705     
32706     /**
32707      * Clear all selections
32708      */
32709     clearSelections : function(){
32710         var n = this.selNode;
32711         if(n){
32712             n.ui.onSelectedChange(false);
32713             this.selNode = null;
32714             this.fireEvent("selectionchange", this, null);
32715         }
32716         return n;
32717     },
32718     
32719     /**
32720      * Get the selected node
32721      * @return {TreeNode} The selected node
32722      */
32723     getSelectedNode : function(){
32724         return this.selNode;    
32725     },
32726     
32727     /**
32728      * Returns true if the node is selected
32729      * @param {TreeNode} node The node to check
32730      * @return {Boolean}
32731      */
32732     isSelected : function(node){
32733         return this.selNode == node;  
32734     },
32735
32736     /**
32737      * Selects the node above the selected node in the tree, intelligently walking the nodes
32738      * @return TreeNode The new selection
32739      */
32740     selectPrevious : function(){
32741         var s = this.selNode || this.lastSelNode;
32742         if(!s){
32743             return null;
32744         }
32745         var ps = s.previousSibling;
32746         if(ps){
32747             if(!ps.isExpanded() || ps.childNodes.length < 1){
32748                 return this.select(ps);
32749             } else{
32750                 var lc = ps.lastChild;
32751                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32752                     lc = lc.lastChild;
32753                 }
32754                 return this.select(lc);
32755             }
32756         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32757             return this.select(s.parentNode);
32758         }
32759         return null;
32760     },
32761
32762     /**
32763      * Selects the node above the selected node in the tree, intelligently walking the nodes
32764      * @return TreeNode The new selection
32765      */
32766     selectNext : function(){
32767         var s = this.selNode || this.lastSelNode;
32768         if(!s){
32769             return null;
32770         }
32771         if(s.firstChild && s.isExpanded()){
32772              return this.select(s.firstChild);
32773          }else if(s.nextSibling){
32774              return this.select(s.nextSibling);
32775          }else if(s.parentNode){
32776             var newS = null;
32777             s.parentNode.bubble(function(){
32778                 if(this.nextSibling){
32779                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32780                     return false;
32781                 }
32782             });
32783             return newS;
32784          }
32785         return null;
32786     },
32787
32788     onKeyDown : function(e){
32789         var s = this.selNode || this.lastSelNode;
32790         // undesirable, but required
32791         var sm = this;
32792         if(!s){
32793             return;
32794         }
32795         var k = e.getKey();
32796         switch(k){
32797              case e.DOWN:
32798                  e.stopEvent();
32799                  this.selectNext();
32800              break;
32801              case e.UP:
32802                  e.stopEvent();
32803                  this.selectPrevious();
32804              break;
32805              case e.RIGHT:
32806                  e.preventDefault();
32807                  if(s.hasChildNodes()){
32808                      if(!s.isExpanded()){
32809                          s.expand();
32810                      }else if(s.firstChild){
32811                          this.select(s.firstChild, e);
32812                      }
32813                  }
32814              break;
32815              case e.LEFT:
32816                  e.preventDefault();
32817                  if(s.hasChildNodes() && s.isExpanded()){
32818                      s.collapse();
32819                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32820                      this.select(s.parentNode, e);
32821                  }
32822              break;
32823         };
32824     }
32825 });
32826
32827 /**
32828  * @class Roo.tree.MultiSelectionModel
32829  * @extends Roo.util.Observable
32830  * Multi selection for a TreePanel.
32831  * @param {Object} cfg Configuration
32832  */
32833 Roo.tree.MultiSelectionModel = function(){
32834    this.selNodes = [];
32835    this.selMap = {};
32836    this.addEvents({
32837        /**
32838         * @event selectionchange
32839         * Fires when the selected nodes change
32840         * @param {MultiSelectionModel} this
32841         * @param {Array} nodes Array of the selected nodes
32842         */
32843        "selectionchange" : true
32844    });
32845    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32846    
32847 };
32848
32849 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32850     init : function(tree){
32851         this.tree = tree;
32852         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32853         tree.on("click", this.onNodeClick, this);
32854     },
32855     
32856     onNodeClick : function(node, e){
32857         this.select(node, e, e.ctrlKey);
32858     },
32859     
32860     /**
32861      * Select a node.
32862      * @param {TreeNode} node The node to select
32863      * @param {EventObject} e (optional) An event associated with the selection
32864      * @param {Boolean} keepExisting True to retain existing selections
32865      * @return {TreeNode} The selected node
32866      */
32867     select : function(node, e, keepExisting){
32868         if(keepExisting !== true){
32869             this.clearSelections(true);
32870         }
32871         if(this.isSelected(node)){
32872             this.lastSelNode = node;
32873             return node;
32874         }
32875         this.selNodes.push(node);
32876         this.selMap[node.id] = node;
32877         this.lastSelNode = node;
32878         node.ui.onSelectedChange(true);
32879         this.fireEvent("selectionchange", this, this.selNodes);
32880         return node;
32881     },
32882     
32883     /**
32884      * Deselect a node.
32885      * @param {TreeNode} node The node to unselect
32886      */
32887     unselect : function(node){
32888         if(this.selMap[node.id]){
32889             node.ui.onSelectedChange(false);
32890             var sn = this.selNodes;
32891             var index = -1;
32892             if(sn.indexOf){
32893                 index = sn.indexOf(node);
32894             }else{
32895                 for(var i = 0, len = sn.length; i < len; i++){
32896                     if(sn[i] == node){
32897                         index = i;
32898                         break;
32899                     }
32900                 }
32901             }
32902             if(index != -1){
32903                 this.selNodes.splice(index, 1);
32904             }
32905             delete this.selMap[node.id];
32906             this.fireEvent("selectionchange", this, this.selNodes);
32907         }
32908     },
32909     
32910     /**
32911      * Clear all selections
32912      */
32913     clearSelections : function(suppressEvent){
32914         var sn = this.selNodes;
32915         if(sn.length > 0){
32916             for(var i = 0, len = sn.length; i < len; i++){
32917                 sn[i].ui.onSelectedChange(false);
32918             }
32919             this.selNodes = [];
32920             this.selMap = {};
32921             if(suppressEvent !== true){
32922                 this.fireEvent("selectionchange", this, this.selNodes);
32923             }
32924         }
32925     },
32926     
32927     /**
32928      * Returns true if the node is selected
32929      * @param {TreeNode} node The node to check
32930      * @return {Boolean}
32931      */
32932     isSelected : function(node){
32933         return this.selMap[node.id] ? true : false;  
32934     },
32935     
32936     /**
32937      * Returns an array of the selected nodes
32938      * @return {Array}
32939      */
32940     getSelectedNodes : function(){
32941         return this.selNodes;    
32942     },
32943
32944     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32945
32946     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32947
32948     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32949 });/*
32950  * Based on:
32951  * Ext JS Library 1.1.1
32952  * Copyright(c) 2006-2007, Ext JS, LLC.
32953  *
32954  * Originally Released Under LGPL - original licence link has changed is not relivant.
32955  *
32956  * Fork - LGPL
32957  * <script type="text/javascript">
32958  */
32959  
32960 /**
32961  * @class Roo.tree.TreeNode
32962  * @extends Roo.data.Node
32963  * @cfg {String} text The text for this node
32964  * @cfg {Boolean} expanded true to start the node expanded
32965  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32966  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32967  * @cfg {Boolean} disabled true to start the node disabled
32968  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32969  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32970  * @cfg {String} cls A css class to be added to the node
32971  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32972  * @cfg {String} href URL of the link used for the node (defaults to #)
32973  * @cfg {String} hrefTarget target frame for the link
32974  * @cfg {String} qtip An Ext QuickTip for the node
32975  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32976  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32977  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32978  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32979  * (defaults to undefined with no checkbox rendered)
32980  * @constructor
32981  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32982  */
32983 Roo.tree.TreeNode = function(attributes){
32984     attributes = attributes || {};
32985     if(typeof attributes == "string"){
32986         attributes = {text: attributes};
32987     }
32988     this.childrenRendered = false;
32989     this.rendered = false;
32990     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32991     this.expanded = attributes.expanded === true;
32992     this.isTarget = attributes.isTarget !== false;
32993     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32994     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32995
32996     /**
32997      * Read-only. The text for this node. To change it use setText().
32998      * @type String
32999      */
33000     this.text = attributes.text;
33001     /**
33002      * True if this node is disabled.
33003      * @type Boolean
33004      */
33005     this.disabled = attributes.disabled === true;
33006
33007     this.addEvents({
33008         /**
33009         * @event textchange
33010         * Fires when the text for this node is changed
33011         * @param {Node} this This node
33012         * @param {String} text The new text
33013         * @param {String} oldText The old text
33014         */
33015         "textchange" : true,
33016         /**
33017         * @event beforeexpand
33018         * Fires before this node is expanded, return false to cancel.
33019         * @param {Node} this This node
33020         * @param {Boolean} deep
33021         * @param {Boolean} anim
33022         */
33023         "beforeexpand" : true,
33024         /**
33025         * @event beforecollapse
33026         * Fires before this node is collapsed, return false to cancel.
33027         * @param {Node} this This node
33028         * @param {Boolean} deep
33029         * @param {Boolean} anim
33030         */
33031         "beforecollapse" : true,
33032         /**
33033         * @event expand
33034         * Fires when this node is expanded
33035         * @param {Node} this This node
33036         */
33037         "expand" : true,
33038         /**
33039         * @event disabledchange
33040         * Fires when the disabled status of this node changes
33041         * @param {Node} this This node
33042         * @param {Boolean} disabled
33043         */
33044         "disabledchange" : true,
33045         /**
33046         * @event collapse
33047         * Fires when this node is collapsed
33048         * @param {Node} this This node
33049         */
33050         "collapse" : true,
33051         /**
33052         * @event beforeclick
33053         * Fires before click processing. Return false to cancel the default action.
33054         * @param {Node} this This node
33055         * @param {Roo.EventObject} e The event object
33056         */
33057         "beforeclick":true,
33058         /**
33059         * @event checkchange
33060         * Fires when a node with a checkbox's checked property changes
33061         * @param {Node} this This node
33062         * @param {Boolean} checked
33063         */
33064         "checkchange":true,
33065         /**
33066         * @event click
33067         * Fires when this node is clicked
33068         * @param {Node} this This node
33069         * @param {Roo.EventObject} e The event object
33070         */
33071         "click":true,
33072         /**
33073         * @event dblclick
33074         * Fires when this node is double clicked
33075         * @param {Node} this This node
33076         * @param {Roo.EventObject} e The event object
33077         */
33078         "dblclick":true,
33079         /**
33080         * @event contextmenu
33081         * Fires when this node is right clicked
33082         * @param {Node} this This node
33083         * @param {Roo.EventObject} e The event object
33084         */
33085         "contextmenu":true,
33086         /**
33087         * @event beforechildrenrendered
33088         * Fires right before the child nodes for this node are rendered
33089         * @param {Node} this This node
33090         */
33091         "beforechildrenrendered":true
33092     });
33093
33094     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33095
33096     /**
33097      * Read-only. The UI for this node
33098      * @type TreeNodeUI
33099      */
33100     this.ui = new uiClass(this);
33101     
33102     // finally support items[]
33103     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33104         return;
33105     }
33106     
33107     
33108     Roo.each(this.attributes.items, function(c) {
33109         this.appendChild(Roo.factory(c,Roo.Tree));
33110     }, this);
33111     delete this.attributes.items;
33112     
33113     
33114     
33115 };
33116 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33117     preventHScroll: true,
33118     /**
33119      * Returns true if this node is expanded
33120      * @return {Boolean}
33121      */
33122     isExpanded : function(){
33123         return this.expanded;
33124     },
33125
33126     /**
33127      * Returns the UI object for this node
33128      * @return {TreeNodeUI}
33129      */
33130     getUI : function(){
33131         return this.ui;
33132     },
33133
33134     // private override
33135     setFirstChild : function(node){
33136         var of = this.firstChild;
33137         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33138         if(this.childrenRendered && of && node != of){
33139             of.renderIndent(true, true);
33140         }
33141         if(this.rendered){
33142             this.renderIndent(true, true);
33143         }
33144     },
33145
33146     // private override
33147     setLastChild : function(node){
33148         var ol = this.lastChild;
33149         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33150         if(this.childrenRendered && ol && node != ol){
33151             ol.renderIndent(true, true);
33152         }
33153         if(this.rendered){
33154             this.renderIndent(true, true);
33155         }
33156     },
33157
33158     // these methods are overridden to provide lazy rendering support
33159     // private override
33160     appendChild : function()
33161     {
33162         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33163         if(node && this.childrenRendered){
33164             node.render();
33165         }
33166         this.ui.updateExpandIcon();
33167         return node;
33168     },
33169
33170     // private override
33171     removeChild : function(node){
33172         this.ownerTree.getSelectionModel().unselect(node);
33173         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33174         // if it's been rendered remove dom node
33175         if(this.childrenRendered){
33176             node.ui.remove();
33177         }
33178         if(this.childNodes.length < 1){
33179             this.collapse(false, false);
33180         }else{
33181             this.ui.updateExpandIcon();
33182         }
33183         if(!this.firstChild) {
33184             this.childrenRendered = false;
33185         }
33186         return node;
33187     },
33188
33189     // private override
33190     insertBefore : function(node, refNode){
33191         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33192         if(newNode && refNode && this.childrenRendered){
33193             node.render();
33194         }
33195         this.ui.updateExpandIcon();
33196         return newNode;
33197     },
33198
33199     /**
33200      * Sets the text for this node
33201      * @param {String} text
33202      */
33203     setText : function(text){
33204         var oldText = this.text;
33205         this.text = text;
33206         this.attributes.text = text;
33207         if(this.rendered){ // event without subscribing
33208             this.ui.onTextChange(this, text, oldText);
33209         }
33210         this.fireEvent("textchange", this, text, oldText);
33211     },
33212
33213     /**
33214      * Triggers selection of this node
33215      */
33216     select : function(){
33217         this.getOwnerTree().getSelectionModel().select(this);
33218     },
33219
33220     /**
33221      * Triggers deselection of this node
33222      */
33223     unselect : function(){
33224         this.getOwnerTree().getSelectionModel().unselect(this);
33225     },
33226
33227     /**
33228      * Returns true if this node is selected
33229      * @return {Boolean}
33230      */
33231     isSelected : function(){
33232         return this.getOwnerTree().getSelectionModel().isSelected(this);
33233     },
33234
33235     /**
33236      * Expand this node.
33237      * @param {Boolean} deep (optional) True to expand all children as well
33238      * @param {Boolean} anim (optional) false to cancel the default animation
33239      * @param {Function} callback (optional) A callback to be called when
33240      * expanding this node completes (does not wait for deep expand to complete).
33241      * Called with 1 parameter, this node.
33242      */
33243     expand : function(deep, anim, callback){
33244         if(!this.expanded){
33245             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33246                 return;
33247             }
33248             if(!this.childrenRendered){
33249                 this.renderChildren();
33250             }
33251             this.expanded = true;
33252             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33253                 this.ui.animExpand(function(){
33254                     this.fireEvent("expand", this);
33255                     if(typeof callback == "function"){
33256                         callback(this);
33257                     }
33258                     if(deep === true){
33259                         this.expandChildNodes(true);
33260                     }
33261                 }.createDelegate(this));
33262                 return;
33263             }else{
33264                 this.ui.expand();
33265                 this.fireEvent("expand", this);
33266                 if(typeof callback == "function"){
33267                     callback(this);
33268                 }
33269             }
33270         }else{
33271            if(typeof callback == "function"){
33272                callback(this);
33273            }
33274         }
33275         if(deep === true){
33276             this.expandChildNodes(true);
33277         }
33278     },
33279
33280     isHiddenRoot : function(){
33281         return this.isRoot && !this.getOwnerTree().rootVisible;
33282     },
33283
33284     /**
33285      * Collapse this node.
33286      * @param {Boolean} deep (optional) True to collapse all children as well
33287      * @param {Boolean} anim (optional) false to cancel the default animation
33288      */
33289     collapse : function(deep, anim){
33290         if(this.expanded && !this.isHiddenRoot()){
33291             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33292                 return;
33293             }
33294             this.expanded = false;
33295             if((this.getOwnerTree().animate && anim !== false) || anim){
33296                 this.ui.animCollapse(function(){
33297                     this.fireEvent("collapse", this);
33298                     if(deep === true){
33299                         this.collapseChildNodes(true);
33300                     }
33301                 }.createDelegate(this));
33302                 return;
33303             }else{
33304                 this.ui.collapse();
33305                 this.fireEvent("collapse", this);
33306             }
33307         }
33308         if(deep === true){
33309             var cs = this.childNodes;
33310             for(var i = 0, len = cs.length; i < len; i++) {
33311                 cs[i].collapse(true, false);
33312             }
33313         }
33314     },
33315
33316     // private
33317     delayedExpand : function(delay){
33318         if(!this.expandProcId){
33319             this.expandProcId = this.expand.defer(delay, this);
33320         }
33321     },
33322
33323     // private
33324     cancelExpand : function(){
33325         if(this.expandProcId){
33326             clearTimeout(this.expandProcId);
33327         }
33328         this.expandProcId = false;
33329     },
33330
33331     /**
33332      * Toggles expanded/collapsed state of the node
33333      */
33334     toggle : function(){
33335         if(this.expanded){
33336             this.collapse();
33337         }else{
33338             this.expand();
33339         }
33340     },
33341
33342     /**
33343      * Ensures all parent nodes are expanded
33344      */
33345     ensureVisible : function(callback){
33346         var tree = this.getOwnerTree();
33347         tree.expandPath(this.parentNode.getPath(), false, function(){
33348             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33349             Roo.callback(callback);
33350         }.createDelegate(this));
33351     },
33352
33353     /**
33354      * Expand all child nodes
33355      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33356      */
33357     expandChildNodes : function(deep){
33358         var cs = this.childNodes;
33359         for(var i = 0, len = cs.length; i < len; i++) {
33360                 cs[i].expand(deep);
33361         }
33362     },
33363
33364     /**
33365      * Collapse all child nodes
33366      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33367      */
33368     collapseChildNodes : function(deep){
33369         var cs = this.childNodes;
33370         for(var i = 0, len = cs.length; i < len; i++) {
33371                 cs[i].collapse(deep);
33372         }
33373     },
33374
33375     /**
33376      * Disables this node
33377      */
33378     disable : function(){
33379         this.disabled = true;
33380         this.unselect();
33381         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33382             this.ui.onDisableChange(this, true);
33383         }
33384         this.fireEvent("disabledchange", this, true);
33385     },
33386
33387     /**
33388      * Enables this node
33389      */
33390     enable : function(){
33391         this.disabled = false;
33392         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33393             this.ui.onDisableChange(this, false);
33394         }
33395         this.fireEvent("disabledchange", this, false);
33396     },
33397
33398     // private
33399     renderChildren : function(suppressEvent){
33400         if(suppressEvent !== false){
33401             this.fireEvent("beforechildrenrendered", this);
33402         }
33403         var cs = this.childNodes;
33404         for(var i = 0, len = cs.length; i < len; i++){
33405             cs[i].render(true);
33406         }
33407         this.childrenRendered = true;
33408     },
33409
33410     // private
33411     sort : function(fn, scope){
33412         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33413         if(this.childrenRendered){
33414             var cs = this.childNodes;
33415             for(var i = 0, len = cs.length; i < len; i++){
33416                 cs[i].render(true);
33417             }
33418         }
33419     },
33420
33421     // private
33422     render : function(bulkRender){
33423         this.ui.render(bulkRender);
33424         if(!this.rendered){
33425             this.rendered = true;
33426             if(this.expanded){
33427                 this.expanded = false;
33428                 this.expand(false, false);
33429             }
33430         }
33431     },
33432
33433     // private
33434     renderIndent : function(deep, refresh){
33435         if(refresh){
33436             this.ui.childIndent = null;
33437         }
33438         this.ui.renderIndent();
33439         if(deep === true && this.childrenRendered){
33440             var cs = this.childNodes;
33441             for(var i = 0, len = cs.length; i < len; i++){
33442                 cs[i].renderIndent(true, refresh);
33443             }
33444         }
33445     }
33446 });/*
33447  * Based on:
33448  * Ext JS Library 1.1.1
33449  * Copyright(c) 2006-2007, Ext JS, LLC.
33450  *
33451  * Originally Released Under LGPL - original licence link has changed is not relivant.
33452  *
33453  * Fork - LGPL
33454  * <script type="text/javascript">
33455  */
33456  
33457 /**
33458  * @class Roo.tree.AsyncTreeNode
33459  * @extends Roo.tree.TreeNode
33460  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33461  * @constructor
33462  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33463  */
33464  Roo.tree.AsyncTreeNode = function(config){
33465     this.loaded = false;
33466     this.loading = false;
33467     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33468     /**
33469     * @event beforeload
33470     * Fires before this node is loaded, return false to cancel
33471     * @param {Node} this This node
33472     */
33473     this.addEvents({'beforeload':true, 'load': true});
33474     /**
33475     * @event load
33476     * Fires when this node is loaded
33477     * @param {Node} this This node
33478     */
33479     /**
33480      * The loader used by this node (defaults to using the tree's defined loader)
33481      * @type TreeLoader
33482      * @property loader
33483      */
33484 };
33485 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33486     expand : function(deep, anim, callback){
33487         if(this.loading){ // if an async load is already running, waiting til it's done
33488             var timer;
33489             var f = function(){
33490                 if(!this.loading){ // done loading
33491                     clearInterval(timer);
33492                     this.expand(deep, anim, callback);
33493                 }
33494             }.createDelegate(this);
33495             timer = setInterval(f, 200);
33496             return;
33497         }
33498         if(!this.loaded){
33499             if(this.fireEvent("beforeload", this) === false){
33500                 return;
33501             }
33502             this.loading = true;
33503             this.ui.beforeLoad(this);
33504             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33505             if(loader){
33506                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33507                 return;
33508             }
33509         }
33510         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33511     },
33512     
33513     /**
33514      * Returns true if this node is currently loading
33515      * @return {Boolean}
33516      */
33517     isLoading : function(){
33518         return this.loading;  
33519     },
33520     
33521     loadComplete : function(deep, anim, callback){
33522         this.loading = false;
33523         this.loaded = true;
33524         this.ui.afterLoad(this);
33525         this.fireEvent("load", this);
33526         this.expand(deep, anim, callback);
33527     },
33528     
33529     /**
33530      * Returns true if this node has been loaded
33531      * @return {Boolean}
33532      */
33533     isLoaded : function(){
33534         return this.loaded;
33535     },
33536     
33537     hasChildNodes : function(){
33538         if(!this.isLeaf() && !this.loaded){
33539             return true;
33540         }else{
33541             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33542         }
33543     },
33544
33545     /**
33546      * Trigger a reload for this node
33547      * @param {Function} callback
33548      */
33549     reload : function(callback){
33550         this.collapse(false, false);
33551         while(this.firstChild){
33552             this.removeChild(this.firstChild);
33553         }
33554         this.childrenRendered = false;
33555         this.loaded = false;
33556         if(this.isHiddenRoot()){
33557             this.expanded = false;
33558         }
33559         this.expand(false, false, callback);
33560     }
33561 });/*
33562  * Based on:
33563  * Ext JS Library 1.1.1
33564  * Copyright(c) 2006-2007, Ext JS, LLC.
33565  *
33566  * Originally Released Under LGPL - original licence link has changed is not relivant.
33567  *
33568  * Fork - LGPL
33569  * <script type="text/javascript">
33570  */
33571  
33572 /**
33573  * @class Roo.tree.TreeNodeUI
33574  * @constructor
33575  * @param {Object} node The node to render
33576  * The TreeNode UI implementation is separate from the
33577  * tree implementation. Unless you are customizing the tree UI,
33578  * you should never have to use this directly.
33579  */
33580 Roo.tree.TreeNodeUI = function(node){
33581     this.node = node;
33582     this.rendered = false;
33583     this.animating = false;
33584     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33585 };
33586
33587 Roo.tree.TreeNodeUI.prototype = {
33588     removeChild : function(node){
33589         if(this.rendered){
33590             this.ctNode.removeChild(node.ui.getEl());
33591         }
33592     },
33593
33594     beforeLoad : function(){
33595          this.addClass("x-tree-node-loading");
33596     },
33597
33598     afterLoad : function(){
33599          this.removeClass("x-tree-node-loading");
33600     },
33601
33602     onTextChange : function(node, text, oldText){
33603         if(this.rendered){
33604             this.textNode.innerHTML = text;
33605         }
33606     },
33607
33608     onDisableChange : function(node, state){
33609         this.disabled = state;
33610         if(state){
33611             this.addClass("x-tree-node-disabled");
33612         }else{
33613             this.removeClass("x-tree-node-disabled");
33614         }
33615     },
33616
33617     onSelectedChange : function(state){
33618         if(state){
33619             this.focus();
33620             this.addClass("x-tree-selected");
33621         }else{
33622             //this.blur();
33623             this.removeClass("x-tree-selected");
33624         }
33625     },
33626
33627     onMove : function(tree, node, oldParent, newParent, index, refNode){
33628         this.childIndent = null;
33629         if(this.rendered){
33630             var targetNode = newParent.ui.getContainer();
33631             if(!targetNode){//target not rendered
33632                 this.holder = document.createElement("div");
33633                 this.holder.appendChild(this.wrap);
33634                 return;
33635             }
33636             var insertBefore = refNode ? refNode.ui.getEl() : null;
33637             if(insertBefore){
33638                 targetNode.insertBefore(this.wrap, insertBefore);
33639             }else{
33640                 targetNode.appendChild(this.wrap);
33641             }
33642             this.node.renderIndent(true);
33643         }
33644     },
33645
33646     addClass : function(cls){
33647         if(this.elNode){
33648             Roo.fly(this.elNode).addClass(cls);
33649         }
33650     },
33651
33652     removeClass : function(cls){
33653         if(this.elNode){
33654             Roo.fly(this.elNode).removeClass(cls);
33655         }
33656     },
33657
33658     remove : function(){
33659         if(this.rendered){
33660             this.holder = document.createElement("div");
33661             this.holder.appendChild(this.wrap);
33662         }
33663     },
33664
33665     fireEvent : function(){
33666         return this.node.fireEvent.apply(this.node, arguments);
33667     },
33668
33669     initEvents : function(){
33670         this.node.on("move", this.onMove, this);
33671         var E = Roo.EventManager;
33672         var a = this.anchor;
33673
33674         var el = Roo.fly(a, '_treeui');
33675
33676         if(Roo.isOpera){ // opera render bug ignores the CSS
33677             el.setStyle("text-decoration", "none");
33678         }
33679
33680         el.on("click", this.onClick, this);
33681         el.on("dblclick", this.onDblClick, this);
33682
33683         if(this.checkbox){
33684             Roo.EventManager.on(this.checkbox,
33685                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33686         }
33687
33688         el.on("contextmenu", this.onContextMenu, this);
33689
33690         var icon = Roo.fly(this.iconNode);
33691         icon.on("click", this.onClick, this);
33692         icon.on("dblclick", this.onDblClick, this);
33693         icon.on("contextmenu", this.onContextMenu, this);
33694         E.on(this.ecNode, "click", this.ecClick, this, true);
33695
33696         if(this.node.disabled){
33697             this.addClass("x-tree-node-disabled");
33698         }
33699         if(this.node.hidden){
33700             this.addClass("x-tree-node-disabled");
33701         }
33702         var ot = this.node.getOwnerTree();
33703         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33704         if(dd && (!this.node.isRoot || ot.rootVisible)){
33705             Roo.dd.Registry.register(this.elNode, {
33706                 node: this.node,
33707                 handles: this.getDDHandles(),
33708                 isHandle: false
33709             });
33710         }
33711     },
33712
33713     getDDHandles : function(){
33714         return [this.iconNode, this.textNode];
33715     },
33716
33717     hide : function(){
33718         if(this.rendered){
33719             this.wrap.style.display = "none";
33720         }
33721     },
33722
33723     show : function(){
33724         if(this.rendered){
33725             this.wrap.style.display = "";
33726         }
33727     },
33728
33729     onContextMenu : function(e){
33730         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33731             e.preventDefault();
33732             this.focus();
33733             this.fireEvent("contextmenu", this.node, e);
33734         }
33735     },
33736
33737     onClick : function(e){
33738         if(this.dropping){
33739             e.stopEvent();
33740             return;
33741         }
33742         if(this.fireEvent("beforeclick", this.node, e) !== false){
33743             if(!this.disabled && this.node.attributes.href){
33744                 this.fireEvent("click", this.node, e);
33745                 return;
33746             }
33747             e.preventDefault();
33748             if(this.disabled){
33749                 return;
33750             }
33751
33752             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33753                 this.node.toggle();
33754             }
33755
33756             this.fireEvent("click", this.node, e);
33757         }else{
33758             e.stopEvent();
33759         }
33760     },
33761
33762     onDblClick : function(e){
33763         e.preventDefault();
33764         if(this.disabled){
33765             return;
33766         }
33767         if(this.checkbox){
33768             this.toggleCheck();
33769         }
33770         if(!this.animating && this.node.hasChildNodes()){
33771             this.node.toggle();
33772         }
33773         this.fireEvent("dblclick", this.node, e);
33774     },
33775
33776     onCheckChange : function(){
33777         var checked = this.checkbox.checked;
33778         this.node.attributes.checked = checked;
33779         this.fireEvent('checkchange', this.node, checked);
33780     },
33781
33782     ecClick : function(e){
33783         if(!this.animating && this.node.hasChildNodes()){
33784             this.node.toggle();
33785         }
33786     },
33787
33788     startDrop : function(){
33789         this.dropping = true;
33790     },
33791
33792     // delayed drop so the click event doesn't get fired on a drop
33793     endDrop : function(){
33794        setTimeout(function(){
33795            this.dropping = false;
33796        }.createDelegate(this), 50);
33797     },
33798
33799     expand : function(){
33800         this.updateExpandIcon();
33801         this.ctNode.style.display = "";
33802     },
33803
33804     focus : function(){
33805         if(!this.node.preventHScroll){
33806             try{this.anchor.focus();
33807             }catch(e){}
33808         }else if(!Roo.isIE){
33809             try{
33810                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33811                 var l = noscroll.scrollLeft;
33812                 this.anchor.focus();
33813                 noscroll.scrollLeft = l;
33814             }catch(e){}
33815         }
33816     },
33817
33818     toggleCheck : function(value){
33819         var cb = this.checkbox;
33820         if(cb){
33821             cb.checked = (value === undefined ? !cb.checked : value);
33822         }
33823     },
33824
33825     blur : function(){
33826         try{
33827             this.anchor.blur();
33828         }catch(e){}
33829     },
33830
33831     animExpand : function(callback){
33832         var ct = Roo.get(this.ctNode);
33833         ct.stopFx();
33834         if(!this.node.hasChildNodes()){
33835             this.updateExpandIcon();
33836             this.ctNode.style.display = "";
33837             Roo.callback(callback);
33838             return;
33839         }
33840         this.animating = true;
33841         this.updateExpandIcon();
33842
33843         ct.slideIn('t', {
33844            callback : function(){
33845                this.animating = false;
33846                Roo.callback(callback);
33847             },
33848             scope: this,
33849             duration: this.node.ownerTree.duration || .25
33850         });
33851     },
33852
33853     highlight : function(){
33854         var tree = this.node.getOwnerTree();
33855         Roo.fly(this.wrap).highlight(
33856             tree.hlColor || "C3DAF9",
33857             {endColor: tree.hlBaseColor}
33858         );
33859     },
33860
33861     collapse : function(){
33862         this.updateExpandIcon();
33863         this.ctNode.style.display = "none";
33864     },
33865
33866     animCollapse : function(callback){
33867         var ct = Roo.get(this.ctNode);
33868         ct.enableDisplayMode('block');
33869         ct.stopFx();
33870
33871         this.animating = true;
33872         this.updateExpandIcon();
33873
33874         ct.slideOut('t', {
33875             callback : function(){
33876                this.animating = false;
33877                Roo.callback(callback);
33878             },
33879             scope: this,
33880             duration: this.node.ownerTree.duration || .25
33881         });
33882     },
33883
33884     getContainer : function(){
33885         return this.ctNode;
33886     },
33887
33888     getEl : function(){
33889         return this.wrap;
33890     },
33891
33892     appendDDGhost : function(ghostNode){
33893         ghostNode.appendChild(this.elNode.cloneNode(true));
33894     },
33895
33896     getDDRepairXY : function(){
33897         return Roo.lib.Dom.getXY(this.iconNode);
33898     },
33899
33900     onRender : function(){
33901         this.render();
33902     },
33903
33904     render : function(bulkRender){
33905         var n = this.node, a = n.attributes;
33906         var targetNode = n.parentNode ?
33907               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33908
33909         if(!this.rendered){
33910             this.rendered = true;
33911
33912             this.renderElements(n, a, targetNode, bulkRender);
33913
33914             if(a.qtip){
33915                if(this.textNode.setAttributeNS){
33916                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33917                    if(a.qtipTitle){
33918                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33919                    }
33920                }else{
33921                    this.textNode.setAttribute("ext:qtip", a.qtip);
33922                    if(a.qtipTitle){
33923                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33924                    }
33925                }
33926             }else if(a.qtipCfg){
33927                 a.qtipCfg.target = Roo.id(this.textNode);
33928                 Roo.QuickTips.register(a.qtipCfg);
33929             }
33930             this.initEvents();
33931             if(!this.node.expanded){
33932                 this.updateExpandIcon();
33933             }
33934         }else{
33935             if(bulkRender === true) {
33936                 targetNode.appendChild(this.wrap);
33937             }
33938         }
33939     },
33940
33941     renderElements : function(n, a, targetNode, bulkRender)
33942     {
33943         // add some indent caching, this helps performance when rendering a large tree
33944         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33945         var t = n.getOwnerTree();
33946         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33947         if (typeof(n.attributes.html) != 'undefined') {
33948             txt = n.attributes.html;
33949         }
33950         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33951         var cb = typeof a.checked == 'boolean';
33952         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33953         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33954             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33955             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33956             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33957             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33958             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33959              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33960                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33961             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33962             "</li>"];
33963
33964         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33965             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33966                                 n.nextSibling.ui.getEl(), buf.join(""));
33967         }else{
33968             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33969         }
33970
33971         this.elNode = this.wrap.childNodes[0];
33972         this.ctNode = this.wrap.childNodes[1];
33973         var cs = this.elNode.childNodes;
33974         this.indentNode = cs[0];
33975         this.ecNode = cs[1];
33976         this.iconNode = cs[2];
33977         var index = 3;
33978         if(cb){
33979             this.checkbox = cs[3];
33980             index++;
33981         }
33982         this.anchor = cs[index];
33983         this.textNode = cs[index].firstChild;
33984     },
33985
33986     getAnchor : function(){
33987         return this.anchor;
33988     },
33989
33990     getTextEl : function(){
33991         return this.textNode;
33992     },
33993
33994     getIconEl : function(){
33995         return this.iconNode;
33996     },
33997
33998     isChecked : function(){
33999         return this.checkbox ? this.checkbox.checked : false;
34000     },
34001
34002     updateExpandIcon : function(){
34003         if(this.rendered){
34004             var n = this.node, c1, c2;
34005             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34006             var hasChild = n.hasChildNodes();
34007             if(hasChild){
34008                 if(n.expanded){
34009                     cls += "-minus";
34010                     c1 = "x-tree-node-collapsed";
34011                     c2 = "x-tree-node-expanded";
34012                 }else{
34013                     cls += "-plus";
34014                     c1 = "x-tree-node-expanded";
34015                     c2 = "x-tree-node-collapsed";
34016                 }
34017                 if(this.wasLeaf){
34018                     this.removeClass("x-tree-node-leaf");
34019                     this.wasLeaf = false;
34020                 }
34021                 if(this.c1 != c1 || this.c2 != c2){
34022                     Roo.fly(this.elNode).replaceClass(c1, c2);
34023                     this.c1 = c1; this.c2 = c2;
34024                 }
34025             }else{
34026                 // this changes non-leafs into leafs if they have no children.
34027                 // it's not very rational behaviour..
34028                 
34029                 if(!this.wasLeaf && this.node.leaf){
34030                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34031                     delete this.c1;
34032                     delete this.c2;
34033                     this.wasLeaf = true;
34034                 }
34035             }
34036             var ecc = "x-tree-ec-icon "+cls;
34037             if(this.ecc != ecc){
34038                 this.ecNode.className = ecc;
34039                 this.ecc = ecc;
34040             }
34041         }
34042     },
34043
34044     getChildIndent : function(){
34045         if(!this.childIndent){
34046             var buf = [];
34047             var p = this.node;
34048             while(p){
34049                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34050                     if(!p.isLast()) {
34051                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34052                     } else {
34053                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34054                     }
34055                 }
34056                 p = p.parentNode;
34057             }
34058             this.childIndent = buf.join("");
34059         }
34060         return this.childIndent;
34061     },
34062
34063     renderIndent : function(){
34064         if(this.rendered){
34065             var indent = "";
34066             var p = this.node.parentNode;
34067             if(p){
34068                 indent = p.ui.getChildIndent();
34069             }
34070             if(this.indentMarkup != indent){ // don't rerender if not required
34071                 this.indentNode.innerHTML = indent;
34072                 this.indentMarkup = indent;
34073             }
34074             this.updateExpandIcon();
34075         }
34076     }
34077 };
34078
34079 Roo.tree.RootTreeNodeUI = function(){
34080     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34081 };
34082 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34083     render : function(){
34084         if(!this.rendered){
34085             var targetNode = this.node.ownerTree.innerCt.dom;
34086             this.node.expanded = true;
34087             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34088             this.wrap = this.ctNode = targetNode.firstChild;
34089         }
34090     },
34091     collapse : function(){
34092     },
34093     expand : function(){
34094     }
34095 });/*
34096  * Based on:
34097  * Ext JS Library 1.1.1
34098  * Copyright(c) 2006-2007, Ext JS, LLC.
34099  *
34100  * Originally Released Under LGPL - original licence link has changed is not relivant.
34101  *
34102  * Fork - LGPL
34103  * <script type="text/javascript">
34104  */
34105 /**
34106  * @class Roo.tree.TreeLoader
34107  * @extends Roo.util.Observable
34108  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34109  * nodes from a specified URL. The response must be a javascript Array definition
34110  * who's elements are node definition objects. eg:
34111  * <pre><code>
34112 {  success : true,
34113    data :      [
34114    
34115     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34116     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34117     ]
34118 }
34119
34120
34121 </code></pre>
34122  * <br><br>
34123  * The old style respose with just an array is still supported, but not recommended.
34124  * <br><br>
34125  *
34126  * A server request is sent, and child nodes are loaded only when a node is expanded.
34127  * The loading node's id is passed to the server under the parameter name "node" to
34128  * enable the server to produce the correct child nodes.
34129  * <br><br>
34130  * To pass extra parameters, an event handler may be attached to the "beforeload"
34131  * event, and the parameters specified in the TreeLoader's baseParams property:
34132  * <pre><code>
34133     myTreeLoader.on("beforeload", function(treeLoader, node) {
34134         this.baseParams.category = node.attributes.category;
34135     }, this);
34136 </code></pre><
34137  * This would pass an HTTP parameter called "category" to the server containing
34138  * the value of the Node's "category" attribute.
34139  * @constructor
34140  * Creates a new Treeloader.
34141  * @param {Object} config A config object containing config properties.
34142  */
34143 Roo.tree.TreeLoader = function(config){
34144     this.baseParams = {};
34145     this.requestMethod = "POST";
34146     Roo.apply(this, config);
34147
34148     this.addEvents({
34149     
34150         /**
34151          * @event beforeload
34152          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34153          * @param {Object} This TreeLoader object.
34154          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34155          * @param {Object} callback The callback function specified in the {@link #load} call.
34156          */
34157         beforeload : true,
34158         /**
34159          * @event load
34160          * Fires when the node has been successfuly loaded.
34161          * @param {Object} This TreeLoader object.
34162          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34163          * @param {Object} response The response object containing the data from the server.
34164          */
34165         load : true,
34166         /**
34167          * @event loadexception
34168          * Fires if the network request failed.
34169          * @param {Object} This TreeLoader object.
34170          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34171          * @param {Object} response The response object containing the data from the server.
34172          */
34173         loadexception : true,
34174         /**
34175          * @event create
34176          * Fires before a node is created, enabling you to return custom Node types 
34177          * @param {Object} This TreeLoader object.
34178          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34179          */
34180         create : true
34181     });
34182
34183     Roo.tree.TreeLoader.superclass.constructor.call(this);
34184 };
34185
34186 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34187     /**
34188     * @cfg {String} dataUrl The URL from which to request a Json string which
34189     * specifies an array of node definition object representing the child nodes
34190     * to be loaded.
34191     */
34192     /**
34193     * @cfg {String} requestMethod either GET or POST
34194     * defaults to POST (due to BC)
34195     * to be loaded.
34196     */
34197     /**
34198     * @cfg {Object} baseParams (optional) An object containing properties which
34199     * specify HTTP parameters to be passed to each request for child nodes.
34200     */
34201     /**
34202     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34203     * created by this loader. If the attributes sent by the server have an attribute in this object,
34204     * they take priority.
34205     */
34206     /**
34207     * @cfg {Object} uiProviders (optional) An object containing properties which
34208     * 
34209     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34210     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34211     * <i>uiProvider</i> attribute of a returned child node is a string rather
34212     * than a reference to a TreeNodeUI implementation, this that string value
34213     * is used as a property name in the uiProviders object. You can define the provider named
34214     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34215     */
34216     uiProviders : {},
34217
34218     /**
34219     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34220     * child nodes before loading.
34221     */
34222     clearOnLoad : true,
34223
34224     /**
34225     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34226     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34227     * Grid query { data : [ .....] }
34228     */
34229     
34230     root : false,
34231      /**
34232     * @cfg {String} queryParam (optional) 
34233     * Name of the query as it will be passed on the querystring (defaults to 'node')
34234     * eg. the request will be ?node=[id]
34235     */
34236     
34237     
34238     queryParam: false,
34239     
34240     /**
34241      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34242      * This is called automatically when a node is expanded, but may be used to reload
34243      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34244      * @param {Roo.tree.TreeNode} node
34245      * @param {Function} callback
34246      */
34247     load : function(node, callback){
34248         if(this.clearOnLoad){
34249             while(node.firstChild){
34250                 node.removeChild(node.firstChild);
34251             }
34252         }
34253         if(node.attributes.children){ // preloaded json children
34254             var cs = node.attributes.children;
34255             for(var i = 0, len = cs.length; i < len; i++){
34256                 node.appendChild(this.createNode(cs[i]));
34257             }
34258             if(typeof callback == "function"){
34259                 callback();
34260             }
34261         }else if(this.dataUrl){
34262             this.requestData(node, callback);
34263         }
34264     },
34265
34266     getParams: function(node){
34267         var buf = [], bp = this.baseParams;
34268         for(var key in bp){
34269             if(typeof bp[key] != "function"){
34270                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34271             }
34272         }
34273         var n = this.queryParam === false ? 'node' : this.queryParam;
34274         buf.push(n + "=", encodeURIComponent(node.id));
34275         return buf.join("");
34276     },
34277
34278     requestData : function(node, callback){
34279         if(this.fireEvent("beforeload", this, node, callback) !== false){
34280             this.transId = Roo.Ajax.request({
34281                 method:this.requestMethod,
34282                 url: this.dataUrl||this.url,
34283                 success: this.handleResponse,
34284                 failure: this.handleFailure,
34285                 scope: this,
34286                 argument: {callback: callback, node: node},
34287                 params: this.getParams(node)
34288             });
34289         }else{
34290             // if the load is cancelled, make sure we notify
34291             // the node that we are done
34292             if(typeof callback == "function"){
34293                 callback();
34294             }
34295         }
34296     },
34297
34298     isLoading : function(){
34299         return this.transId ? true : false;
34300     },
34301
34302     abort : function(){
34303         if(this.isLoading()){
34304             Roo.Ajax.abort(this.transId);
34305         }
34306     },
34307
34308     // private
34309     createNode : function(attr)
34310     {
34311         // apply baseAttrs, nice idea Corey!
34312         if(this.baseAttrs){
34313             Roo.applyIf(attr, this.baseAttrs);
34314         }
34315         if(this.applyLoader !== false){
34316             attr.loader = this;
34317         }
34318         // uiProvider = depreciated..
34319         
34320         if(typeof(attr.uiProvider) == 'string'){
34321            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34322                 /**  eval:var:attr */ eval(attr.uiProvider);
34323         }
34324         if(typeof(this.uiProviders['default']) != 'undefined') {
34325             attr.uiProvider = this.uiProviders['default'];
34326         }
34327         
34328         this.fireEvent('create', this, attr);
34329         
34330         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34331         return(attr.leaf ?
34332                         new Roo.tree.TreeNode(attr) :
34333                         new Roo.tree.AsyncTreeNode(attr));
34334     },
34335
34336     processResponse : function(response, node, callback)
34337     {
34338         var json = response.responseText;
34339         try {
34340             
34341             var o = Roo.decode(json);
34342             
34343             if (this.root === false && typeof(o.success) != undefined) {
34344                 this.root = 'data'; // the default behaviour for list like data..
34345                 }
34346                 
34347             if (this.root !== false &&  !o.success) {
34348                 // it's a failure condition.
34349                 var a = response.argument;
34350                 this.fireEvent("loadexception", this, a.node, response);
34351                 Roo.log("Load failed - should have a handler really");
34352                 return;
34353             }
34354             
34355             
34356             
34357             if (this.root !== false) {
34358                  o = o[this.root];
34359             }
34360             
34361             for(var i = 0, len = o.length; i < len; i++){
34362                 var n = this.createNode(o[i]);
34363                 if(n){
34364                     node.appendChild(n);
34365                 }
34366             }
34367             if(typeof callback == "function"){
34368                 callback(this, node);
34369             }
34370         }catch(e){
34371             this.handleFailure(response);
34372         }
34373     },
34374
34375     handleResponse : function(response){
34376         this.transId = false;
34377         var a = response.argument;
34378         this.processResponse(response, a.node, a.callback);
34379         this.fireEvent("load", this, a.node, response);
34380     },
34381
34382     handleFailure : function(response)
34383     {
34384         // should handle failure better..
34385         this.transId = false;
34386         var a = response.argument;
34387         this.fireEvent("loadexception", this, a.node, response);
34388         if(typeof a.callback == "function"){
34389             a.callback(this, a.node);
34390         }
34391     }
34392 });/*
34393  * Based on:
34394  * Ext JS Library 1.1.1
34395  * Copyright(c) 2006-2007, Ext JS, LLC.
34396  *
34397  * Originally Released Under LGPL - original licence link has changed is not relivant.
34398  *
34399  * Fork - LGPL
34400  * <script type="text/javascript">
34401  */
34402
34403 /**
34404 * @class Roo.tree.TreeFilter
34405 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34406 * @param {TreePanel} tree
34407 * @param {Object} config (optional)
34408  */
34409 Roo.tree.TreeFilter = function(tree, config){
34410     this.tree = tree;
34411     this.filtered = {};
34412     Roo.apply(this, config);
34413 };
34414
34415 Roo.tree.TreeFilter.prototype = {
34416     clearBlank:false,
34417     reverse:false,
34418     autoClear:false,
34419     remove:false,
34420
34421      /**
34422      * Filter the data by a specific attribute.
34423      * @param {String/RegExp} value Either string that the attribute value
34424      * should start with or a RegExp to test against the attribute
34425      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34426      * @param {TreeNode} startNode (optional) The node to start the filter at.
34427      */
34428     filter : function(value, attr, startNode){
34429         attr = attr || "text";
34430         var f;
34431         if(typeof value == "string"){
34432             var vlen = value.length;
34433             // auto clear empty filter
34434             if(vlen == 0 && this.clearBlank){
34435                 this.clear();
34436                 return;
34437             }
34438             value = value.toLowerCase();
34439             f = function(n){
34440                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34441             };
34442         }else if(value.exec){ // regex?
34443             f = function(n){
34444                 return value.test(n.attributes[attr]);
34445             };
34446         }else{
34447             throw 'Illegal filter type, must be string or regex';
34448         }
34449         this.filterBy(f, null, startNode);
34450         },
34451
34452     /**
34453      * Filter by a function. The passed function will be called with each
34454      * node in the tree (or from the startNode). If the function returns true, the node is kept
34455      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34456      * @param {Function} fn The filter function
34457      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34458      */
34459     filterBy : function(fn, scope, startNode){
34460         startNode = startNode || this.tree.root;
34461         if(this.autoClear){
34462             this.clear();
34463         }
34464         var af = this.filtered, rv = this.reverse;
34465         var f = function(n){
34466             if(n == startNode){
34467                 return true;
34468             }
34469             if(af[n.id]){
34470                 return false;
34471             }
34472             var m = fn.call(scope || n, n);
34473             if(!m || rv){
34474                 af[n.id] = n;
34475                 n.ui.hide();
34476                 return false;
34477             }
34478             return true;
34479         };
34480         startNode.cascade(f);
34481         if(this.remove){
34482            for(var id in af){
34483                if(typeof id != "function"){
34484                    var n = af[id];
34485                    if(n && n.parentNode){
34486                        n.parentNode.removeChild(n);
34487                    }
34488                }
34489            }
34490         }
34491     },
34492
34493     /**
34494      * Clears the current filter. Note: with the "remove" option
34495      * set a filter cannot be cleared.
34496      */
34497     clear : function(){
34498         var t = this.tree;
34499         var af = this.filtered;
34500         for(var id in af){
34501             if(typeof id != "function"){
34502                 var n = af[id];
34503                 if(n){
34504                     n.ui.show();
34505                 }
34506             }
34507         }
34508         this.filtered = {};
34509     }
34510 };
34511 /*
34512  * Based on:
34513  * Ext JS Library 1.1.1
34514  * Copyright(c) 2006-2007, Ext JS, LLC.
34515  *
34516  * Originally Released Under LGPL - original licence link has changed is not relivant.
34517  *
34518  * Fork - LGPL
34519  * <script type="text/javascript">
34520  */
34521  
34522
34523 /**
34524  * @class Roo.tree.TreeSorter
34525  * Provides sorting of nodes in a TreePanel
34526  * 
34527  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34528  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34529  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34530  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34531  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34532  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34533  * @constructor
34534  * @param {TreePanel} tree
34535  * @param {Object} config
34536  */
34537 Roo.tree.TreeSorter = function(tree, config){
34538     Roo.apply(this, config);
34539     tree.on("beforechildrenrendered", this.doSort, this);
34540     tree.on("append", this.updateSort, this);
34541     tree.on("insert", this.updateSort, this);
34542     
34543     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34544     var p = this.property || "text";
34545     var sortType = this.sortType;
34546     var fs = this.folderSort;
34547     var cs = this.caseSensitive === true;
34548     var leafAttr = this.leafAttr || 'leaf';
34549
34550     this.sortFn = function(n1, n2){
34551         if(fs){
34552             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34553                 return 1;
34554             }
34555             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34556                 return -1;
34557             }
34558         }
34559         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34560         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34561         if(v1 < v2){
34562                         return dsc ? +1 : -1;
34563                 }else if(v1 > v2){
34564                         return dsc ? -1 : +1;
34565         }else{
34566                 return 0;
34567         }
34568     };
34569 };
34570
34571 Roo.tree.TreeSorter.prototype = {
34572     doSort : function(node){
34573         node.sort(this.sortFn);
34574     },
34575     
34576     compareNodes : function(n1, n2){
34577         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34578     },
34579     
34580     updateSort : function(tree, node){
34581         if(node.childrenRendered){
34582             this.doSort.defer(1, this, [node]);
34583         }
34584     }
34585 };/*
34586  * Based on:
34587  * Ext JS Library 1.1.1
34588  * Copyright(c) 2006-2007, Ext JS, LLC.
34589  *
34590  * Originally Released Under LGPL - original licence link has changed is not relivant.
34591  *
34592  * Fork - LGPL
34593  * <script type="text/javascript">
34594  */
34595
34596 if(Roo.dd.DropZone){
34597     
34598 Roo.tree.TreeDropZone = function(tree, config){
34599     this.allowParentInsert = false;
34600     this.allowContainerDrop = false;
34601     this.appendOnly = false;
34602     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34603     this.tree = tree;
34604     this.lastInsertClass = "x-tree-no-status";
34605     this.dragOverData = {};
34606 };
34607
34608 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34609     ddGroup : "TreeDD",
34610     scroll:  true,
34611     
34612     expandDelay : 1000,
34613     
34614     expandNode : function(node){
34615         if(node.hasChildNodes() && !node.isExpanded()){
34616             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34617         }
34618     },
34619     
34620     queueExpand : function(node){
34621         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34622     },
34623     
34624     cancelExpand : function(){
34625         if(this.expandProcId){
34626             clearTimeout(this.expandProcId);
34627             this.expandProcId = false;
34628         }
34629     },
34630     
34631     isValidDropPoint : function(n, pt, dd, e, data){
34632         if(!n || !data){ return false; }
34633         var targetNode = n.node;
34634         var dropNode = data.node;
34635         // default drop rules
34636         if(!(targetNode && targetNode.isTarget && pt)){
34637             return false;
34638         }
34639         if(pt == "append" && targetNode.allowChildren === false){
34640             return false;
34641         }
34642         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34643             return false;
34644         }
34645         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34646             return false;
34647         }
34648         // reuse the object
34649         var overEvent = this.dragOverData;
34650         overEvent.tree = this.tree;
34651         overEvent.target = targetNode;
34652         overEvent.data = data;
34653         overEvent.point = pt;
34654         overEvent.source = dd;
34655         overEvent.rawEvent = e;
34656         overEvent.dropNode = dropNode;
34657         overEvent.cancel = false;  
34658         var result = this.tree.fireEvent("nodedragover", overEvent);
34659         return overEvent.cancel === false && result !== false;
34660     },
34661     
34662     getDropPoint : function(e, n, dd)
34663     {
34664         var tn = n.node;
34665         if(tn.isRoot){
34666             return tn.allowChildren !== false ? "append" : false; // always append for root
34667         }
34668         var dragEl = n.ddel;
34669         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34670         var y = Roo.lib.Event.getPageY(e);
34671         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34672         
34673         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34674         var noAppend = tn.allowChildren === false;
34675         if(this.appendOnly || tn.parentNode.allowChildren === false){
34676             return noAppend ? false : "append";
34677         }
34678         var noBelow = false;
34679         if(!this.allowParentInsert){
34680             noBelow = tn.hasChildNodes() && tn.isExpanded();
34681         }
34682         var q = (b - t) / (noAppend ? 2 : 3);
34683         if(y >= t && y < (t + q)){
34684             return "above";
34685         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34686             return "below";
34687         }else{
34688             return "append";
34689         }
34690     },
34691     
34692     onNodeEnter : function(n, dd, e, data)
34693     {
34694         this.cancelExpand();
34695     },
34696     
34697     onNodeOver : function(n, dd, e, data)
34698     {
34699        
34700         var pt = this.getDropPoint(e, n, dd);
34701         var node = n.node;
34702         
34703         // auto node expand check
34704         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34705             this.queueExpand(node);
34706         }else if(pt != "append"){
34707             this.cancelExpand();
34708         }
34709         
34710         // set the insert point style on the target node
34711         var returnCls = this.dropNotAllowed;
34712         if(this.isValidDropPoint(n, pt, dd, e, data)){
34713            if(pt){
34714                var el = n.ddel;
34715                var cls;
34716                if(pt == "above"){
34717                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34718                    cls = "x-tree-drag-insert-above";
34719                }else if(pt == "below"){
34720                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34721                    cls = "x-tree-drag-insert-below";
34722                }else{
34723                    returnCls = "x-tree-drop-ok-append";
34724                    cls = "x-tree-drag-append";
34725                }
34726                if(this.lastInsertClass != cls){
34727                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34728                    this.lastInsertClass = cls;
34729                }
34730            }
34731        }
34732        return returnCls;
34733     },
34734     
34735     onNodeOut : function(n, dd, e, data){
34736         
34737         this.cancelExpand();
34738         this.removeDropIndicators(n);
34739     },
34740     
34741     onNodeDrop : function(n, dd, e, data){
34742         var point = this.getDropPoint(e, n, dd);
34743         var targetNode = n.node;
34744         targetNode.ui.startDrop();
34745         if(!this.isValidDropPoint(n, point, dd, e, data)){
34746             targetNode.ui.endDrop();
34747             return false;
34748         }
34749         // first try to find the drop node
34750         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34751         var dropEvent = {
34752             tree : this.tree,
34753             target: targetNode,
34754             data: data,
34755             point: point,
34756             source: dd,
34757             rawEvent: e,
34758             dropNode: dropNode,
34759             cancel: !dropNode   
34760         };
34761         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34762         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34763             targetNode.ui.endDrop();
34764             return false;
34765         }
34766         // allow target changing
34767         targetNode = dropEvent.target;
34768         if(point == "append" && !targetNode.isExpanded()){
34769             targetNode.expand(false, null, function(){
34770                 this.completeDrop(dropEvent);
34771             }.createDelegate(this));
34772         }else{
34773             this.completeDrop(dropEvent);
34774         }
34775         return true;
34776     },
34777     
34778     completeDrop : function(de){
34779         var ns = de.dropNode, p = de.point, t = de.target;
34780         if(!(ns instanceof Array)){
34781             ns = [ns];
34782         }
34783         var n;
34784         for(var i = 0, len = ns.length; i < len; i++){
34785             n = ns[i];
34786             if(p == "above"){
34787                 t.parentNode.insertBefore(n, t);
34788             }else if(p == "below"){
34789                 t.parentNode.insertBefore(n, t.nextSibling);
34790             }else{
34791                 t.appendChild(n);
34792             }
34793         }
34794         n.ui.focus();
34795         if(this.tree.hlDrop){
34796             n.ui.highlight();
34797         }
34798         t.ui.endDrop();
34799         this.tree.fireEvent("nodedrop", de);
34800     },
34801     
34802     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34803         if(this.tree.hlDrop){
34804             dropNode.ui.focus();
34805             dropNode.ui.highlight();
34806         }
34807         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34808     },
34809     
34810     getTree : function(){
34811         return this.tree;
34812     },
34813     
34814     removeDropIndicators : function(n){
34815         if(n && n.ddel){
34816             var el = n.ddel;
34817             Roo.fly(el).removeClass([
34818                     "x-tree-drag-insert-above",
34819                     "x-tree-drag-insert-below",
34820                     "x-tree-drag-append"]);
34821             this.lastInsertClass = "_noclass";
34822         }
34823     },
34824     
34825     beforeDragDrop : function(target, e, id){
34826         this.cancelExpand();
34827         return true;
34828     },
34829     
34830     afterRepair : function(data){
34831         if(data && Roo.enableFx){
34832             data.node.ui.highlight();
34833         }
34834         this.hideProxy();
34835     } 
34836     
34837 });
34838
34839 }
34840 /*
34841  * Based on:
34842  * Ext JS Library 1.1.1
34843  * Copyright(c) 2006-2007, Ext JS, LLC.
34844  *
34845  * Originally Released Under LGPL - original licence link has changed is not relivant.
34846  *
34847  * Fork - LGPL
34848  * <script type="text/javascript">
34849  */
34850  
34851
34852 if(Roo.dd.DragZone){
34853 Roo.tree.TreeDragZone = function(tree, config){
34854     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34855     this.tree = tree;
34856 };
34857
34858 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34859     ddGroup : "TreeDD",
34860    
34861     onBeforeDrag : function(data, e){
34862         var n = data.node;
34863         return n && n.draggable && !n.disabled;
34864     },
34865      
34866     
34867     onInitDrag : function(e){
34868         var data = this.dragData;
34869         this.tree.getSelectionModel().select(data.node);
34870         this.proxy.update("");
34871         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34872         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34873     },
34874     
34875     getRepairXY : function(e, data){
34876         return data.node.ui.getDDRepairXY();
34877     },
34878     
34879     onEndDrag : function(data, e){
34880         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34881         
34882         
34883     },
34884     
34885     onValidDrop : function(dd, e, id){
34886         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34887         this.hideProxy();
34888     },
34889     
34890     beforeInvalidDrop : function(e, id){
34891         // this scrolls the original position back into view
34892         var sm = this.tree.getSelectionModel();
34893         sm.clearSelections();
34894         sm.select(this.dragData.node);
34895     }
34896 });
34897 }/*
34898  * Based on:
34899  * Ext JS Library 1.1.1
34900  * Copyright(c) 2006-2007, Ext JS, LLC.
34901  *
34902  * Originally Released Under LGPL - original licence link has changed is not relivant.
34903  *
34904  * Fork - LGPL
34905  * <script type="text/javascript">
34906  */
34907 /**
34908  * @class Roo.tree.TreeEditor
34909  * @extends Roo.Editor
34910  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34911  * as the editor field.
34912  * @constructor
34913  * @param {Object} config (used to be the tree panel.)
34914  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34915  * 
34916  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34917  * @cfg {Roo.form.TextField|Object} field The field configuration
34918  *
34919  * 
34920  */
34921 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34922     var tree = config;
34923     var field;
34924     if (oldconfig) { // old style..
34925         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34926     } else {
34927         // new style..
34928         tree = config.tree;
34929         config.field = config.field  || {};
34930         config.field.xtype = 'TextField';
34931         field = Roo.factory(config.field, Roo.form);
34932     }
34933     config = config || {};
34934     
34935     
34936     this.addEvents({
34937         /**
34938          * @event beforenodeedit
34939          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34940          * false from the handler of this event.
34941          * @param {Editor} this
34942          * @param {Roo.tree.Node} node 
34943          */
34944         "beforenodeedit" : true
34945     });
34946     
34947     //Roo.log(config);
34948     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34949
34950     this.tree = tree;
34951
34952     tree.on('beforeclick', this.beforeNodeClick, this);
34953     tree.getTreeEl().on('mousedown', this.hide, this);
34954     this.on('complete', this.updateNode, this);
34955     this.on('beforestartedit', this.fitToTree, this);
34956     this.on('startedit', this.bindScroll, this, {delay:10});
34957     this.on('specialkey', this.onSpecialKey, this);
34958 };
34959
34960 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34961     /**
34962      * @cfg {String} alignment
34963      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34964      */
34965     alignment: "l-l",
34966     // inherit
34967     autoSize: false,
34968     /**
34969      * @cfg {Boolean} hideEl
34970      * True to hide the bound element while the editor is displayed (defaults to false)
34971      */
34972     hideEl : false,
34973     /**
34974      * @cfg {String} cls
34975      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34976      */
34977     cls: "x-small-editor x-tree-editor",
34978     /**
34979      * @cfg {Boolean} shim
34980      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34981      */
34982     shim:false,
34983     // inherit
34984     shadow:"frame",
34985     /**
34986      * @cfg {Number} maxWidth
34987      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34988      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34989      * scroll and client offsets into account prior to each edit.
34990      */
34991     maxWidth: 250,
34992
34993     editDelay : 350,
34994
34995     // private
34996     fitToTree : function(ed, el){
34997         var td = this.tree.getTreeEl().dom, nd = el.dom;
34998         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34999             td.scrollLeft = nd.offsetLeft;
35000         }
35001         var w = Math.min(
35002                 this.maxWidth,
35003                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35004         this.setSize(w, '');
35005         
35006         return this.fireEvent('beforenodeedit', this, this.editNode);
35007         
35008     },
35009
35010     // private
35011     triggerEdit : function(node){
35012         this.completeEdit();
35013         this.editNode = node;
35014         this.startEdit(node.ui.textNode, node.text);
35015     },
35016
35017     // private
35018     bindScroll : function(){
35019         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35020     },
35021
35022     // private
35023     beforeNodeClick : function(node, e){
35024         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35025         this.lastClick = new Date();
35026         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35027             e.stopEvent();
35028             this.triggerEdit(node);
35029             return false;
35030         }
35031         return true;
35032     },
35033
35034     // private
35035     updateNode : function(ed, value){
35036         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35037         this.editNode.setText(value);
35038     },
35039
35040     // private
35041     onHide : function(){
35042         Roo.tree.TreeEditor.superclass.onHide.call(this);
35043         if(this.editNode){
35044             this.editNode.ui.focus();
35045         }
35046     },
35047
35048     // private
35049     onSpecialKey : function(field, e){
35050         var k = e.getKey();
35051         if(k == e.ESC){
35052             e.stopEvent();
35053             this.cancelEdit();
35054         }else if(k == e.ENTER && !e.hasModifier()){
35055             e.stopEvent();
35056             this.completeEdit();
35057         }
35058     }
35059 });//<Script type="text/javascript">
35060 /*
35061  * Based on:
35062  * Ext JS Library 1.1.1
35063  * Copyright(c) 2006-2007, Ext JS, LLC.
35064  *
35065  * Originally Released Under LGPL - original licence link has changed is not relivant.
35066  *
35067  * Fork - LGPL
35068  * <script type="text/javascript">
35069  */
35070  
35071 /**
35072  * Not documented??? - probably should be...
35073  */
35074
35075 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35076     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35077     
35078     renderElements : function(n, a, targetNode, bulkRender){
35079         //consel.log("renderElements?");
35080         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35081
35082         var t = n.getOwnerTree();
35083         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35084         
35085         var cols = t.columns;
35086         var bw = t.borderWidth;
35087         var c = cols[0];
35088         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35089          var cb = typeof a.checked == "boolean";
35090         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35091         var colcls = 'x-t-' + tid + '-c0';
35092         var buf = [
35093             '<li class="x-tree-node">',
35094             
35095                 
35096                 '<div class="x-tree-node-el ', a.cls,'">',
35097                     // extran...
35098                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35099                 
35100                 
35101                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35102                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35103                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35104                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35105                            (a.iconCls ? ' '+a.iconCls : ''),
35106                            '" unselectable="on" />',
35107                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35108                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35109                              
35110                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35111                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35112                             '<span unselectable="on" qtip="' + tx + '">',
35113                              tx,
35114                              '</span></a>' ,
35115                     '</div>',
35116                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35117                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35118                  ];
35119         for(var i = 1, len = cols.length; i < len; i++){
35120             c = cols[i];
35121             colcls = 'x-t-' + tid + '-c' +i;
35122             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35123             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35124                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35125                       "</div>");
35126          }
35127          
35128          buf.push(
35129             '</a>',
35130             '<div class="x-clear"></div></div>',
35131             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35132             "</li>");
35133         
35134         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35135             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35136                                 n.nextSibling.ui.getEl(), buf.join(""));
35137         }else{
35138             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35139         }
35140         var el = this.wrap.firstChild;
35141         this.elRow = el;
35142         this.elNode = el.firstChild;
35143         this.ranchor = el.childNodes[1];
35144         this.ctNode = this.wrap.childNodes[1];
35145         var cs = el.firstChild.childNodes;
35146         this.indentNode = cs[0];
35147         this.ecNode = cs[1];
35148         this.iconNode = cs[2];
35149         var index = 3;
35150         if(cb){
35151             this.checkbox = cs[3];
35152             index++;
35153         }
35154         this.anchor = cs[index];
35155         
35156         this.textNode = cs[index].firstChild;
35157         
35158         //el.on("click", this.onClick, this);
35159         //el.on("dblclick", this.onDblClick, this);
35160         
35161         
35162        // console.log(this);
35163     },
35164     initEvents : function(){
35165         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35166         
35167             
35168         var a = this.ranchor;
35169
35170         var el = Roo.get(a);
35171
35172         if(Roo.isOpera){ // opera render bug ignores the CSS
35173             el.setStyle("text-decoration", "none");
35174         }
35175
35176         el.on("click", this.onClick, this);
35177         el.on("dblclick", this.onDblClick, this);
35178         el.on("contextmenu", this.onContextMenu, this);
35179         
35180     },
35181     
35182     /*onSelectedChange : function(state){
35183         if(state){
35184             this.focus();
35185             this.addClass("x-tree-selected");
35186         }else{
35187             //this.blur();
35188             this.removeClass("x-tree-selected");
35189         }
35190     },*/
35191     addClass : function(cls){
35192         if(this.elRow){
35193             Roo.fly(this.elRow).addClass(cls);
35194         }
35195         
35196     },
35197     
35198     
35199     removeClass : function(cls){
35200         if(this.elRow){
35201             Roo.fly(this.elRow).removeClass(cls);
35202         }
35203     }
35204
35205     
35206     
35207 });//<Script type="text/javascript">
35208
35209 /*
35210  * Based on:
35211  * Ext JS Library 1.1.1
35212  * Copyright(c) 2006-2007, Ext JS, LLC.
35213  *
35214  * Originally Released Under LGPL - original licence link has changed is not relivant.
35215  *
35216  * Fork - LGPL
35217  * <script type="text/javascript">
35218  */
35219  
35220
35221 /**
35222  * @class Roo.tree.ColumnTree
35223  * @extends Roo.data.TreePanel
35224  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35225  * @cfg {int} borderWidth  compined right/left border allowance
35226  * @constructor
35227  * @param {String/HTMLElement/Element} el The container element
35228  * @param {Object} config
35229  */
35230 Roo.tree.ColumnTree =  function(el, config)
35231 {
35232    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35233    this.addEvents({
35234         /**
35235         * @event resize
35236         * Fire this event on a container when it resizes
35237         * @param {int} w Width
35238         * @param {int} h Height
35239         */
35240        "resize" : true
35241     });
35242     this.on('resize', this.onResize, this);
35243 };
35244
35245 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35246     //lines:false,
35247     
35248     
35249     borderWidth: Roo.isBorderBox ? 0 : 2, 
35250     headEls : false,
35251     
35252     render : function(){
35253         // add the header.....
35254        
35255         Roo.tree.ColumnTree.superclass.render.apply(this);
35256         
35257         this.el.addClass('x-column-tree');
35258         
35259         this.headers = this.el.createChild(
35260             {cls:'x-tree-headers'},this.innerCt.dom);
35261    
35262         var cols = this.columns, c;
35263         var totalWidth = 0;
35264         this.headEls = [];
35265         var  len = cols.length;
35266         for(var i = 0; i < len; i++){
35267              c = cols[i];
35268              totalWidth += c.width;
35269             this.headEls.push(this.headers.createChild({
35270                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35271                  cn: {
35272                      cls:'x-tree-hd-text',
35273                      html: c.header
35274                  },
35275                  style:'width:'+(c.width-this.borderWidth)+'px;'
35276              }));
35277         }
35278         this.headers.createChild({cls:'x-clear'});
35279         // prevent floats from wrapping when clipped
35280         this.headers.setWidth(totalWidth);
35281         //this.innerCt.setWidth(totalWidth);
35282         this.innerCt.setStyle({ overflow: 'auto' });
35283         this.onResize(this.width, this.height);
35284              
35285         
35286     },
35287     onResize : function(w,h)
35288     {
35289         this.height = h;
35290         this.width = w;
35291         // resize cols..
35292         this.innerCt.setWidth(this.width);
35293         this.innerCt.setHeight(this.height-20);
35294         
35295         // headers...
35296         var cols = this.columns, c;
35297         var totalWidth = 0;
35298         var expEl = false;
35299         var len = cols.length;
35300         for(var i = 0; i < len; i++){
35301             c = cols[i];
35302             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35303                 // it's the expander..
35304                 expEl  = this.headEls[i];
35305                 continue;
35306             }
35307             totalWidth += c.width;
35308             
35309         }
35310         if (expEl) {
35311             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35312         }
35313         this.headers.setWidth(w-20);
35314
35315         
35316         
35317         
35318     }
35319 });
35320 /*
35321  * Based on:
35322  * Ext JS Library 1.1.1
35323  * Copyright(c) 2006-2007, Ext JS, LLC.
35324  *
35325  * Originally Released Under LGPL - original licence link has changed is not relivant.
35326  *
35327  * Fork - LGPL
35328  * <script type="text/javascript">
35329  */
35330  
35331 /**
35332  * @class Roo.menu.Menu
35333  * @extends Roo.util.Observable
35334  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35335  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35336  * @constructor
35337  * Creates a new Menu
35338  * @param {Object} config Configuration options
35339  */
35340 Roo.menu.Menu = function(config){
35341     Roo.apply(this, config);
35342     this.id = this.id || Roo.id();
35343     this.addEvents({
35344         /**
35345          * @event beforeshow
35346          * Fires before this menu is displayed
35347          * @param {Roo.menu.Menu} this
35348          */
35349         beforeshow : true,
35350         /**
35351          * @event beforehide
35352          * Fires before this menu is hidden
35353          * @param {Roo.menu.Menu} this
35354          */
35355         beforehide : true,
35356         /**
35357          * @event show
35358          * Fires after this menu is displayed
35359          * @param {Roo.menu.Menu} this
35360          */
35361         show : true,
35362         /**
35363          * @event hide
35364          * Fires after this menu is hidden
35365          * @param {Roo.menu.Menu} this
35366          */
35367         hide : true,
35368         /**
35369          * @event click
35370          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35371          * @param {Roo.menu.Menu} this
35372          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35373          * @param {Roo.EventObject} e
35374          */
35375         click : true,
35376         /**
35377          * @event mouseover
35378          * Fires when the mouse is hovering over this menu
35379          * @param {Roo.menu.Menu} this
35380          * @param {Roo.EventObject} e
35381          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35382          */
35383         mouseover : true,
35384         /**
35385          * @event mouseout
35386          * Fires when the mouse exits this menu
35387          * @param {Roo.menu.Menu} this
35388          * @param {Roo.EventObject} e
35389          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35390          */
35391         mouseout : true,
35392         /**
35393          * @event itemclick
35394          * Fires when a menu item contained in this menu is clicked
35395          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35396          * @param {Roo.EventObject} e
35397          */
35398         itemclick: true
35399     });
35400     if (this.registerMenu) {
35401         Roo.menu.MenuMgr.register(this);
35402     }
35403     
35404     var mis = this.items;
35405     this.items = new Roo.util.MixedCollection();
35406     if(mis){
35407         this.add.apply(this, mis);
35408     }
35409 };
35410
35411 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35412     /**
35413      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35414      */
35415     minWidth : 120,
35416     /**
35417      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35418      * for bottom-right shadow (defaults to "sides")
35419      */
35420     shadow : "sides",
35421     /**
35422      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35423      * this menu (defaults to "tl-tr?")
35424      */
35425     subMenuAlign : "tl-tr?",
35426     /**
35427      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35428      * relative to its element of origin (defaults to "tl-bl?")
35429      */
35430     defaultAlign : "tl-bl?",
35431     /**
35432      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35433      */
35434     allowOtherMenus : false,
35435     /**
35436      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35437      */
35438     registerMenu : true,
35439
35440     hidden:true,
35441
35442     // private
35443     render : function(){
35444         if(this.el){
35445             return;
35446         }
35447         var el = this.el = new Roo.Layer({
35448             cls: "x-menu",
35449             shadow:this.shadow,
35450             constrain: false,
35451             parentEl: this.parentEl || document.body,
35452             zindex:15000
35453         });
35454
35455         this.keyNav = new Roo.menu.MenuNav(this);
35456
35457         if(this.plain){
35458             el.addClass("x-menu-plain");
35459         }
35460         if(this.cls){
35461             el.addClass(this.cls);
35462         }
35463         // generic focus element
35464         this.focusEl = el.createChild({
35465             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35466         });
35467         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35468         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35469         
35470         ul.on("mouseover", this.onMouseOver, this);
35471         ul.on("mouseout", this.onMouseOut, this);
35472         this.items.each(function(item){
35473             if (item.hidden) {
35474                 return;
35475             }
35476             
35477             var li = document.createElement("li");
35478             li.className = "x-menu-list-item";
35479             ul.dom.appendChild(li);
35480             item.render(li, this);
35481         }, this);
35482         this.ul = ul;
35483         this.autoWidth();
35484     },
35485
35486     // private
35487     autoWidth : function(){
35488         var el = this.el, ul = this.ul;
35489         if(!el){
35490             return;
35491         }
35492         var w = this.width;
35493         if(w){
35494             el.setWidth(w);
35495         }else if(Roo.isIE){
35496             el.setWidth(this.minWidth);
35497             var t = el.dom.offsetWidth; // force recalc
35498             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35499         }
35500     },
35501
35502     // private
35503     delayAutoWidth : function(){
35504         if(this.rendered){
35505             if(!this.awTask){
35506                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35507             }
35508             this.awTask.delay(20);
35509         }
35510     },
35511
35512     // private
35513     findTargetItem : function(e){
35514         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35515         if(t && t.menuItemId){
35516             return this.items.get(t.menuItemId);
35517         }
35518     },
35519
35520     // private
35521     onClick : function(e){
35522         Roo.log("menu.onClick");
35523         var t = this.findTargetItem(e);
35524         if(!t){
35525             return;
35526         }
35527         Roo.log(e);
35528         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35529             if(t == this.activeItem && t.shouldDeactivate(e)){
35530                 this.activeItem.deactivate();
35531                 delete this.activeItem;
35532                 return;
35533             }
35534             if(t.canActivate){
35535                 this.setActiveItem(t, true);
35536             }
35537             return;
35538             
35539             
35540         }
35541         
35542         t.onClick(e);
35543         this.fireEvent("click", this, t, e);
35544     },
35545
35546     // private
35547     setActiveItem : function(item, autoExpand){
35548         if(item != this.activeItem){
35549             if(this.activeItem){
35550                 this.activeItem.deactivate();
35551             }
35552             this.activeItem = item;
35553             item.activate(autoExpand);
35554         }else if(autoExpand){
35555             item.expandMenu();
35556         }
35557     },
35558
35559     // private
35560     tryActivate : function(start, step){
35561         var items = this.items;
35562         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35563             var item = items.get(i);
35564             if(!item.disabled && item.canActivate){
35565                 this.setActiveItem(item, false);
35566                 return item;
35567             }
35568         }
35569         return false;
35570     },
35571
35572     // private
35573     onMouseOver : function(e){
35574         var t;
35575         if(t = this.findTargetItem(e)){
35576             if(t.canActivate && !t.disabled){
35577                 this.setActiveItem(t, true);
35578             }
35579         }
35580         this.fireEvent("mouseover", this, e, t);
35581     },
35582
35583     // private
35584     onMouseOut : function(e){
35585         var t;
35586         if(t = this.findTargetItem(e)){
35587             if(t == this.activeItem && t.shouldDeactivate(e)){
35588                 this.activeItem.deactivate();
35589                 delete this.activeItem;
35590             }
35591         }
35592         this.fireEvent("mouseout", this, e, t);
35593     },
35594
35595     /**
35596      * Read-only.  Returns true if the menu is currently displayed, else false.
35597      * @type Boolean
35598      */
35599     isVisible : function(){
35600         return this.el && !this.hidden;
35601     },
35602
35603     /**
35604      * Displays this menu relative to another element
35605      * @param {String/HTMLElement/Roo.Element} element The element to align to
35606      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35607      * the element (defaults to this.defaultAlign)
35608      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35609      */
35610     show : function(el, pos, parentMenu){
35611         this.parentMenu = parentMenu;
35612         if(!this.el){
35613             this.render();
35614         }
35615         this.fireEvent("beforeshow", this);
35616         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35617     },
35618
35619     /**
35620      * Displays this menu at a specific xy position
35621      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35622      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35623      */
35624     showAt : function(xy, parentMenu, /* private: */_e){
35625         this.parentMenu = parentMenu;
35626         if(!this.el){
35627             this.render();
35628         }
35629         if(_e !== false){
35630             this.fireEvent("beforeshow", this);
35631             xy = this.el.adjustForConstraints(xy);
35632         }
35633         this.el.setXY(xy);
35634         this.el.show();
35635         this.hidden = false;
35636         this.focus();
35637         this.fireEvent("show", this);
35638     },
35639
35640     focus : function(){
35641         if(!this.hidden){
35642             this.doFocus.defer(50, this);
35643         }
35644     },
35645
35646     doFocus : function(){
35647         if(!this.hidden){
35648             this.focusEl.focus();
35649         }
35650     },
35651
35652     /**
35653      * Hides this menu and optionally all parent menus
35654      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35655      */
35656     hide : function(deep){
35657         if(this.el && this.isVisible()){
35658             this.fireEvent("beforehide", this);
35659             if(this.activeItem){
35660                 this.activeItem.deactivate();
35661                 this.activeItem = null;
35662             }
35663             this.el.hide();
35664             this.hidden = true;
35665             this.fireEvent("hide", this);
35666         }
35667         if(deep === true && this.parentMenu){
35668             this.parentMenu.hide(true);
35669         }
35670     },
35671
35672     /**
35673      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35674      * Any of the following are valid:
35675      * <ul>
35676      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35677      * <li>An HTMLElement object which will be converted to a menu item</li>
35678      * <li>A menu item config object that will be created as a new menu item</li>
35679      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35680      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35681      * </ul>
35682      * Usage:
35683      * <pre><code>
35684 // Create the menu
35685 var menu = new Roo.menu.Menu();
35686
35687 // Create a menu item to add by reference
35688 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35689
35690 // Add a bunch of items at once using different methods.
35691 // Only the last item added will be returned.
35692 var item = menu.add(
35693     menuItem,                // add existing item by ref
35694     'Dynamic Item',          // new TextItem
35695     '-',                     // new separator
35696     { text: 'Config Item' }  // new item by config
35697 );
35698 </code></pre>
35699      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35700      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35701      */
35702     add : function(){
35703         var a = arguments, l = a.length, item;
35704         for(var i = 0; i < l; i++){
35705             var el = a[i];
35706             if ((typeof(el) == "object") && el.xtype && el.xns) {
35707                 el = Roo.factory(el, Roo.menu);
35708             }
35709             
35710             if(el.render){ // some kind of Item
35711                 item = this.addItem(el);
35712             }else if(typeof el == "string"){ // string
35713                 if(el == "separator" || el == "-"){
35714                     item = this.addSeparator();
35715                 }else{
35716                     item = this.addText(el);
35717                 }
35718             }else if(el.tagName || el.el){ // element
35719                 item = this.addElement(el);
35720             }else if(typeof el == "object"){ // must be menu item config?
35721                 item = this.addMenuItem(el);
35722             }
35723         }
35724         return item;
35725     },
35726
35727     /**
35728      * Returns this menu's underlying {@link Roo.Element} object
35729      * @return {Roo.Element} The element
35730      */
35731     getEl : function(){
35732         if(!this.el){
35733             this.render();
35734         }
35735         return this.el;
35736     },
35737
35738     /**
35739      * Adds a separator bar to the menu
35740      * @return {Roo.menu.Item} The menu item that was added
35741      */
35742     addSeparator : function(){
35743         return this.addItem(new Roo.menu.Separator());
35744     },
35745
35746     /**
35747      * Adds an {@link Roo.Element} object to the menu
35748      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35749      * @return {Roo.menu.Item} The menu item that was added
35750      */
35751     addElement : function(el){
35752         return this.addItem(new Roo.menu.BaseItem(el));
35753     },
35754
35755     /**
35756      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35757      * @param {Roo.menu.Item} item The menu item to add
35758      * @return {Roo.menu.Item} The menu item that was added
35759      */
35760     addItem : function(item){
35761         this.items.add(item);
35762         if(this.ul){
35763             var li = document.createElement("li");
35764             li.className = "x-menu-list-item";
35765             this.ul.dom.appendChild(li);
35766             item.render(li, this);
35767             this.delayAutoWidth();
35768         }
35769         return item;
35770     },
35771
35772     /**
35773      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35774      * @param {Object} config A MenuItem config object
35775      * @return {Roo.menu.Item} The menu item that was added
35776      */
35777     addMenuItem : function(config){
35778         if(!(config instanceof Roo.menu.Item)){
35779             if(typeof config.checked == "boolean"){ // must be check menu item config?
35780                 config = new Roo.menu.CheckItem(config);
35781             }else{
35782                 config = new Roo.menu.Item(config);
35783             }
35784         }
35785         return this.addItem(config);
35786     },
35787
35788     /**
35789      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35790      * @param {String} text The text to display in the menu item
35791      * @return {Roo.menu.Item} The menu item that was added
35792      */
35793     addText : function(text){
35794         return this.addItem(new Roo.menu.TextItem({ text : text }));
35795     },
35796
35797     /**
35798      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35799      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35800      * @param {Roo.menu.Item} item The menu item to add
35801      * @return {Roo.menu.Item} The menu item that was added
35802      */
35803     insert : function(index, item){
35804         this.items.insert(index, item);
35805         if(this.ul){
35806             var li = document.createElement("li");
35807             li.className = "x-menu-list-item";
35808             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35809             item.render(li, this);
35810             this.delayAutoWidth();
35811         }
35812         return item;
35813     },
35814
35815     /**
35816      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35817      * @param {Roo.menu.Item} item The menu item to remove
35818      */
35819     remove : function(item){
35820         this.items.removeKey(item.id);
35821         item.destroy();
35822     },
35823
35824     /**
35825      * Removes and destroys all items in the menu
35826      */
35827     removeAll : function(){
35828         var f;
35829         while(f = this.items.first()){
35830             this.remove(f);
35831         }
35832     }
35833 });
35834
35835 // MenuNav is a private utility class used internally by the Menu
35836 Roo.menu.MenuNav = function(menu){
35837     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35838     this.scope = this.menu = menu;
35839 };
35840
35841 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35842     doRelay : function(e, h){
35843         var k = e.getKey();
35844         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35845             this.menu.tryActivate(0, 1);
35846             return false;
35847         }
35848         return h.call(this.scope || this, e, this.menu);
35849     },
35850
35851     up : function(e, m){
35852         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35853             m.tryActivate(m.items.length-1, -1);
35854         }
35855     },
35856
35857     down : function(e, m){
35858         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35859             m.tryActivate(0, 1);
35860         }
35861     },
35862
35863     right : function(e, m){
35864         if(m.activeItem){
35865             m.activeItem.expandMenu(true);
35866         }
35867     },
35868
35869     left : function(e, m){
35870         m.hide();
35871         if(m.parentMenu && m.parentMenu.activeItem){
35872             m.parentMenu.activeItem.activate();
35873         }
35874     },
35875
35876     enter : function(e, m){
35877         if(m.activeItem){
35878             e.stopPropagation();
35879             m.activeItem.onClick(e);
35880             m.fireEvent("click", this, m.activeItem);
35881             return true;
35882         }
35883     }
35884 });/*
35885  * Based on:
35886  * Ext JS Library 1.1.1
35887  * Copyright(c) 2006-2007, Ext JS, LLC.
35888  *
35889  * Originally Released Under LGPL - original licence link has changed is not relivant.
35890  *
35891  * Fork - LGPL
35892  * <script type="text/javascript">
35893  */
35894  
35895 /**
35896  * @class Roo.menu.MenuMgr
35897  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35898  * @singleton
35899  */
35900 Roo.menu.MenuMgr = function(){
35901    var menus, active, groups = {}, attached = false, lastShow = new Date();
35902
35903    // private - called when first menu is created
35904    function init(){
35905        menus = {};
35906        active = new Roo.util.MixedCollection();
35907        Roo.get(document).addKeyListener(27, function(){
35908            if(active.length > 0){
35909                hideAll();
35910            }
35911        });
35912    }
35913
35914    // private
35915    function hideAll(){
35916        if(active && active.length > 0){
35917            var c = active.clone();
35918            c.each(function(m){
35919                m.hide();
35920            });
35921        }
35922    }
35923
35924    // private
35925    function onHide(m){
35926        active.remove(m);
35927        if(active.length < 1){
35928            Roo.get(document).un("mousedown", onMouseDown);
35929            attached = false;
35930        }
35931    }
35932
35933    // private
35934    function onShow(m){
35935        var last = active.last();
35936        lastShow = new Date();
35937        active.add(m);
35938        if(!attached){
35939            Roo.get(document).on("mousedown", onMouseDown);
35940            attached = true;
35941        }
35942        if(m.parentMenu){
35943           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35944           m.parentMenu.activeChild = m;
35945        }else if(last && last.isVisible()){
35946           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35947        }
35948    }
35949
35950    // private
35951    function onBeforeHide(m){
35952        if(m.activeChild){
35953            m.activeChild.hide();
35954        }
35955        if(m.autoHideTimer){
35956            clearTimeout(m.autoHideTimer);
35957            delete m.autoHideTimer;
35958        }
35959    }
35960
35961    // private
35962    function onBeforeShow(m){
35963        var pm = m.parentMenu;
35964        if(!pm && !m.allowOtherMenus){
35965            hideAll();
35966        }else if(pm && pm.activeChild && active != m){
35967            pm.activeChild.hide();
35968        }
35969    }
35970
35971    // private
35972    function onMouseDown(e){
35973        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35974            hideAll();
35975        }
35976    }
35977
35978    // private
35979    function onBeforeCheck(mi, state){
35980        if(state){
35981            var g = groups[mi.group];
35982            for(var i = 0, l = g.length; i < l; i++){
35983                if(g[i] != mi){
35984                    g[i].setChecked(false);
35985                }
35986            }
35987        }
35988    }
35989
35990    return {
35991
35992        /**
35993         * Hides all menus that are currently visible
35994         */
35995        hideAll : function(){
35996             hideAll();  
35997        },
35998
35999        // private
36000        register : function(menu){
36001            if(!menus){
36002                init();
36003            }
36004            menus[menu.id] = menu;
36005            menu.on("beforehide", onBeforeHide);
36006            menu.on("hide", onHide);
36007            menu.on("beforeshow", onBeforeShow);
36008            menu.on("show", onShow);
36009            var g = menu.group;
36010            if(g && menu.events["checkchange"]){
36011                if(!groups[g]){
36012                    groups[g] = [];
36013                }
36014                groups[g].push(menu);
36015                menu.on("checkchange", onCheck);
36016            }
36017        },
36018
36019         /**
36020          * Returns a {@link Roo.menu.Menu} object
36021          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36022          * be used to generate and return a new Menu instance.
36023          */
36024        get : function(menu){
36025            if(typeof menu == "string"){ // menu id
36026                return menus[menu];
36027            }else if(menu.events){  // menu instance
36028                return menu;
36029            }else if(typeof menu.length == 'number'){ // array of menu items?
36030                return new Roo.menu.Menu({items:menu});
36031            }else{ // otherwise, must be a config
36032                return new Roo.menu.Menu(menu);
36033            }
36034        },
36035
36036        // private
36037        unregister : function(menu){
36038            delete menus[menu.id];
36039            menu.un("beforehide", onBeforeHide);
36040            menu.un("hide", onHide);
36041            menu.un("beforeshow", onBeforeShow);
36042            menu.un("show", onShow);
36043            var g = menu.group;
36044            if(g && menu.events["checkchange"]){
36045                groups[g].remove(menu);
36046                menu.un("checkchange", onCheck);
36047            }
36048        },
36049
36050        // private
36051        registerCheckable : function(menuItem){
36052            var g = menuItem.group;
36053            if(g){
36054                if(!groups[g]){
36055                    groups[g] = [];
36056                }
36057                groups[g].push(menuItem);
36058                menuItem.on("beforecheckchange", onBeforeCheck);
36059            }
36060        },
36061
36062        // private
36063        unregisterCheckable : function(menuItem){
36064            var g = menuItem.group;
36065            if(g){
36066                groups[g].remove(menuItem);
36067                menuItem.un("beforecheckchange", onBeforeCheck);
36068            }
36069        }
36070    };
36071 }();/*
36072  * Based on:
36073  * Ext JS Library 1.1.1
36074  * Copyright(c) 2006-2007, Ext JS, LLC.
36075  *
36076  * Originally Released Under LGPL - original licence link has changed is not relivant.
36077  *
36078  * Fork - LGPL
36079  * <script type="text/javascript">
36080  */
36081  
36082
36083 /**
36084  * @class Roo.menu.BaseItem
36085  * @extends Roo.Component
36086  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36087  * management and base configuration options shared by all menu components.
36088  * @constructor
36089  * Creates a new BaseItem
36090  * @param {Object} config Configuration options
36091  */
36092 Roo.menu.BaseItem = function(config){
36093     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36094
36095     this.addEvents({
36096         /**
36097          * @event click
36098          * Fires when this item is clicked
36099          * @param {Roo.menu.BaseItem} this
36100          * @param {Roo.EventObject} e
36101          */
36102         click: true,
36103         /**
36104          * @event activate
36105          * Fires when this item is activated
36106          * @param {Roo.menu.BaseItem} this
36107          */
36108         activate : true,
36109         /**
36110          * @event deactivate
36111          * Fires when this item is deactivated
36112          * @param {Roo.menu.BaseItem} this
36113          */
36114         deactivate : true
36115     });
36116
36117     if(this.handler){
36118         this.on("click", this.handler, this.scope, true);
36119     }
36120 };
36121
36122 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36123     /**
36124      * @cfg {Function} handler
36125      * A function that will handle the click event of this menu item (defaults to undefined)
36126      */
36127     /**
36128      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36129      */
36130     canActivate : false,
36131     
36132      /**
36133      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36134      */
36135     hidden: false,
36136     
36137     /**
36138      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36139      */
36140     activeClass : "x-menu-item-active",
36141     /**
36142      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36143      */
36144     hideOnClick : true,
36145     /**
36146      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36147      */
36148     hideDelay : 100,
36149
36150     // private
36151     ctype: "Roo.menu.BaseItem",
36152
36153     // private
36154     actionMode : "container",
36155
36156     // private
36157     render : function(container, parentMenu){
36158         this.parentMenu = parentMenu;
36159         Roo.menu.BaseItem.superclass.render.call(this, container);
36160         this.container.menuItemId = this.id;
36161     },
36162
36163     // private
36164     onRender : function(container, position){
36165         this.el = Roo.get(this.el);
36166         container.dom.appendChild(this.el.dom);
36167     },
36168
36169     // private
36170     onClick : function(e){
36171         if(!this.disabled && this.fireEvent("click", this, e) !== false
36172                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36173             this.handleClick(e);
36174         }else{
36175             e.stopEvent();
36176         }
36177     },
36178
36179     // private
36180     activate : function(){
36181         if(this.disabled){
36182             return false;
36183         }
36184         var li = this.container;
36185         li.addClass(this.activeClass);
36186         this.region = li.getRegion().adjust(2, 2, -2, -2);
36187         this.fireEvent("activate", this);
36188         return true;
36189     },
36190
36191     // private
36192     deactivate : function(){
36193         this.container.removeClass(this.activeClass);
36194         this.fireEvent("deactivate", this);
36195     },
36196
36197     // private
36198     shouldDeactivate : function(e){
36199         return !this.region || !this.region.contains(e.getPoint());
36200     },
36201
36202     // private
36203     handleClick : function(e){
36204         if(this.hideOnClick){
36205             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36206         }
36207     },
36208
36209     // private
36210     expandMenu : function(autoActivate){
36211         // do nothing
36212     },
36213
36214     // private
36215     hideMenu : function(){
36216         // do nothing
36217     }
36218 });/*
36219  * Based on:
36220  * Ext JS Library 1.1.1
36221  * Copyright(c) 2006-2007, Ext JS, LLC.
36222  *
36223  * Originally Released Under LGPL - original licence link has changed is not relivant.
36224  *
36225  * Fork - LGPL
36226  * <script type="text/javascript">
36227  */
36228  
36229 /**
36230  * @class Roo.menu.Adapter
36231  * @extends Roo.menu.BaseItem
36232  * 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.
36233  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36234  * @constructor
36235  * Creates a new Adapter
36236  * @param {Object} config Configuration options
36237  */
36238 Roo.menu.Adapter = function(component, config){
36239     Roo.menu.Adapter.superclass.constructor.call(this, config);
36240     this.component = component;
36241 };
36242 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36243     // private
36244     canActivate : true,
36245
36246     // private
36247     onRender : function(container, position){
36248         this.component.render(container);
36249         this.el = this.component.getEl();
36250     },
36251
36252     // private
36253     activate : function(){
36254         if(this.disabled){
36255             return false;
36256         }
36257         this.component.focus();
36258         this.fireEvent("activate", this);
36259         return true;
36260     },
36261
36262     // private
36263     deactivate : function(){
36264         this.fireEvent("deactivate", this);
36265     },
36266
36267     // private
36268     disable : function(){
36269         this.component.disable();
36270         Roo.menu.Adapter.superclass.disable.call(this);
36271     },
36272
36273     // private
36274     enable : function(){
36275         this.component.enable();
36276         Roo.menu.Adapter.superclass.enable.call(this);
36277     }
36278 });/*
36279  * Based on:
36280  * Ext JS Library 1.1.1
36281  * Copyright(c) 2006-2007, Ext JS, LLC.
36282  *
36283  * Originally Released Under LGPL - original licence link has changed is not relivant.
36284  *
36285  * Fork - LGPL
36286  * <script type="text/javascript">
36287  */
36288
36289 /**
36290  * @class Roo.menu.TextItem
36291  * @extends Roo.menu.BaseItem
36292  * Adds a static text string to a menu, usually used as either a heading or group separator.
36293  * Note: old style constructor with text is still supported.
36294  * 
36295  * @constructor
36296  * Creates a new TextItem
36297  * @param {Object} cfg Configuration
36298  */
36299 Roo.menu.TextItem = function(cfg){
36300     if (typeof(cfg) == 'string') {
36301         this.text = cfg;
36302     } else {
36303         Roo.apply(this,cfg);
36304     }
36305     
36306     Roo.menu.TextItem.superclass.constructor.call(this);
36307 };
36308
36309 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36310     /**
36311      * @cfg {Boolean} text Text to show on item.
36312      */
36313     text : '',
36314     
36315     /**
36316      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36317      */
36318     hideOnClick : false,
36319     /**
36320      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36321      */
36322     itemCls : "x-menu-text",
36323
36324     // private
36325     onRender : function(){
36326         var s = document.createElement("span");
36327         s.className = this.itemCls;
36328         s.innerHTML = this.text;
36329         this.el = s;
36330         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36331     }
36332 });/*
36333  * Based on:
36334  * Ext JS Library 1.1.1
36335  * Copyright(c) 2006-2007, Ext JS, LLC.
36336  *
36337  * Originally Released Under LGPL - original licence link has changed is not relivant.
36338  *
36339  * Fork - LGPL
36340  * <script type="text/javascript">
36341  */
36342
36343 /**
36344  * @class Roo.menu.Separator
36345  * @extends Roo.menu.BaseItem
36346  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36347  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36348  * @constructor
36349  * @param {Object} config Configuration options
36350  */
36351 Roo.menu.Separator = function(config){
36352     Roo.menu.Separator.superclass.constructor.call(this, config);
36353 };
36354
36355 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36356     /**
36357      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36358      */
36359     itemCls : "x-menu-sep",
36360     /**
36361      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36362      */
36363     hideOnClick : false,
36364
36365     // private
36366     onRender : function(li){
36367         var s = document.createElement("span");
36368         s.className = this.itemCls;
36369         s.innerHTML = "&#160;";
36370         this.el = s;
36371         li.addClass("x-menu-sep-li");
36372         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36373     }
36374 });/*
36375  * Based on:
36376  * Ext JS Library 1.1.1
36377  * Copyright(c) 2006-2007, Ext JS, LLC.
36378  *
36379  * Originally Released Under LGPL - original licence link has changed is not relivant.
36380  *
36381  * Fork - LGPL
36382  * <script type="text/javascript">
36383  */
36384 /**
36385  * @class Roo.menu.Item
36386  * @extends Roo.menu.BaseItem
36387  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36388  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36389  * activation and click handling.
36390  * @constructor
36391  * Creates a new Item
36392  * @param {Object} config Configuration options
36393  */
36394 Roo.menu.Item = function(config){
36395     Roo.menu.Item.superclass.constructor.call(this, config);
36396     if(this.menu){
36397         this.menu = Roo.menu.MenuMgr.get(this.menu);
36398     }
36399 };
36400 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36401     
36402     /**
36403      * @cfg {String} text
36404      * The text to show on the menu item.
36405      */
36406     text: '',
36407      /**
36408      * @cfg {String} HTML to render in menu
36409      * The text to show on the menu item (HTML version).
36410      */
36411     html: '',
36412     /**
36413      * @cfg {String} icon
36414      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36415      */
36416     icon: undefined,
36417     /**
36418      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36419      */
36420     itemCls : "x-menu-item",
36421     /**
36422      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36423      */
36424     canActivate : true,
36425     /**
36426      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36427      */
36428     showDelay: 200,
36429     // doc'd in BaseItem
36430     hideDelay: 200,
36431
36432     // private
36433     ctype: "Roo.menu.Item",
36434     
36435     // private
36436     onRender : function(container, position){
36437         var el = document.createElement("a");
36438         el.hideFocus = true;
36439         el.unselectable = "on";
36440         el.href = this.href || "#";
36441         if(this.hrefTarget){
36442             el.target = this.hrefTarget;
36443         }
36444         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36445         
36446         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36447         
36448         el.innerHTML = String.format(
36449                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36450                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36451         this.el = el;
36452         Roo.menu.Item.superclass.onRender.call(this, container, position);
36453     },
36454
36455     /**
36456      * Sets the text to display in this menu item
36457      * @param {String} text The text to display
36458      * @param {Boolean} isHTML true to indicate text is pure html.
36459      */
36460     setText : function(text, isHTML){
36461         if (isHTML) {
36462             this.html = text;
36463         } else {
36464             this.text = text;
36465             this.html = '';
36466         }
36467         if(this.rendered){
36468             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36469      
36470             this.el.update(String.format(
36471                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36472                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36473             this.parentMenu.autoWidth();
36474         }
36475     },
36476
36477     // private
36478     handleClick : function(e){
36479         if(!this.href){ // if no link defined, stop the event automatically
36480             e.stopEvent();
36481         }
36482         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36483     },
36484
36485     // private
36486     activate : function(autoExpand){
36487         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36488             this.focus();
36489             if(autoExpand){
36490                 this.expandMenu();
36491             }
36492         }
36493         return true;
36494     },
36495
36496     // private
36497     shouldDeactivate : function(e){
36498         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36499             if(this.menu && this.menu.isVisible()){
36500                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36501             }
36502             return true;
36503         }
36504         return false;
36505     },
36506
36507     // private
36508     deactivate : function(){
36509         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36510         this.hideMenu();
36511     },
36512
36513     // private
36514     expandMenu : function(autoActivate){
36515         if(!this.disabled && this.menu){
36516             clearTimeout(this.hideTimer);
36517             delete this.hideTimer;
36518             if(!this.menu.isVisible() && !this.showTimer){
36519                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36520             }else if (this.menu.isVisible() && autoActivate){
36521                 this.menu.tryActivate(0, 1);
36522             }
36523         }
36524     },
36525
36526     // private
36527     deferExpand : function(autoActivate){
36528         delete this.showTimer;
36529         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36530         if(autoActivate){
36531             this.menu.tryActivate(0, 1);
36532         }
36533     },
36534
36535     // private
36536     hideMenu : function(){
36537         clearTimeout(this.showTimer);
36538         delete this.showTimer;
36539         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36540             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36541         }
36542     },
36543
36544     // private
36545     deferHide : function(){
36546         delete this.hideTimer;
36547         this.menu.hide();
36548     }
36549 });/*
36550  * Based on:
36551  * Ext JS Library 1.1.1
36552  * Copyright(c) 2006-2007, Ext JS, LLC.
36553  *
36554  * Originally Released Under LGPL - original licence link has changed is not relivant.
36555  *
36556  * Fork - LGPL
36557  * <script type="text/javascript">
36558  */
36559  
36560 /**
36561  * @class Roo.menu.CheckItem
36562  * @extends Roo.menu.Item
36563  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36564  * @constructor
36565  * Creates a new CheckItem
36566  * @param {Object} config Configuration options
36567  */
36568 Roo.menu.CheckItem = function(config){
36569     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36570     this.addEvents({
36571         /**
36572          * @event beforecheckchange
36573          * Fires before the checked value is set, providing an opportunity to cancel if needed
36574          * @param {Roo.menu.CheckItem} this
36575          * @param {Boolean} checked The new checked value that will be set
36576          */
36577         "beforecheckchange" : true,
36578         /**
36579          * @event checkchange
36580          * Fires after the checked value has been set
36581          * @param {Roo.menu.CheckItem} this
36582          * @param {Boolean} checked The checked value that was set
36583          */
36584         "checkchange" : true
36585     });
36586     if(this.checkHandler){
36587         this.on('checkchange', this.checkHandler, this.scope);
36588     }
36589 };
36590 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36591     /**
36592      * @cfg {String} group
36593      * All check items with the same group name will automatically be grouped into a single-select
36594      * radio button group (defaults to '')
36595      */
36596     /**
36597      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36598      */
36599     itemCls : "x-menu-item x-menu-check-item",
36600     /**
36601      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36602      */
36603     groupClass : "x-menu-group-item",
36604
36605     /**
36606      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36607      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36608      * initialized with checked = true will be rendered as checked.
36609      */
36610     checked: false,
36611
36612     // private
36613     ctype: "Roo.menu.CheckItem",
36614
36615     // private
36616     onRender : function(c){
36617         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36618         if(this.group){
36619             this.el.addClass(this.groupClass);
36620         }
36621         Roo.menu.MenuMgr.registerCheckable(this);
36622         if(this.checked){
36623             this.checked = false;
36624             this.setChecked(true, true);
36625         }
36626     },
36627
36628     // private
36629     destroy : function(){
36630         if(this.rendered){
36631             Roo.menu.MenuMgr.unregisterCheckable(this);
36632         }
36633         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36634     },
36635
36636     /**
36637      * Set the checked state of this item
36638      * @param {Boolean} checked The new checked value
36639      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36640      */
36641     setChecked : function(state, suppressEvent){
36642         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36643             if(this.container){
36644                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36645             }
36646             this.checked = state;
36647             if(suppressEvent !== true){
36648                 this.fireEvent("checkchange", this, state);
36649             }
36650         }
36651     },
36652
36653     // private
36654     handleClick : function(e){
36655        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36656            this.setChecked(!this.checked);
36657        }
36658        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36659     }
36660 });/*
36661  * Based on:
36662  * Ext JS Library 1.1.1
36663  * Copyright(c) 2006-2007, Ext JS, LLC.
36664  *
36665  * Originally Released Under LGPL - original licence link has changed is not relivant.
36666  *
36667  * Fork - LGPL
36668  * <script type="text/javascript">
36669  */
36670  
36671 /**
36672  * @class Roo.menu.DateItem
36673  * @extends Roo.menu.Adapter
36674  * A menu item that wraps the {@link Roo.DatPicker} component.
36675  * @constructor
36676  * Creates a new DateItem
36677  * @param {Object} config Configuration options
36678  */
36679 Roo.menu.DateItem = function(config){
36680     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36681     /** The Roo.DatePicker object @type Roo.DatePicker */
36682     this.picker = this.component;
36683     this.addEvents({select: true});
36684     
36685     this.picker.on("render", function(picker){
36686         picker.getEl().swallowEvent("click");
36687         picker.container.addClass("x-menu-date-item");
36688     });
36689
36690     this.picker.on("select", this.onSelect, this);
36691 };
36692
36693 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36694     // private
36695     onSelect : function(picker, date){
36696         this.fireEvent("select", this, date, picker);
36697         Roo.menu.DateItem.superclass.handleClick.call(this);
36698     }
36699 });/*
36700  * Based on:
36701  * Ext JS Library 1.1.1
36702  * Copyright(c) 2006-2007, Ext JS, LLC.
36703  *
36704  * Originally Released Under LGPL - original licence link has changed is not relivant.
36705  *
36706  * Fork - LGPL
36707  * <script type="text/javascript">
36708  */
36709  
36710 /**
36711  * @class Roo.menu.ColorItem
36712  * @extends Roo.menu.Adapter
36713  * A menu item that wraps the {@link Roo.ColorPalette} component.
36714  * @constructor
36715  * Creates a new ColorItem
36716  * @param {Object} config Configuration options
36717  */
36718 Roo.menu.ColorItem = function(config){
36719     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36720     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36721     this.palette = this.component;
36722     this.relayEvents(this.palette, ["select"]);
36723     if(this.selectHandler){
36724         this.on('select', this.selectHandler, this.scope);
36725     }
36726 };
36727 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36728  * Based on:
36729  * Ext JS Library 1.1.1
36730  * Copyright(c) 2006-2007, Ext JS, LLC.
36731  *
36732  * Originally Released Under LGPL - original licence link has changed is not relivant.
36733  *
36734  * Fork - LGPL
36735  * <script type="text/javascript">
36736  */
36737  
36738
36739 /**
36740  * @class Roo.menu.DateMenu
36741  * @extends Roo.menu.Menu
36742  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36743  * @constructor
36744  * Creates a new DateMenu
36745  * @param {Object} config Configuration options
36746  */
36747 Roo.menu.DateMenu = function(config){
36748     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36749     this.plain = true;
36750     var di = new Roo.menu.DateItem(config);
36751     this.add(di);
36752     /**
36753      * The {@link Roo.DatePicker} instance for this DateMenu
36754      * @type DatePicker
36755      */
36756     this.picker = di.picker;
36757     /**
36758      * @event select
36759      * @param {DatePicker} picker
36760      * @param {Date} date
36761      */
36762     this.relayEvents(di, ["select"]);
36763     this.on('beforeshow', function(){
36764         if(this.picker){
36765             this.picker.hideMonthPicker(false);
36766         }
36767     }, this);
36768 };
36769 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36770     cls:'x-date-menu'
36771 });/*
36772  * Based on:
36773  * Ext JS Library 1.1.1
36774  * Copyright(c) 2006-2007, Ext JS, LLC.
36775  *
36776  * Originally Released Under LGPL - original licence link has changed is not relivant.
36777  *
36778  * Fork - LGPL
36779  * <script type="text/javascript">
36780  */
36781  
36782
36783 /**
36784  * @class Roo.menu.ColorMenu
36785  * @extends Roo.menu.Menu
36786  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36787  * @constructor
36788  * Creates a new ColorMenu
36789  * @param {Object} config Configuration options
36790  */
36791 Roo.menu.ColorMenu = function(config){
36792     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36793     this.plain = true;
36794     var ci = new Roo.menu.ColorItem(config);
36795     this.add(ci);
36796     /**
36797      * The {@link Roo.ColorPalette} instance for this ColorMenu
36798      * @type ColorPalette
36799      */
36800     this.palette = ci.palette;
36801     /**
36802      * @event select
36803      * @param {ColorPalette} palette
36804      * @param {String} color
36805      */
36806     this.relayEvents(ci, ["select"]);
36807 };
36808 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36809  * Based on:
36810  * Ext JS Library 1.1.1
36811  * Copyright(c) 2006-2007, Ext JS, LLC.
36812  *
36813  * Originally Released Under LGPL - original licence link has changed is not relivant.
36814  *
36815  * Fork - LGPL
36816  * <script type="text/javascript">
36817  */
36818  
36819 /**
36820  * @class Roo.form.Field
36821  * @extends Roo.BoxComponent
36822  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36823  * @constructor
36824  * Creates a new Field
36825  * @param {Object} config Configuration options
36826  */
36827 Roo.form.Field = function(config){
36828     Roo.form.Field.superclass.constructor.call(this, config);
36829 };
36830
36831 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36832     /**
36833      * @cfg {String} fieldLabel Label to use when rendering a form.
36834      */
36835        /**
36836      * @cfg {String} qtip Mouse over tip
36837      */
36838      
36839     /**
36840      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36841      */
36842     invalidClass : "x-form-invalid",
36843     /**
36844      * @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")
36845      */
36846     invalidText : "The value in this field is invalid",
36847     /**
36848      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36849      */
36850     focusClass : "x-form-focus",
36851     /**
36852      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36853       automatic validation (defaults to "keyup").
36854      */
36855     validationEvent : "keyup",
36856     /**
36857      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36858      */
36859     validateOnBlur : true,
36860     /**
36861      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36862      */
36863     validationDelay : 250,
36864     /**
36865      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36866      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36867      */
36868     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36869     /**
36870      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36871      */
36872     fieldClass : "x-form-field",
36873     /**
36874      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36875      *<pre>
36876 Value         Description
36877 -----------   ----------------------------------------------------------------------
36878 qtip          Display a quick tip when the user hovers over the field
36879 title         Display a default browser title attribute popup
36880 under         Add a block div beneath the field containing the error text
36881 side          Add an error icon to the right of the field with a popup on hover
36882 [element id]  Add the error text directly to the innerHTML of the specified element
36883 </pre>
36884      */
36885     msgTarget : 'qtip',
36886     /**
36887      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36888      */
36889     msgFx : 'normal',
36890
36891     /**
36892      * @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.
36893      */
36894     readOnly : false,
36895
36896     /**
36897      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36898      */
36899     disabled : false,
36900
36901     /**
36902      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36903      */
36904     inputType : undefined,
36905     
36906     /**
36907      * @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).
36908          */
36909         tabIndex : undefined,
36910         
36911     // private
36912     isFormField : true,
36913
36914     // private
36915     hasFocus : false,
36916     /**
36917      * @property {Roo.Element} fieldEl
36918      * Element Containing the rendered Field (with label etc.)
36919      */
36920     /**
36921      * @cfg {Mixed} value A value to initialize this field with.
36922      */
36923     value : undefined,
36924
36925     /**
36926      * @cfg {String} name The field's HTML name attribute.
36927      */
36928     /**
36929      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36930      */
36931
36932         // private ??
36933         initComponent : function(){
36934         Roo.form.Field.superclass.initComponent.call(this);
36935         this.addEvents({
36936             /**
36937              * @event focus
36938              * Fires when this field receives input focus.
36939              * @param {Roo.form.Field} this
36940              */
36941             focus : true,
36942             /**
36943              * @event blur
36944              * Fires when this field loses input focus.
36945              * @param {Roo.form.Field} this
36946              */
36947             blur : true,
36948             /**
36949              * @event specialkey
36950              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36951              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36952              * @param {Roo.form.Field} this
36953              * @param {Roo.EventObject} e The event object
36954              */
36955             specialkey : true,
36956             /**
36957              * @event change
36958              * Fires just before the field blurs if the field value has changed.
36959              * @param {Roo.form.Field} this
36960              * @param {Mixed} newValue The new value
36961              * @param {Mixed} oldValue The original value
36962              */
36963             change : true,
36964             /**
36965              * @event invalid
36966              * Fires after the field has been marked as invalid.
36967              * @param {Roo.form.Field} this
36968              * @param {String} msg The validation message
36969              */
36970             invalid : true,
36971             /**
36972              * @event valid
36973              * Fires after the field has been validated with no errors.
36974              * @param {Roo.form.Field} this
36975              */
36976             valid : true,
36977              /**
36978              * @event keyup
36979              * Fires after the key up
36980              * @param {Roo.form.Field} this
36981              * @param {Roo.EventObject}  e The event Object
36982              */
36983             keyup : true
36984         });
36985     },
36986
36987     /**
36988      * Returns the name attribute of the field if available
36989      * @return {String} name The field name
36990      */
36991     getName: function(){
36992          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36993     },
36994
36995     // private
36996     onRender : function(ct, position){
36997         Roo.form.Field.superclass.onRender.call(this, ct, position);
36998         if(!this.el){
36999             var cfg = this.getAutoCreate();
37000             if(!cfg.name){
37001                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37002             }
37003             if (!cfg.name.length) {
37004                 delete cfg.name;
37005             }
37006             if(this.inputType){
37007                 cfg.type = this.inputType;
37008             }
37009             this.el = ct.createChild(cfg, position);
37010         }
37011         var type = this.el.dom.type;
37012         if(type){
37013             if(type == 'password'){
37014                 type = 'text';
37015             }
37016             this.el.addClass('x-form-'+type);
37017         }
37018         if(this.readOnly){
37019             this.el.dom.readOnly = true;
37020         }
37021         if(this.tabIndex !== undefined){
37022             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37023         }
37024
37025         this.el.addClass([this.fieldClass, this.cls]);
37026         this.initValue();
37027     },
37028
37029     /**
37030      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37031      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37032      * @return {Roo.form.Field} this
37033      */
37034     applyTo : function(target){
37035         this.allowDomMove = false;
37036         this.el = Roo.get(target);
37037         this.render(this.el.dom.parentNode);
37038         return this;
37039     },
37040
37041     // private
37042     initValue : function(){
37043         if(this.value !== undefined){
37044             this.setValue(this.value);
37045         }else if(this.el.dom.value.length > 0){
37046             this.setValue(this.el.dom.value);
37047         }
37048     },
37049
37050     /**
37051      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37052      */
37053     isDirty : function() {
37054         if(this.disabled) {
37055             return false;
37056         }
37057         return String(this.getValue()) !== String(this.originalValue);
37058     },
37059
37060     // private
37061     afterRender : function(){
37062         Roo.form.Field.superclass.afterRender.call(this);
37063         this.initEvents();
37064     },
37065
37066     // private
37067     fireKey : function(e){
37068         //Roo.log('field ' + e.getKey());
37069         if(e.isNavKeyPress()){
37070             this.fireEvent("specialkey", this, e);
37071         }
37072     },
37073
37074     /**
37075      * Resets the current field value to the originally loaded value and clears any validation messages
37076      */
37077     reset : function(){
37078         this.setValue(this.resetValue);
37079         this.clearInvalid();
37080     },
37081
37082     // private
37083     initEvents : function(){
37084         // safari killled keypress - so keydown is now used..
37085         this.el.on("keydown" , this.fireKey,  this);
37086         this.el.on("focus", this.onFocus,  this);
37087         this.el.on("blur", this.onBlur,  this);
37088         this.el.relayEvent('keyup', this);
37089
37090         // reference to original value for reset
37091         this.originalValue = this.getValue();
37092         this.resetValue =  this.getValue();
37093     },
37094
37095     // private
37096     onFocus : function(){
37097         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37098             this.el.addClass(this.focusClass);
37099         }
37100         if(!this.hasFocus){
37101             this.hasFocus = true;
37102             this.startValue = this.getValue();
37103             this.fireEvent("focus", this);
37104         }
37105     },
37106
37107     beforeBlur : Roo.emptyFn,
37108
37109     // private
37110     onBlur : function(){
37111         this.beforeBlur();
37112         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37113             this.el.removeClass(this.focusClass);
37114         }
37115         this.hasFocus = false;
37116         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37117             this.validate();
37118         }
37119         var v = this.getValue();
37120         if(String(v) !== String(this.startValue)){
37121             this.fireEvent('change', this, v, this.startValue);
37122         }
37123         this.fireEvent("blur", this);
37124     },
37125
37126     /**
37127      * Returns whether or not the field value is currently valid
37128      * @param {Boolean} preventMark True to disable marking the field invalid
37129      * @return {Boolean} True if the value is valid, else false
37130      */
37131     isValid : function(preventMark){
37132         if(this.disabled){
37133             return true;
37134         }
37135         var restore = this.preventMark;
37136         this.preventMark = preventMark === true;
37137         var v = this.validateValue(this.processValue(this.getRawValue()));
37138         this.preventMark = restore;
37139         return v;
37140     },
37141
37142     /**
37143      * Validates the field value
37144      * @return {Boolean} True if the value is valid, else false
37145      */
37146     validate : function(){
37147         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37148             this.clearInvalid();
37149             return true;
37150         }
37151         return false;
37152     },
37153
37154     processValue : function(value){
37155         return value;
37156     },
37157
37158     // private
37159     // Subclasses should provide the validation implementation by overriding this
37160     validateValue : function(value){
37161         return true;
37162     },
37163
37164     /**
37165      * Mark this field as invalid
37166      * @param {String} msg The validation message
37167      */
37168     markInvalid : function(msg){
37169         if(!this.rendered || this.preventMark){ // not rendered
37170             return;
37171         }
37172         
37173         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37174         
37175         obj.el.addClass(this.invalidClass);
37176         msg = msg || this.invalidText;
37177         switch(this.msgTarget){
37178             case 'qtip':
37179                 obj.el.dom.qtip = msg;
37180                 obj.el.dom.qclass = 'x-form-invalid-tip';
37181                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37182                     Roo.QuickTips.enable();
37183                 }
37184                 break;
37185             case 'title':
37186                 this.el.dom.title = msg;
37187                 break;
37188             case 'under':
37189                 if(!this.errorEl){
37190                     var elp = this.el.findParent('.x-form-element', 5, true);
37191                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37192                     this.errorEl.setWidth(elp.getWidth(true)-20);
37193                 }
37194                 this.errorEl.update(msg);
37195                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37196                 break;
37197             case 'side':
37198                 if(!this.errorIcon){
37199                     var elp = this.el.findParent('.x-form-element', 5, true);
37200                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37201                 }
37202                 this.alignErrorIcon();
37203                 this.errorIcon.dom.qtip = msg;
37204                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37205                 this.errorIcon.show();
37206                 this.on('resize', this.alignErrorIcon, this);
37207                 break;
37208             default:
37209                 var t = Roo.getDom(this.msgTarget);
37210                 t.innerHTML = msg;
37211                 t.style.display = this.msgDisplay;
37212                 break;
37213         }
37214         this.fireEvent('invalid', this, msg);
37215     },
37216
37217     // private
37218     alignErrorIcon : function(){
37219         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37220     },
37221
37222     /**
37223      * Clear any invalid styles/messages for this field
37224      */
37225     clearInvalid : function(){
37226         if(!this.rendered || this.preventMark){ // not rendered
37227             return;
37228         }
37229         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37230         
37231         obj.el.removeClass(this.invalidClass);
37232         switch(this.msgTarget){
37233             case 'qtip':
37234                 obj.el.dom.qtip = '';
37235                 break;
37236             case 'title':
37237                 this.el.dom.title = '';
37238                 break;
37239             case 'under':
37240                 if(this.errorEl){
37241                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37242                 }
37243                 break;
37244             case 'side':
37245                 if(this.errorIcon){
37246                     this.errorIcon.dom.qtip = '';
37247                     this.errorIcon.hide();
37248                     this.un('resize', this.alignErrorIcon, this);
37249                 }
37250                 break;
37251             default:
37252                 var t = Roo.getDom(this.msgTarget);
37253                 t.innerHTML = '';
37254                 t.style.display = 'none';
37255                 break;
37256         }
37257         this.fireEvent('valid', this);
37258     },
37259
37260     /**
37261      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37262      * @return {Mixed} value The field value
37263      */
37264     getRawValue : function(){
37265         var v = this.el.getValue();
37266         
37267         return v;
37268     },
37269
37270     /**
37271      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37272      * @return {Mixed} value The field value
37273      */
37274     getValue : function(){
37275         var v = this.el.getValue();
37276          
37277         return v;
37278     },
37279
37280     /**
37281      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37282      * @param {Mixed} value The value to set
37283      */
37284     setRawValue : function(v){
37285         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37286     },
37287
37288     /**
37289      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37290      * @param {Mixed} value The value to set
37291      */
37292     setValue : function(v){
37293         this.value = v;
37294         if(this.rendered){
37295             this.el.dom.value = (v === null || v === undefined ? '' : v);
37296              this.validate();
37297         }
37298     },
37299
37300     adjustSize : function(w, h){
37301         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37302         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37303         return s;
37304     },
37305
37306     adjustWidth : function(tag, w){
37307         tag = tag.toLowerCase();
37308         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37309             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37310                 if(tag == 'input'){
37311                     return w + 2;
37312                 }
37313                 if(tag == 'textarea'){
37314                     return w-2;
37315                 }
37316             }else if(Roo.isOpera){
37317                 if(tag == 'input'){
37318                     return w + 2;
37319                 }
37320                 if(tag == 'textarea'){
37321                     return w-2;
37322                 }
37323             }
37324         }
37325         return w;
37326     }
37327 });
37328
37329
37330 // anything other than normal should be considered experimental
37331 Roo.form.Field.msgFx = {
37332     normal : {
37333         show: function(msgEl, f){
37334             msgEl.setDisplayed('block');
37335         },
37336
37337         hide : function(msgEl, f){
37338             msgEl.setDisplayed(false).update('');
37339         }
37340     },
37341
37342     slide : {
37343         show: function(msgEl, f){
37344             msgEl.slideIn('t', {stopFx:true});
37345         },
37346
37347         hide : function(msgEl, f){
37348             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37349         }
37350     },
37351
37352     slideRight : {
37353         show: function(msgEl, f){
37354             msgEl.fixDisplay();
37355             msgEl.alignTo(f.el, 'tl-tr');
37356             msgEl.slideIn('l', {stopFx:true});
37357         },
37358
37359         hide : function(msgEl, f){
37360             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37361         }
37362     }
37363 };/*
37364  * Based on:
37365  * Ext JS Library 1.1.1
37366  * Copyright(c) 2006-2007, Ext JS, LLC.
37367  *
37368  * Originally Released Under LGPL - original licence link has changed is not relivant.
37369  *
37370  * Fork - LGPL
37371  * <script type="text/javascript">
37372  */
37373  
37374
37375 /**
37376  * @class Roo.form.TextField
37377  * @extends Roo.form.Field
37378  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37379  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37380  * @constructor
37381  * Creates a new TextField
37382  * @param {Object} config Configuration options
37383  */
37384 Roo.form.TextField = function(config){
37385     Roo.form.TextField.superclass.constructor.call(this, config);
37386     this.addEvents({
37387         /**
37388          * @event autosize
37389          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37390          * according to the default logic, but this event provides a hook for the developer to apply additional
37391          * logic at runtime to resize the field if needed.
37392              * @param {Roo.form.Field} this This text field
37393              * @param {Number} width The new field width
37394              */
37395         autosize : true
37396     });
37397 };
37398
37399 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37400     /**
37401      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37402      */
37403     grow : false,
37404     /**
37405      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37406      */
37407     growMin : 30,
37408     /**
37409      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37410      */
37411     growMax : 800,
37412     /**
37413      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37414      */
37415     vtype : null,
37416     /**
37417      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37418      */
37419     maskRe : null,
37420     /**
37421      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37422      */
37423     disableKeyFilter : false,
37424     /**
37425      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37426      */
37427     allowBlank : true,
37428     /**
37429      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37430      */
37431     minLength : 0,
37432     /**
37433      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37434      */
37435     maxLength : Number.MAX_VALUE,
37436     /**
37437      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37438      */
37439     minLengthText : "The minimum length for this field is {0}",
37440     /**
37441      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37442      */
37443     maxLengthText : "The maximum length for this field is {0}",
37444     /**
37445      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37446      */
37447     selectOnFocus : false,
37448     /**
37449      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37450      */
37451     blankText : "This field is required",
37452     /**
37453      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37454      * If available, this function will be called only after the basic validators all return true, and will be passed the
37455      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37456      */
37457     validator : null,
37458     /**
37459      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37460      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37461      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37462      */
37463     regex : null,
37464     /**
37465      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37466      */
37467     regexText : "",
37468     /**
37469      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37470      */
37471     emptyText : null,
37472    
37473
37474     // private
37475     initEvents : function()
37476     {
37477         if (this.emptyText) {
37478             this.el.attr('placeholder', this.emptyText);
37479         }
37480         
37481         Roo.form.TextField.superclass.initEvents.call(this);
37482         if(this.validationEvent == 'keyup'){
37483             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37484             this.el.on('keyup', this.filterValidation, this);
37485         }
37486         else if(this.validationEvent !== false){
37487             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37488         }
37489         
37490         if(this.selectOnFocus){
37491             this.on("focus", this.preFocus, this);
37492             
37493         }
37494         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37495             this.el.on("keypress", this.filterKeys, this);
37496         }
37497         if(this.grow){
37498             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37499             this.el.on("click", this.autoSize,  this);
37500         }
37501         if(this.el.is('input[type=password]') && Roo.isSafari){
37502             this.el.on('keydown', this.SafariOnKeyDown, this);
37503         }
37504     },
37505
37506     processValue : function(value){
37507         if(this.stripCharsRe){
37508             var newValue = value.replace(this.stripCharsRe, '');
37509             if(newValue !== value){
37510                 this.setRawValue(newValue);
37511                 return newValue;
37512             }
37513         }
37514         return value;
37515     },
37516
37517     filterValidation : function(e){
37518         if(!e.isNavKeyPress()){
37519             this.validationTask.delay(this.validationDelay);
37520         }
37521     },
37522
37523     // private
37524     onKeyUp : function(e){
37525         if(!e.isNavKeyPress()){
37526             this.autoSize();
37527         }
37528     },
37529
37530     /**
37531      * Resets the current field value to the originally-loaded value and clears any validation messages.
37532      *  
37533      */
37534     reset : function(){
37535         Roo.form.TextField.superclass.reset.call(this);
37536        
37537     },
37538
37539     
37540     // private
37541     preFocus : function(){
37542         
37543         if(this.selectOnFocus){
37544             this.el.dom.select();
37545         }
37546     },
37547
37548     
37549     // private
37550     filterKeys : function(e){
37551         var k = e.getKey();
37552         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37553             return;
37554         }
37555         var c = e.getCharCode(), cc = String.fromCharCode(c);
37556         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37557             return;
37558         }
37559         if(!this.maskRe.test(cc)){
37560             e.stopEvent();
37561         }
37562     },
37563
37564     setValue : function(v){
37565         
37566         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37567         
37568         this.autoSize();
37569     },
37570
37571     /**
37572      * Validates a value according to the field's validation rules and marks the field as invalid
37573      * if the validation fails
37574      * @param {Mixed} value The value to validate
37575      * @return {Boolean} True if the value is valid, else false
37576      */
37577     validateValue : function(value){
37578         if(value.length < 1)  { // if it's blank
37579              if(this.allowBlank){
37580                 this.clearInvalid();
37581                 return true;
37582              }else{
37583                 this.markInvalid(this.blankText);
37584                 return false;
37585              }
37586         }
37587         if(value.length < this.minLength){
37588             this.markInvalid(String.format(this.minLengthText, this.minLength));
37589             return false;
37590         }
37591         if(value.length > this.maxLength){
37592             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37593             return false;
37594         }
37595         if(this.vtype){
37596             var vt = Roo.form.VTypes;
37597             if(!vt[this.vtype](value, this)){
37598                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37599                 return false;
37600             }
37601         }
37602         if(typeof this.validator == "function"){
37603             var msg = this.validator(value);
37604             if(msg !== true){
37605                 this.markInvalid(msg);
37606                 return false;
37607             }
37608         }
37609         if(this.regex && !this.regex.test(value)){
37610             this.markInvalid(this.regexText);
37611             return false;
37612         }
37613         return true;
37614     },
37615
37616     /**
37617      * Selects text in this field
37618      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37619      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37620      */
37621     selectText : function(start, end){
37622         var v = this.getRawValue();
37623         if(v.length > 0){
37624             start = start === undefined ? 0 : start;
37625             end = end === undefined ? v.length : end;
37626             var d = this.el.dom;
37627             if(d.setSelectionRange){
37628                 d.setSelectionRange(start, end);
37629             }else if(d.createTextRange){
37630                 var range = d.createTextRange();
37631                 range.moveStart("character", start);
37632                 range.moveEnd("character", v.length-end);
37633                 range.select();
37634             }
37635         }
37636     },
37637
37638     /**
37639      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37640      * This only takes effect if grow = true, and fires the autosize event.
37641      */
37642     autoSize : function(){
37643         if(!this.grow || !this.rendered){
37644             return;
37645         }
37646         if(!this.metrics){
37647             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37648         }
37649         var el = this.el;
37650         var v = el.dom.value;
37651         var d = document.createElement('div');
37652         d.appendChild(document.createTextNode(v));
37653         v = d.innerHTML;
37654         d = null;
37655         v += "&#160;";
37656         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37657         this.el.setWidth(w);
37658         this.fireEvent("autosize", this, w);
37659     },
37660     
37661     // private
37662     SafariOnKeyDown : function(event)
37663     {
37664         // this is a workaround for a password hang bug on chrome/ webkit.
37665         
37666         var isSelectAll = false;
37667         
37668         if(this.el.dom.selectionEnd > 0){
37669             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37670         }
37671         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37672             event.preventDefault();
37673             this.setValue('');
37674             return;
37675         }
37676         
37677         if(isSelectAll){ // backspace and delete key
37678             
37679             event.preventDefault();
37680             // this is very hacky as keydown always get's upper case.
37681             //
37682             var cc = String.fromCharCode(event.getCharCode());
37683             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37684             
37685         }
37686         
37687         
37688     }
37689 });/*
37690  * Based on:
37691  * Ext JS Library 1.1.1
37692  * Copyright(c) 2006-2007, Ext JS, LLC.
37693  *
37694  * Originally Released Under LGPL - original licence link has changed is not relivant.
37695  *
37696  * Fork - LGPL
37697  * <script type="text/javascript">
37698  */
37699  
37700 /**
37701  * @class Roo.form.Hidden
37702  * @extends Roo.form.TextField
37703  * Simple Hidden element used on forms 
37704  * 
37705  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37706  * 
37707  * @constructor
37708  * Creates a new Hidden form element.
37709  * @param {Object} config Configuration options
37710  */
37711
37712
37713
37714 // easy hidden field...
37715 Roo.form.Hidden = function(config){
37716     Roo.form.Hidden.superclass.constructor.call(this, config);
37717 };
37718   
37719 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37720     fieldLabel:      '',
37721     inputType:      'hidden',
37722     width:          50,
37723     allowBlank:     true,
37724     labelSeparator: '',
37725     hidden:         true,
37726     itemCls :       'x-form-item-display-none'
37727
37728
37729 });
37730
37731
37732 /*
37733  * Based on:
37734  * Ext JS Library 1.1.1
37735  * Copyright(c) 2006-2007, Ext JS, LLC.
37736  *
37737  * Originally Released Under LGPL - original licence link has changed is not relivant.
37738  *
37739  * Fork - LGPL
37740  * <script type="text/javascript">
37741  */
37742  
37743 /**
37744  * @class Roo.form.TriggerField
37745  * @extends Roo.form.TextField
37746  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37747  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37748  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37749  * for which you can provide a custom implementation.  For example:
37750  * <pre><code>
37751 var trigger = new Roo.form.TriggerField();
37752 trigger.onTriggerClick = myTriggerFn;
37753 trigger.applyTo('my-field');
37754 </code></pre>
37755  *
37756  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37757  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37758  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37759  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37760  * @constructor
37761  * Create a new TriggerField.
37762  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37763  * to the base TextField)
37764  */
37765 Roo.form.TriggerField = function(config){
37766     this.mimicing = false;
37767     Roo.form.TriggerField.superclass.constructor.call(this, config);
37768 };
37769
37770 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37771     /**
37772      * @cfg {String} triggerClass A CSS class to apply to the trigger
37773      */
37774     /**
37775      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37776      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37777      */
37778     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37779     /**
37780      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37781      */
37782     hideTrigger:false,
37783
37784     /** @cfg {Boolean} grow @hide */
37785     /** @cfg {Number} growMin @hide */
37786     /** @cfg {Number} growMax @hide */
37787
37788     /**
37789      * @hide 
37790      * @method
37791      */
37792     autoSize: Roo.emptyFn,
37793     // private
37794     monitorTab : true,
37795     // private
37796     deferHeight : true,
37797
37798     
37799     actionMode : 'wrap',
37800     // private
37801     onResize : function(w, h){
37802         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37803         if(typeof w == 'number'){
37804             var x = w - this.trigger.getWidth();
37805             this.el.setWidth(this.adjustWidth('input', x));
37806             this.trigger.setStyle('left', x+'px');
37807         }
37808     },
37809
37810     // private
37811     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37812
37813     // private
37814     getResizeEl : function(){
37815         return this.wrap;
37816     },
37817
37818     // private
37819     getPositionEl : function(){
37820         return this.wrap;
37821     },
37822
37823     // private
37824     alignErrorIcon : function(){
37825         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37826     },
37827
37828     // private
37829     onRender : function(ct, position){
37830         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37831         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37832         this.trigger = this.wrap.createChild(this.triggerConfig ||
37833                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37834         if(this.hideTrigger){
37835             this.trigger.setDisplayed(false);
37836         }
37837         this.initTrigger();
37838         if(!this.width){
37839             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37840         }
37841     },
37842
37843     // private
37844     initTrigger : function(){
37845         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37846         this.trigger.addClassOnOver('x-form-trigger-over');
37847         this.trigger.addClassOnClick('x-form-trigger-click');
37848     },
37849
37850     // private
37851     onDestroy : function(){
37852         if(this.trigger){
37853             this.trigger.removeAllListeners();
37854             this.trigger.remove();
37855         }
37856         if(this.wrap){
37857             this.wrap.remove();
37858         }
37859         Roo.form.TriggerField.superclass.onDestroy.call(this);
37860     },
37861
37862     // private
37863     onFocus : function(){
37864         Roo.form.TriggerField.superclass.onFocus.call(this);
37865         if(!this.mimicing){
37866             this.wrap.addClass('x-trigger-wrap-focus');
37867             this.mimicing = true;
37868             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37869             if(this.monitorTab){
37870                 this.el.on("keydown", this.checkTab, this);
37871             }
37872         }
37873     },
37874
37875     // private
37876     checkTab : function(e){
37877         if(e.getKey() == e.TAB){
37878             this.triggerBlur();
37879         }
37880     },
37881
37882     // private
37883     onBlur : function(){
37884         // do nothing
37885     },
37886
37887     // private
37888     mimicBlur : function(e, t){
37889         if(!this.wrap.contains(t) && this.validateBlur()){
37890             this.triggerBlur();
37891         }
37892     },
37893
37894     // private
37895     triggerBlur : function(){
37896         this.mimicing = false;
37897         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37898         if(this.monitorTab){
37899             this.el.un("keydown", this.checkTab, this);
37900         }
37901         this.wrap.removeClass('x-trigger-wrap-focus');
37902         Roo.form.TriggerField.superclass.onBlur.call(this);
37903     },
37904
37905     // private
37906     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37907     validateBlur : function(e, t){
37908         return true;
37909     },
37910
37911     // private
37912     onDisable : function(){
37913         Roo.form.TriggerField.superclass.onDisable.call(this);
37914         if(this.wrap){
37915             this.wrap.addClass('x-item-disabled');
37916         }
37917     },
37918
37919     // private
37920     onEnable : function(){
37921         Roo.form.TriggerField.superclass.onEnable.call(this);
37922         if(this.wrap){
37923             this.wrap.removeClass('x-item-disabled');
37924         }
37925     },
37926
37927     // private
37928     onShow : function(){
37929         var ae = this.getActionEl();
37930         
37931         if(ae){
37932             ae.dom.style.display = '';
37933             ae.dom.style.visibility = 'visible';
37934         }
37935     },
37936
37937     // private
37938     
37939     onHide : function(){
37940         var ae = this.getActionEl();
37941         ae.dom.style.display = 'none';
37942     },
37943
37944     /**
37945      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37946      * by an implementing function.
37947      * @method
37948      * @param {EventObject} e
37949      */
37950     onTriggerClick : Roo.emptyFn
37951 });
37952
37953 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37954 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37955 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37956 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37957     initComponent : function(){
37958         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37959
37960         this.triggerConfig = {
37961             tag:'span', cls:'x-form-twin-triggers', cn:[
37962             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37963             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37964         ]};
37965     },
37966
37967     getTrigger : function(index){
37968         return this.triggers[index];
37969     },
37970
37971     initTrigger : function(){
37972         var ts = this.trigger.select('.x-form-trigger', true);
37973         this.wrap.setStyle('overflow', 'hidden');
37974         var triggerField = this;
37975         ts.each(function(t, all, index){
37976             t.hide = function(){
37977                 var w = triggerField.wrap.getWidth();
37978                 this.dom.style.display = 'none';
37979                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37980             };
37981             t.show = function(){
37982                 var w = triggerField.wrap.getWidth();
37983                 this.dom.style.display = '';
37984                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37985             };
37986             var triggerIndex = 'Trigger'+(index+1);
37987
37988             if(this['hide'+triggerIndex]){
37989                 t.dom.style.display = 'none';
37990             }
37991             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37992             t.addClassOnOver('x-form-trigger-over');
37993             t.addClassOnClick('x-form-trigger-click');
37994         }, this);
37995         this.triggers = ts.elements;
37996     },
37997
37998     onTrigger1Click : Roo.emptyFn,
37999     onTrigger2Click : Roo.emptyFn
38000 });/*
38001  * Based on:
38002  * Ext JS Library 1.1.1
38003  * Copyright(c) 2006-2007, Ext JS, LLC.
38004  *
38005  * Originally Released Under LGPL - original licence link has changed is not relivant.
38006  *
38007  * Fork - LGPL
38008  * <script type="text/javascript">
38009  */
38010  
38011 /**
38012  * @class Roo.form.TextArea
38013  * @extends Roo.form.TextField
38014  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38015  * support for auto-sizing.
38016  * @constructor
38017  * Creates a new TextArea
38018  * @param {Object} config Configuration options
38019  */
38020 Roo.form.TextArea = function(config){
38021     Roo.form.TextArea.superclass.constructor.call(this, config);
38022     // these are provided exchanges for backwards compat
38023     // minHeight/maxHeight were replaced by growMin/growMax to be
38024     // compatible with TextField growing config values
38025     if(this.minHeight !== undefined){
38026         this.growMin = this.minHeight;
38027     }
38028     if(this.maxHeight !== undefined){
38029         this.growMax = this.maxHeight;
38030     }
38031 };
38032
38033 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38034     /**
38035      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38036      */
38037     growMin : 60,
38038     /**
38039      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38040      */
38041     growMax: 1000,
38042     /**
38043      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38044      * in the field (equivalent to setting overflow: hidden, defaults to false)
38045      */
38046     preventScrollbars: false,
38047     /**
38048      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38049      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38050      */
38051
38052     // private
38053     onRender : function(ct, position){
38054         if(!this.el){
38055             this.defaultAutoCreate = {
38056                 tag: "textarea",
38057                 style:"width:300px;height:60px;",
38058                 autocomplete: "off"
38059             };
38060         }
38061         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38062         if(this.grow){
38063             this.textSizeEl = Roo.DomHelper.append(document.body, {
38064                 tag: "pre", cls: "x-form-grow-sizer"
38065             });
38066             if(this.preventScrollbars){
38067                 this.el.setStyle("overflow", "hidden");
38068             }
38069             this.el.setHeight(this.growMin);
38070         }
38071     },
38072
38073     onDestroy : function(){
38074         if(this.textSizeEl){
38075             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38076         }
38077         Roo.form.TextArea.superclass.onDestroy.call(this);
38078     },
38079
38080     // private
38081     onKeyUp : function(e){
38082         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38083             this.autoSize();
38084         }
38085     },
38086
38087     /**
38088      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38089      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38090      */
38091     autoSize : function(){
38092         if(!this.grow || !this.textSizeEl){
38093             return;
38094         }
38095         var el = this.el;
38096         var v = el.dom.value;
38097         var ts = this.textSizeEl;
38098
38099         ts.innerHTML = '';
38100         ts.appendChild(document.createTextNode(v));
38101         v = ts.innerHTML;
38102
38103         Roo.fly(ts).setWidth(this.el.getWidth());
38104         if(v.length < 1){
38105             v = "&#160;&#160;";
38106         }else{
38107             if(Roo.isIE){
38108                 v = v.replace(/\n/g, '<p>&#160;</p>');
38109             }
38110             v += "&#160;\n&#160;";
38111         }
38112         ts.innerHTML = v;
38113         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38114         if(h != this.lastHeight){
38115             this.lastHeight = h;
38116             this.el.setHeight(h);
38117             this.fireEvent("autosize", this, h);
38118         }
38119     }
38120 });/*
38121  * Based on:
38122  * Ext JS Library 1.1.1
38123  * Copyright(c) 2006-2007, Ext JS, LLC.
38124  *
38125  * Originally Released Under LGPL - original licence link has changed is not relivant.
38126  *
38127  * Fork - LGPL
38128  * <script type="text/javascript">
38129  */
38130  
38131
38132 /**
38133  * @class Roo.form.NumberField
38134  * @extends Roo.form.TextField
38135  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38136  * @constructor
38137  * Creates a new NumberField
38138  * @param {Object} config Configuration options
38139  */
38140 Roo.form.NumberField = function(config){
38141     Roo.form.NumberField.superclass.constructor.call(this, config);
38142 };
38143
38144 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38145     /**
38146      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38147      */
38148     fieldClass: "x-form-field x-form-num-field",
38149     /**
38150      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38151      */
38152     allowDecimals : true,
38153     /**
38154      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38155      */
38156     decimalSeparator : ".",
38157     /**
38158      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38159      */
38160     decimalPrecision : 2,
38161     /**
38162      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38163      */
38164     allowNegative : true,
38165     /**
38166      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38167      */
38168     minValue : Number.NEGATIVE_INFINITY,
38169     /**
38170      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38171      */
38172     maxValue : Number.MAX_VALUE,
38173     /**
38174      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38175      */
38176     minText : "The minimum value for this field is {0}",
38177     /**
38178      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38179      */
38180     maxText : "The maximum value for this field is {0}",
38181     /**
38182      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38183      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38184      */
38185     nanText : "{0} is not a valid number",
38186
38187     // private
38188     initEvents : function(){
38189         Roo.form.NumberField.superclass.initEvents.call(this);
38190         var allowed = "0123456789";
38191         if(this.allowDecimals){
38192             allowed += this.decimalSeparator;
38193         }
38194         if(this.allowNegative){
38195             allowed += "-";
38196         }
38197         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38198         var keyPress = function(e){
38199             var k = e.getKey();
38200             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38201                 return;
38202             }
38203             var c = e.getCharCode();
38204             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38205                 e.stopEvent();
38206             }
38207         };
38208         this.el.on("keypress", keyPress, this);
38209     },
38210
38211     // private
38212     validateValue : function(value){
38213         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38214             return false;
38215         }
38216         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38217              return true;
38218         }
38219         var num = this.parseValue(value);
38220         if(isNaN(num)){
38221             this.markInvalid(String.format(this.nanText, value));
38222             return false;
38223         }
38224         if(num < this.minValue){
38225             this.markInvalid(String.format(this.minText, this.minValue));
38226             return false;
38227         }
38228         if(num > this.maxValue){
38229             this.markInvalid(String.format(this.maxText, this.maxValue));
38230             return false;
38231         }
38232         return true;
38233     },
38234
38235     getValue : function(){
38236         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38237     },
38238
38239     // private
38240     parseValue : function(value){
38241         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38242         return isNaN(value) ? '' : value;
38243     },
38244
38245     // private
38246     fixPrecision : function(value){
38247         var nan = isNaN(value);
38248         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38249             return nan ? '' : value;
38250         }
38251         return parseFloat(value).toFixed(this.decimalPrecision);
38252     },
38253
38254     setValue : function(v){
38255         v = this.fixPrecision(v);
38256         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38257     },
38258
38259     // private
38260     decimalPrecisionFcn : function(v){
38261         return Math.floor(v);
38262     },
38263
38264     beforeBlur : function(){
38265         var v = this.parseValue(this.getRawValue());
38266         if(v){
38267             this.setValue(v);
38268         }
38269     }
38270 });/*
38271  * Based on:
38272  * Ext JS Library 1.1.1
38273  * Copyright(c) 2006-2007, Ext JS, LLC.
38274  *
38275  * Originally Released Under LGPL - original licence link has changed is not relivant.
38276  *
38277  * Fork - LGPL
38278  * <script type="text/javascript">
38279  */
38280  
38281 /**
38282  * @class Roo.form.DateField
38283  * @extends Roo.form.TriggerField
38284  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38285 * @constructor
38286 * Create a new DateField
38287 * @param {Object} config
38288  */
38289 Roo.form.DateField = function(config){
38290     Roo.form.DateField.superclass.constructor.call(this, config);
38291     
38292       this.addEvents({
38293          
38294         /**
38295          * @event select
38296          * Fires when a date is selected
38297              * @param {Roo.form.DateField} combo This combo box
38298              * @param {Date} date The date selected
38299              */
38300         'select' : true
38301          
38302     });
38303     
38304     
38305     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38306     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38307     this.ddMatch = null;
38308     if(this.disabledDates){
38309         var dd = this.disabledDates;
38310         var re = "(?:";
38311         for(var i = 0; i < dd.length; i++){
38312             re += dd[i];
38313             if(i != dd.length-1) re += "|";
38314         }
38315         this.ddMatch = new RegExp(re + ")");
38316     }
38317 };
38318
38319 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38320     /**
38321      * @cfg {String} format
38322      * The default date format string which can be overriden for localization support.  The format must be
38323      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38324      */
38325     format : "m/d/y",
38326     /**
38327      * @cfg {String} altFormats
38328      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38329      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38330      */
38331     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38332     /**
38333      * @cfg {Array} disabledDays
38334      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38335      */
38336     disabledDays : null,
38337     /**
38338      * @cfg {String} disabledDaysText
38339      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38340      */
38341     disabledDaysText : "Disabled",
38342     /**
38343      * @cfg {Array} disabledDates
38344      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38345      * expression so they are very powerful. Some examples:
38346      * <ul>
38347      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38348      * <li>["03/08", "09/16"] would disable those days for every year</li>
38349      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38350      * <li>["03/../2006"] would disable every day in March 2006</li>
38351      * <li>["^03"] would disable every day in every March</li>
38352      * </ul>
38353      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38354      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38355      */
38356     disabledDates : null,
38357     /**
38358      * @cfg {String} disabledDatesText
38359      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38360      */
38361     disabledDatesText : "Disabled",
38362     /**
38363      * @cfg {Date/String} minValue
38364      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38365      * valid format (defaults to null).
38366      */
38367     minValue : null,
38368     /**
38369      * @cfg {Date/String} maxValue
38370      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38371      * valid format (defaults to null).
38372      */
38373     maxValue : null,
38374     /**
38375      * @cfg {String} minText
38376      * The error text to display when the date in the cell is before minValue (defaults to
38377      * 'The date in this field must be after {minValue}').
38378      */
38379     minText : "The date in this field must be equal to or after {0}",
38380     /**
38381      * @cfg {String} maxText
38382      * The error text to display when the date in the cell is after maxValue (defaults to
38383      * 'The date in this field must be before {maxValue}').
38384      */
38385     maxText : "The date in this field must be equal to or before {0}",
38386     /**
38387      * @cfg {String} invalidText
38388      * The error text to display when the date in the field is invalid (defaults to
38389      * '{value} is not a valid date - it must be in the format {format}').
38390      */
38391     invalidText : "{0} is not a valid date - it must be in the format {1}",
38392     /**
38393      * @cfg {String} triggerClass
38394      * An additional CSS class used to style the trigger button.  The trigger will always get the
38395      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38396      * which displays a calendar icon).
38397      */
38398     triggerClass : 'x-form-date-trigger',
38399     
38400
38401     /**
38402      * @cfg {Boolean} useIso
38403      * if enabled, then the date field will use a hidden field to store the 
38404      * real value as iso formated date. default (false)
38405      */ 
38406     useIso : false,
38407     /**
38408      * @cfg {String/Object} autoCreate
38409      * A DomHelper element spec, or true for a default element spec (defaults to
38410      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38411      */ 
38412     // private
38413     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38414     
38415     // private
38416     hiddenField: false,
38417     
38418     onRender : function(ct, position)
38419     {
38420         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38421         if (this.useIso) {
38422             //this.el.dom.removeAttribute('name'); 
38423             Roo.log("Changing name?");
38424             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38425             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38426                     'before', true);
38427             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38428             // prevent input submission
38429             this.hiddenName = this.name;
38430         }
38431             
38432             
38433     },
38434     
38435     // private
38436     validateValue : function(value)
38437     {
38438         value = this.formatDate(value);
38439         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38440             Roo.log('super failed');
38441             return false;
38442         }
38443         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38444              return true;
38445         }
38446         var svalue = value;
38447         value = this.parseDate(value);
38448         if(!value){
38449             Roo.log('parse date failed' + svalue);
38450             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38451             return false;
38452         }
38453         var time = value.getTime();
38454         if(this.minValue && time < this.minValue.getTime()){
38455             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38456             return false;
38457         }
38458         if(this.maxValue && time > this.maxValue.getTime()){
38459             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38460             return false;
38461         }
38462         if(this.disabledDays){
38463             var day = value.getDay();
38464             for(var i = 0; i < this.disabledDays.length; i++) {
38465                 if(day === this.disabledDays[i]){
38466                     this.markInvalid(this.disabledDaysText);
38467                     return false;
38468                 }
38469             }
38470         }
38471         var fvalue = this.formatDate(value);
38472         if(this.ddMatch && this.ddMatch.test(fvalue)){
38473             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38474             return false;
38475         }
38476         return true;
38477     },
38478
38479     // private
38480     // Provides logic to override the default TriggerField.validateBlur which just returns true
38481     validateBlur : function(){
38482         return !this.menu || !this.menu.isVisible();
38483     },
38484     
38485     getName: function()
38486     {
38487         // returns hidden if it's set..
38488         if (!this.rendered) {return ''};
38489         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38490         
38491     },
38492
38493     /**
38494      * Returns the current date value of the date field.
38495      * @return {Date} The date value
38496      */
38497     getValue : function(){
38498         
38499         return  this.hiddenField ?
38500                 this.hiddenField.value :
38501                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38502     },
38503
38504     /**
38505      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38506      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38507      * (the default format used is "m/d/y").
38508      * <br />Usage:
38509      * <pre><code>
38510 //All of these calls set the same date value (May 4, 2006)
38511
38512 //Pass a date object:
38513 var dt = new Date('5/4/06');
38514 dateField.setValue(dt);
38515
38516 //Pass a date string (default format):
38517 dateField.setValue('5/4/06');
38518
38519 //Pass a date string (custom format):
38520 dateField.format = 'Y-m-d';
38521 dateField.setValue('2006-5-4');
38522 </code></pre>
38523      * @param {String/Date} date The date or valid date string
38524      */
38525     setValue : function(date){
38526         if (this.hiddenField) {
38527             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38528         }
38529         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38530         // make sure the value field is always stored as a date..
38531         this.value = this.parseDate(date);
38532         
38533         
38534     },
38535
38536     // private
38537     parseDate : function(value){
38538         if(!value || value instanceof Date){
38539             return value;
38540         }
38541         var v = Date.parseDate(value, this.format);
38542          if (!v && this.useIso) {
38543             v = Date.parseDate(value, 'Y-m-d');
38544         }
38545         if(!v && this.altFormats){
38546             if(!this.altFormatsArray){
38547                 this.altFormatsArray = this.altFormats.split("|");
38548             }
38549             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38550                 v = Date.parseDate(value, this.altFormatsArray[i]);
38551             }
38552         }
38553         return v;
38554     },
38555
38556     // private
38557     formatDate : function(date, fmt){
38558         return (!date || !(date instanceof Date)) ?
38559                date : date.dateFormat(fmt || this.format);
38560     },
38561
38562     // private
38563     menuListeners : {
38564         select: function(m, d){
38565             
38566             this.setValue(d);
38567             this.fireEvent('select', this, d);
38568         },
38569         show : function(){ // retain focus styling
38570             this.onFocus();
38571         },
38572         hide : function(){
38573             this.focus.defer(10, this);
38574             var ml = this.menuListeners;
38575             this.menu.un("select", ml.select,  this);
38576             this.menu.un("show", ml.show,  this);
38577             this.menu.un("hide", ml.hide,  this);
38578         }
38579     },
38580
38581     // private
38582     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38583     onTriggerClick : function(){
38584         if(this.disabled){
38585             return;
38586         }
38587         if(this.menu == null){
38588             this.menu = new Roo.menu.DateMenu();
38589         }
38590         Roo.apply(this.menu.picker,  {
38591             showClear: this.allowBlank,
38592             minDate : this.minValue,
38593             maxDate : this.maxValue,
38594             disabledDatesRE : this.ddMatch,
38595             disabledDatesText : this.disabledDatesText,
38596             disabledDays : this.disabledDays,
38597             disabledDaysText : this.disabledDaysText,
38598             format : this.useIso ? 'Y-m-d' : this.format,
38599             minText : String.format(this.minText, this.formatDate(this.minValue)),
38600             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38601         });
38602         this.menu.on(Roo.apply({}, this.menuListeners, {
38603             scope:this
38604         }));
38605         this.menu.picker.setValue(this.getValue() || new Date());
38606         this.menu.show(this.el, "tl-bl?");
38607     },
38608
38609     beforeBlur : function(){
38610         var v = this.parseDate(this.getRawValue());
38611         if(v){
38612             this.setValue(v);
38613         }
38614     },
38615
38616     /*@
38617      * overide
38618      * 
38619      */
38620     isDirty : function() {
38621         if(this.disabled) {
38622             return false;
38623         }
38624         
38625         if(typeof(this.startValue) === 'undefined'){
38626             return false;
38627         }
38628         
38629         return String(this.getValue()) !== String(this.startValue);
38630         
38631     }
38632 });/*
38633  * Based on:
38634  * Ext JS Library 1.1.1
38635  * Copyright(c) 2006-2007, Ext JS, LLC.
38636  *
38637  * Originally Released Under LGPL - original licence link has changed is not relivant.
38638  *
38639  * Fork - LGPL
38640  * <script type="text/javascript">
38641  */
38642  
38643 /**
38644  * @class Roo.form.MonthField
38645  * @extends Roo.form.TriggerField
38646  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38647 * @constructor
38648 * Create a new MonthField
38649 * @param {Object} config
38650  */
38651 Roo.form.MonthField = function(config){
38652     
38653     Roo.form.MonthField.superclass.constructor.call(this, config);
38654     
38655       this.addEvents({
38656          
38657         /**
38658          * @event select
38659          * Fires when a date is selected
38660              * @param {Roo.form.MonthFieeld} combo This combo box
38661              * @param {Date} date The date selected
38662              */
38663         'select' : true
38664          
38665     });
38666     
38667     
38668     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38669     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38670     this.ddMatch = null;
38671     if(this.disabledDates){
38672         var dd = this.disabledDates;
38673         var re = "(?:";
38674         for(var i = 0; i < dd.length; i++){
38675             re += dd[i];
38676             if(i != dd.length-1) re += "|";
38677         }
38678         this.ddMatch = new RegExp(re + ")");
38679     }
38680 };
38681
38682 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38683     /**
38684      * @cfg {String} format
38685      * The default date format string which can be overriden for localization support.  The format must be
38686      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38687      */
38688     format : "M Y",
38689     /**
38690      * @cfg {String} altFormats
38691      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38692      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38693      */
38694     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38695     /**
38696      * @cfg {Array} disabledDays
38697      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38698      */
38699     disabledDays : [0,1,2,3,4,5,6],
38700     /**
38701      * @cfg {String} disabledDaysText
38702      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38703      */
38704     disabledDaysText : "Disabled",
38705     /**
38706      * @cfg {Array} disabledDates
38707      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38708      * expression so they are very powerful. Some examples:
38709      * <ul>
38710      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38711      * <li>["03/08", "09/16"] would disable those days for every year</li>
38712      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38713      * <li>["03/../2006"] would disable every day in March 2006</li>
38714      * <li>["^03"] would disable every day in every March</li>
38715      * </ul>
38716      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38717      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38718      */
38719     disabledDates : null,
38720     /**
38721      * @cfg {String} disabledDatesText
38722      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38723      */
38724     disabledDatesText : "Disabled",
38725     /**
38726      * @cfg {Date/String} minValue
38727      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38728      * valid format (defaults to null).
38729      */
38730     minValue : null,
38731     /**
38732      * @cfg {Date/String} maxValue
38733      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38734      * valid format (defaults to null).
38735      */
38736     maxValue : null,
38737     /**
38738      * @cfg {String} minText
38739      * The error text to display when the date in the cell is before minValue (defaults to
38740      * 'The date in this field must be after {minValue}').
38741      */
38742     minText : "The date in this field must be equal to or after {0}",
38743     /**
38744      * @cfg {String} maxTextf
38745      * The error text to display when the date in the cell is after maxValue (defaults to
38746      * 'The date in this field must be before {maxValue}').
38747      */
38748     maxText : "The date in this field must be equal to or before {0}",
38749     /**
38750      * @cfg {String} invalidText
38751      * The error text to display when the date in the field is invalid (defaults to
38752      * '{value} is not a valid date - it must be in the format {format}').
38753      */
38754     invalidText : "{0} is not a valid date - it must be in the format {1}",
38755     /**
38756      * @cfg {String} triggerClass
38757      * An additional CSS class used to style the trigger button.  The trigger will always get the
38758      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38759      * which displays a calendar icon).
38760      */
38761     triggerClass : 'x-form-date-trigger',
38762     
38763
38764     /**
38765      * @cfg {Boolean} useIso
38766      * if enabled, then the date field will use a hidden field to store the 
38767      * real value as iso formated date. default (true)
38768      */ 
38769     useIso : true,
38770     /**
38771      * @cfg {String/Object} autoCreate
38772      * A DomHelper element spec, or true for a default element spec (defaults to
38773      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38774      */ 
38775     // private
38776     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38777     
38778     // private
38779     hiddenField: false,
38780     
38781     hideMonthPicker : false,
38782     
38783     onRender : function(ct, position)
38784     {
38785         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38786         if (this.useIso) {
38787             this.el.dom.removeAttribute('name'); 
38788             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38789                     'before', true);
38790             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38791             // prevent input submission
38792             this.hiddenName = this.name;
38793         }
38794             
38795             
38796     },
38797     
38798     // private
38799     validateValue : function(value)
38800     {
38801         value = this.formatDate(value);
38802         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38803             return false;
38804         }
38805         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38806              return true;
38807         }
38808         var svalue = value;
38809         value = this.parseDate(value);
38810         if(!value){
38811             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38812             return false;
38813         }
38814         var time = value.getTime();
38815         if(this.minValue && time < this.minValue.getTime()){
38816             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38817             return false;
38818         }
38819         if(this.maxValue && time > this.maxValue.getTime()){
38820             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38821             return false;
38822         }
38823         /*if(this.disabledDays){
38824             var day = value.getDay();
38825             for(var i = 0; i < this.disabledDays.length; i++) {
38826                 if(day === this.disabledDays[i]){
38827                     this.markInvalid(this.disabledDaysText);
38828                     return false;
38829                 }
38830             }
38831         }
38832         */
38833         var fvalue = this.formatDate(value);
38834         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38835             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38836             return false;
38837         }
38838         */
38839         return true;
38840     },
38841
38842     // private
38843     // Provides logic to override the default TriggerField.validateBlur which just returns true
38844     validateBlur : function(){
38845         return !this.menu || !this.menu.isVisible();
38846     },
38847
38848     /**
38849      * Returns the current date value of the date field.
38850      * @return {Date} The date value
38851      */
38852     getValue : function(){
38853         
38854         
38855         
38856         return  this.hiddenField ?
38857                 this.hiddenField.value :
38858                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38859     },
38860
38861     /**
38862      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38863      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38864      * (the default format used is "m/d/y").
38865      * <br />Usage:
38866      * <pre><code>
38867 //All of these calls set the same date value (May 4, 2006)
38868
38869 //Pass a date object:
38870 var dt = new Date('5/4/06');
38871 monthField.setValue(dt);
38872
38873 //Pass a date string (default format):
38874 monthField.setValue('5/4/06');
38875
38876 //Pass a date string (custom format):
38877 monthField.format = 'Y-m-d';
38878 monthField.setValue('2006-5-4');
38879 </code></pre>
38880      * @param {String/Date} date The date or valid date string
38881      */
38882     setValue : function(date){
38883         Roo.log('month setValue' + date);
38884         // can only be first of month..
38885         
38886         var val = this.parseDate(date);
38887         
38888         if (this.hiddenField) {
38889             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38890         }
38891         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38892         this.value = this.parseDate(date);
38893     },
38894
38895     // private
38896     parseDate : function(value){
38897         if(!value || value instanceof Date){
38898             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38899             return value;
38900         }
38901         var v = Date.parseDate(value, this.format);
38902         if (!v && this.useIso) {
38903             v = Date.parseDate(value, 'Y-m-d');
38904         }
38905         if (v) {
38906             // 
38907             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38908         }
38909         
38910         
38911         if(!v && this.altFormats){
38912             if(!this.altFormatsArray){
38913                 this.altFormatsArray = this.altFormats.split("|");
38914             }
38915             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38916                 v = Date.parseDate(value, this.altFormatsArray[i]);
38917             }
38918         }
38919         return v;
38920     },
38921
38922     // private
38923     formatDate : function(date, fmt){
38924         return (!date || !(date instanceof Date)) ?
38925                date : date.dateFormat(fmt || this.format);
38926     },
38927
38928     // private
38929     menuListeners : {
38930         select: function(m, d){
38931             this.setValue(d);
38932             this.fireEvent('select', this, d);
38933         },
38934         show : function(){ // retain focus styling
38935             this.onFocus();
38936         },
38937         hide : function(){
38938             this.focus.defer(10, this);
38939             var ml = this.menuListeners;
38940             this.menu.un("select", ml.select,  this);
38941             this.menu.un("show", ml.show,  this);
38942             this.menu.un("hide", ml.hide,  this);
38943         }
38944     },
38945     // private
38946     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38947     onTriggerClick : function(){
38948         if(this.disabled){
38949             return;
38950         }
38951         if(this.menu == null){
38952             this.menu = new Roo.menu.DateMenu();
38953            
38954         }
38955         
38956         Roo.apply(this.menu.picker,  {
38957             
38958             showClear: this.allowBlank,
38959             minDate : this.minValue,
38960             maxDate : this.maxValue,
38961             disabledDatesRE : this.ddMatch,
38962             disabledDatesText : this.disabledDatesText,
38963             
38964             format : this.useIso ? 'Y-m-d' : this.format,
38965             minText : String.format(this.minText, this.formatDate(this.minValue)),
38966             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38967             
38968         });
38969          this.menu.on(Roo.apply({}, this.menuListeners, {
38970             scope:this
38971         }));
38972        
38973         
38974         var m = this.menu;
38975         var p = m.picker;
38976         
38977         // hide month picker get's called when we called by 'before hide';
38978         
38979         var ignorehide = true;
38980         p.hideMonthPicker  = function(disableAnim){
38981             if (ignorehide) {
38982                 return;
38983             }
38984              if(this.monthPicker){
38985                 Roo.log("hideMonthPicker called");
38986                 if(disableAnim === true){
38987                     this.monthPicker.hide();
38988                 }else{
38989                     this.monthPicker.slideOut('t', {duration:.2});
38990                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38991                     p.fireEvent("select", this, this.value);
38992                     m.hide();
38993                 }
38994             }
38995         }
38996         
38997         Roo.log('picker set value');
38998         Roo.log(this.getValue());
38999         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39000         m.show(this.el, 'tl-bl?');
39001         ignorehide  = false;
39002         // this will trigger hideMonthPicker..
39003         
39004         
39005         // hidden the day picker
39006         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39007         
39008         
39009         
39010       
39011         
39012         p.showMonthPicker.defer(100, p);
39013     
39014         
39015        
39016     },
39017
39018     beforeBlur : function(){
39019         var v = this.parseDate(this.getRawValue());
39020         if(v){
39021             this.setValue(v);
39022         }
39023     }
39024
39025     /** @cfg {Boolean} grow @hide */
39026     /** @cfg {Number} growMin @hide */
39027     /** @cfg {Number} growMax @hide */
39028     /**
39029      * @hide
39030      * @method autoSize
39031      */
39032 });/*
39033  * Based on:
39034  * Ext JS Library 1.1.1
39035  * Copyright(c) 2006-2007, Ext JS, LLC.
39036  *
39037  * Originally Released Under LGPL - original licence link has changed is not relivant.
39038  *
39039  * Fork - LGPL
39040  * <script type="text/javascript">
39041  */
39042  
39043
39044 /**
39045  * @class Roo.form.ComboBox
39046  * @extends Roo.form.TriggerField
39047  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39048  * @constructor
39049  * Create a new ComboBox.
39050  * @param {Object} config Configuration options
39051  */
39052 Roo.form.ComboBox = function(config){
39053     Roo.form.ComboBox.superclass.constructor.call(this, config);
39054     this.addEvents({
39055         /**
39056          * @event expand
39057          * Fires when the dropdown list is expanded
39058              * @param {Roo.form.ComboBox} combo This combo box
39059              */
39060         'expand' : true,
39061         /**
39062          * @event collapse
39063          * Fires when the dropdown list is collapsed
39064              * @param {Roo.form.ComboBox} combo This combo box
39065              */
39066         'collapse' : true,
39067         /**
39068          * @event beforeselect
39069          * Fires before a list item is selected. Return false to cancel the selection.
39070              * @param {Roo.form.ComboBox} combo This combo box
39071              * @param {Roo.data.Record} record The data record returned from the underlying store
39072              * @param {Number} index The index of the selected item in the dropdown list
39073              */
39074         'beforeselect' : true,
39075         /**
39076          * @event select
39077          * Fires when a list item is selected
39078              * @param {Roo.form.ComboBox} combo This combo box
39079              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39080              * @param {Number} index The index of the selected item in the dropdown list
39081              */
39082         'select' : true,
39083         /**
39084          * @event beforequery
39085          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39086          * The event object passed has these properties:
39087              * @param {Roo.form.ComboBox} combo This combo box
39088              * @param {String} query The query
39089              * @param {Boolean} forceAll true to force "all" query
39090              * @param {Boolean} cancel true to cancel the query
39091              * @param {Object} e The query event object
39092              */
39093         'beforequery': true,
39094          /**
39095          * @event add
39096          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39097              * @param {Roo.form.ComboBox} combo This combo box
39098              */
39099         'add' : true,
39100         /**
39101          * @event edit
39102          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39103              * @param {Roo.form.ComboBox} combo This combo box
39104              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39105              */
39106         'edit' : true
39107         
39108         
39109     });
39110     if(this.transform){
39111         this.allowDomMove = false;
39112         var s = Roo.getDom(this.transform);
39113         if(!this.hiddenName){
39114             this.hiddenName = s.name;
39115         }
39116         if(!this.store){
39117             this.mode = 'local';
39118             var d = [], opts = s.options;
39119             for(var i = 0, len = opts.length;i < len; i++){
39120                 var o = opts[i];
39121                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39122                 if(o.selected) {
39123                     this.value = value;
39124                 }
39125                 d.push([value, o.text]);
39126             }
39127             this.store = new Roo.data.SimpleStore({
39128                 'id': 0,
39129                 fields: ['value', 'text'],
39130                 data : d
39131             });
39132             this.valueField = 'value';
39133             this.displayField = 'text';
39134         }
39135         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39136         if(!this.lazyRender){
39137             this.target = true;
39138             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39139             s.parentNode.removeChild(s); // remove it
39140             this.render(this.el.parentNode);
39141         }else{
39142             s.parentNode.removeChild(s); // remove it
39143         }
39144
39145     }
39146     if (this.store) {
39147         this.store = Roo.factory(this.store, Roo.data);
39148     }
39149     
39150     this.selectedIndex = -1;
39151     if(this.mode == 'local'){
39152         if(config.queryDelay === undefined){
39153             this.queryDelay = 10;
39154         }
39155         if(config.minChars === undefined){
39156             this.minChars = 0;
39157         }
39158     }
39159 };
39160
39161 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39162     /**
39163      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39164      */
39165     /**
39166      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39167      * rendering into an Roo.Editor, defaults to false)
39168      */
39169     /**
39170      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39171      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39172      */
39173     /**
39174      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39175      */
39176     /**
39177      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39178      * the dropdown list (defaults to undefined, with no header element)
39179      */
39180
39181      /**
39182      * @cfg {String/Roo.Template} tpl The template to use to render the output
39183      */
39184      
39185     // private
39186     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39187     /**
39188      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39189      */
39190     listWidth: undefined,
39191     /**
39192      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39193      * mode = 'remote' or 'text' if mode = 'local')
39194      */
39195     displayField: undefined,
39196     /**
39197      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39198      * mode = 'remote' or 'value' if mode = 'local'). 
39199      * Note: use of a valueField requires the user make a selection
39200      * in order for a value to be mapped.
39201      */
39202     valueField: undefined,
39203     
39204     
39205     /**
39206      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39207      * field's data value (defaults to the underlying DOM element's name)
39208      */
39209     hiddenName: undefined,
39210     /**
39211      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39212      */
39213     listClass: '',
39214     /**
39215      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39216      */
39217     selectedClass: 'x-combo-selected',
39218     /**
39219      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39220      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39221      * which displays a downward arrow icon).
39222      */
39223     triggerClass : 'x-form-arrow-trigger',
39224     /**
39225      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39226      */
39227     shadow:'sides',
39228     /**
39229      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39230      * anchor positions (defaults to 'tl-bl')
39231      */
39232     listAlign: 'tl-bl?',
39233     /**
39234      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39235      */
39236     maxHeight: 300,
39237     /**
39238      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39239      * query specified by the allQuery config option (defaults to 'query')
39240      */
39241     triggerAction: 'query',
39242     /**
39243      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39244      * (defaults to 4, does not apply if editable = false)
39245      */
39246     minChars : 4,
39247     /**
39248      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39249      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39250      */
39251     typeAhead: false,
39252     /**
39253      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39254      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39255      */
39256     queryDelay: 500,
39257     /**
39258      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39259      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39260      */
39261     pageSize: 0,
39262     /**
39263      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39264      * when editable = true (defaults to false)
39265      */
39266     selectOnFocus:false,
39267     /**
39268      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39269      */
39270     queryParam: 'query',
39271     /**
39272      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39273      * when mode = 'remote' (defaults to 'Loading...')
39274      */
39275     loadingText: 'Loading...',
39276     /**
39277      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39278      */
39279     resizable: false,
39280     /**
39281      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39282      */
39283     handleHeight : 8,
39284     /**
39285      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39286      * traditional select (defaults to true)
39287      */
39288     editable: true,
39289     /**
39290      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39291      */
39292     allQuery: '',
39293     /**
39294      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39295      */
39296     mode: 'remote',
39297     /**
39298      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39299      * listWidth has a higher value)
39300      */
39301     minListWidth : 70,
39302     /**
39303      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39304      * allow the user to set arbitrary text into the field (defaults to false)
39305      */
39306     forceSelection:false,
39307     /**
39308      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39309      * if typeAhead = true (defaults to 250)
39310      */
39311     typeAheadDelay : 250,
39312     /**
39313      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39314      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39315      */
39316     valueNotFoundText : undefined,
39317     /**
39318      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39319      */
39320     blockFocus : false,
39321     
39322     /**
39323      * @cfg {Boolean} disableClear Disable showing of clear button.
39324      */
39325     disableClear : false,
39326     /**
39327      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39328      */
39329     alwaysQuery : false,
39330     
39331     //private
39332     addicon : false,
39333     editicon: false,
39334     
39335     // element that contains real text value.. (when hidden is used..)
39336      
39337     // private
39338     onRender : function(ct, position){
39339         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39340         if(this.hiddenName){
39341             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39342                     'before', true);
39343             this.hiddenField.value =
39344                 this.hiddenValue !== undefined ? this.hiddenValue :
39345                 this.value !== undefined ? this.value : '';
39346
39347             // prevent input submission
39348             this.el.dom.removeAttribute('name');
39349              
39350              
39351         }
39352         if(Roo.isGecko){
39353             this.el.dom.setAttribute('autocomplete', 'off');
39354         }
39355
39356         var cls = 'x-combo-list';
39357
39358         this.list = new Roo.Layer({
39359             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39360         });
39361
39362         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39363         this.list.setWidth(lw);
39364         this.list.swallowEvent('mousewheel');
39365         this.assetHeight = 0;
39366
39367         if(this.title){
39368             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39369             this.assetHeight += this.header.getHeight();
39370         }
39371
39372         this.innerList = this.list.createChild({cls:cls+'-inner'});
39373         this.innerList.on('mouseover', this.onViewOver, this);
39374         this.innerList.on('mousemove', this.onViewMove, this);
39375         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39376         
39377         if(this.allowBlank && !this.pageSize && !this.disableClear){
39378             this.footer = this.list.createChild({cls:cls+'-ft'});
39379             this.pageTb = new Roo.Toolbar(this.footer);
39380            
39381         }
39382         if(this.pageSize){
39383             this.footer = this.list.createChild({cls:cls+'-ft'});
39384             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39385                     {pageSize: this.pageSize});
39386             
39387         }
39388         
39389         if (this.pageTb && this.allowBlank && !this.disableClear) {
39390             var _this = this;
39391             this.pageTb.add(new Roo.Toolbar.Fill(), {
39392                 cls: 'x-btn-icon x-btn-clear',
39393                 text: '&#160;',
39394                 handler: function()
39395                 {
39396                     _this.collapse();
39397                     _this.clearValue();
39398                     _this.onSelect(false, -1);
39399                 }
39400             });
39401         }
39402         if (this.footer) {
39403             this.assetHeight += this.footer.getHeight();
39404         }
39405         
39406
39407         if(!this.tpl){
39408             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39409         }
39410
39411         this.view = new Roo.View(this.innerList, this.tpl, {
39412             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39413         });
39414
39415         this.view.on('click', this.onViewClick, this);
39416
39417         this.store.on('beforeload', this.onBeforeLoad, this);
39418         this.store.on('load', this.onLoad, this);
39419         this.store.on('loadexception', this.onLoadException, this);
39420
39421         if(this.resizable){
39422             this.resizer = new Roo.Resizable(this.list,  {
39423                pinned:true, handles:'se'
39424             });
39425             this.resizer.on('resize', function(r, w, h){
39426                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39427                 this.listWidth = w;
39428                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39429                 this.restrictHeight();
39430             }, this);
39431             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39432         }
39433         if(!this.editable){
39434             this.editable = true;
39435             this.setEditable(false);
39436         }  
39437         
39438         
39439         if (typeof(this.events.add.listeners) != 'undefined') {
39440             
39441             this.addicon = this.wrap.createChild(
39442                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39443        
39444             this.addicon.on('click', function(e) {
39445                 this.fireEvent('add', this);
39446             }, this);
39447         }
39448         if (typeof(this.events.edit.listeners) != 'undefined') {
39449             
39450             this.editicon = this.wrap.createChild(
39451                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39452             if (this.addicon) {
39453                 this.editicon.setStyle('margin-left', '40px');
39454             }
39455             this.editicon.on('click', function(e) {
39456                 
39457                 // we fire even  if inothing is selected..
39458                 this.fireEvent('edit', this, this.lastData );
39459                 
39460             }, this);
39461         }
39462         
39463         
39464         
39465     },
39466
39467     // private
39468     initEvents : function(){
39469         Roo.form.ComboBox.superclass.initEvents.call(this);
39470
39471         this.keyNav = new Roo.KeyNav(this.el, {
39472             "up" : function(e){
39473                 this.inKeyMode = true;
39474                 this.selectPrev();
39475             },
39476
39477             "down" : function(e){
39478                 if(!this.isExpanded()){
39479                     this.onTriggerClick();
39480                 }else{
39481                     this.inKeyMode = true;
39482                     this.selectNext();
39483                 }
39484             },
39485
39486             "enter" : function(e){
39487                 this.onViewClick();
39488                 //return true;
39489             },
39490
39491             "esc" : function(e){
39492                 this.collapse();
39493             },
39494
39495             "tab" : function(e){
39496                 this.onViewClick(false);
39497                 this.fireEvent("specialkey", this, e);
39498                 return true;
39499             },
39500
39501             scope : this,
39502
39503             doRelay : function(foo, bar, hname){
39504                 if(hname == 'down' || this.scope.isExpanded()){
39505                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39506                 }
39507                 return true;
39508             },
39509
39510             forceKeyDown: true
39511         });
39512         this.queryDelay = Math.max(this.queryDelay || 10,
39513                 this.mode == 'local' ? 10 : 250);
39514         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39515         if(this.typeAhead){
39516             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39517         }
39518         if(this.editable !== false){
39519             this.el.on("keyup", this.onKeyUp, this);
39520         }
39521         if(this.forceSelection){
39522             this.on('blur', this.doForce, this);
39523         }
39524     },
39525
39526     onDestroy : function(){
39527         if(this.view){
39528             this.view.setStore(null);
39529             this.view.el.removeAllListeners();
39530             this.view.el.remove();
39531             this.view.purgeListeners();
39532         }
39533         if(this.list){
39534             this.list.destroy();
39535         }
39536         if(this.store){
39537             this.store.un('beforeload', this.onBeforeLoad, this);
39538             this.store.un('load', this.onLoad, this);
39539             this.store.un('loadexception', this.onLoadException, this);
39540         }
39541         Roo.form.ComboBox.superclass.onDestroy.call(this);
39542     },
39543
39544     // private
39545     fireKey : function(e){
39546         if(e.isNavKeyPress() && !this.list.isVisible()){
39547             this.fireEvent("specialkey", this, e);
39548         }
39549     },
39550
39551     // private
39552     onResize: function(w, h){
39553         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39554         
39555         if(typeof w != 'number'){
39556             // we do not handle it!?!?
39557             return;
39558         }
39559         var tw = this.trigger.getWidth();
39560         tw += this.addicon ? this.addicon.getWidth() : 0;
39561         tw += this.editicon ? this.editicon.getWidth() : 0;
39562         var x = w - tw;
39563         this.el.setWidth( this.adjustWidth('input', x));
39564             
39565         this.trigger.setStyle('left', x+'px');
39566         
39567         if(this.list && this.listWidth === undefined){
39568             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39569             this.list.setWidth(lw);
39570             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39571         }
39572         
39573     
39574         
39575     },
39576
39577     /**
39578      * Allow or prevent the user from directly editing the field text.  If false is passed,
39579      * the user will only be able to select from the items defined in the dropdown list.  This method
39580      * is the runtime equivalent of setting the 'editable' config option at config time.
39581      * @param {Boolean} value True to allow the user to directly edit the field text
39582      */
39583     setEditable : function(value){
39584         if(value == this.editable){
39585             return;
39586         }
39587         this.editable = value;
39588         if(!value){
39589             this.el.dom.setAttribute('readOnly', true);
39590             this.el.on('mousedown', this.onTriggerClick,  this);
39591             this.el.addClass('x-combo-noedit');
39592         }else{
39593             this.el.dom.setAttribute('readOnly', false);
39594             this.el.un('mousedown', this.onTriggerClick,  this);
39595             this.el.removeClass('x-combo-noedit');
39596         }
39597     },
39598
39599     // private
39600     onBeforeLoad : function(){
39601         if(!this.hasFocus){
39602             return;
39603         }
39604         this.innerList.update(this.loadingText ?
39605                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39606         this.restrictHeight();
39607         this.selectedIndex = -1;
39608     },
39609
39610     // private
39611     onLoad : function(){
39612         if(!this.hasFocus){
39613             return;
39614         }
39615         if(this.store.getCount() > 0){
39616             this.expand();
39617             this.restrictHeight();
39618             if(this.lastQuery == this.allQuery){
39619                 if(this.editable){
39620                     this.el.dom.select();
39621                 }
39622                 if(!this.selectByValue(this.value, true)){
39623                     this.select(0, true);
39624                 }
39625             }else{
39626                 this.selectNext();
39627                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39628                     this.taTask.delay(this.typeAheadDelay);
39629                 }
39630             }
39631         }else{
39632             this.onEmptyResults();
39633         }
39634         //this.el.focus();
39635     },
39636     // private
39637     onLoadException : function()
39638     {
39639         this.collapse();
39640         Roo.log(this.store.reader.jsonData);
39641         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39642             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39643         }
39644         
39645         
39646     },
39647     // private
39648     onTypeAhead : function(){
39649         if(this.store.getCount() > 0){
39650             var r = this.store.getAt(0);
39651             var newValue = r.data[this.displayField];
39652             var len = newValue.length;
39653             var selStart = this.getRawValue().length;
39654             if(selStart != len){
39655                 this.setRawValue(newValue);
39656                 this.selectText(selStart, newValue.length);
39657             }
39658         }
39659     },
39660
39661     // private
39662     onSelect : function(record, index){
39663         if(this.fireEvent('beforeselect', this, record, index) !== false){
39664             this.setFromData(index > -1 ? record.data : false);
39665             this.collapse();
39666             this.fireEvent('select', this, record, index);
39667         }
39668     },
39669
39670     /**
39671      * Returns the currently selected field value or empty string if no value is set.
39672      * @return {String} value The selected value
39673      */
39674     getValue : function(){
39675         if(this.valueField){
39676             return typeof this.value != 'undefined' ? this.value : '';
39677         }else{
39678             return Roo.form.ComboBox.superclass.getValue.call(this);
39679         }
39680     },
39681
39682     /**
39683      * Clears any text/value currently set in the field
39684      */
39685     clearValue : function(){
39686         if(this.hiddenField){
39687             this.hiddenField.value = '';
39688         }
39689         this.value = '';
39690         this.setRawValue('');
39691         this.lastSelectionText = '';
39692         
39693     },
39694
39695     /**
39696      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39697      * will be displayed in the field.  If the value does not match the data value of an existing item,
39698      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39699      * Otherwise the field will be blank (although the value will still be set).
39700      * @param {String} value The value to match
39701      */
39702     setValue : function(v){
39703         var text = v;
39704         if(this.valueField){
39705             var r = this.findRecord(this.valueField, v);
39706             if(r){
39707                 text = r.data[this.displayField];
39708             }else if(this.valueNotFoundText !== undefined){
39709                 text = this.valueNotFoundText;
39710             }
39711         }
39712         this.lastSelectionText = text;
39713         if(this.hiddenField){
39714             this.hiddenField.value = v;
39715         }
39716         Roo.form.ComboBox.superclass.setValue.call(this, text);
39717         this.value = v;
39718     },
39719     /**
39720      * @property {Object} the last set data for the element
39721      */
39722     
39723     lastData : false,
39724     /**
39725      * Sets the value of the field based on a object which is related to the record format for the store.
39726      * @param {Object} value the value to set as. or false on reset?
39727      */
39728     setFromData : function(o){
39729         var dv = ''; // display value
39730         var vv = ''; // value value..
39731         this.lastData = o;
39732         if (this.displayField) {
39733             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39734         } else {
39735             // this is an error condition!!!
39736             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39737         }
39738         
39739         if(this.valueField){
39740             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39741         }
39742         if(this.hiddenField){
39743             this.hiddenField.value = vv;
39744             
39745             this.lastSelectionText = dv;
39746             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39747             this.value = vv;
39748             return;
39749         }
39750         // no hidden field.. - we store the value in 'value', but still display
39751         // display field!!!!
39752         this.lastSelectionText = dv;
39753         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39754         this.value = vv;
39755         
39756         
39757     },
39758     // private
39759     reset : function(){
39760         // overridden so that last data is reset..
39761         this.setValue(this.resetValue);
39762         this.clearInvalid();
39763         this.lastData = false;
39764         if (this.view) {
39765             this.view.clearSelections();
39766         }
39767     },
39768     // private
39769     findRecord : function(prop, value){
39770         var record;
39771         if(this.store.getCount() > 0){
39772             this.store.each(function(r){
39773                 if(r.data[prop] == value){
39774                     record = r;
39775                     return false;
39776                 }
39777                 return true;
39778             });
39779         }
39780         return record;
39781     },
39782     
39783     getName: function()
39784     {
39785         // returns hidden if it's set..
39786         if (!this.rendered) {return ''};
39787         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39788         
39789     },
39790     // private
39791     onViewMove : function(e, t){
39792         this.inKeyMode = false;
39793     },
39794
39795     // private
39796     onViewOver : function(e, t){
39797         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39798             return;
39799         }
39800         var item = this.view.findItemFromChild(t);
39801         if(item){
39802             var index = this.view.indexOf(item);
39803             this.select(index, false);
39804         }
39805     },
39806
39807     // private
39808     onViewClick : function(doFocus)
39809     {
39810         var index = this.view.getSelectedIndexes()[0];
39811         var r = this.store.getAt(index);
39812         if(r){
39813             this.onSelect(r, index);
39814         }
39815         if(doFocus !== false && !this.blockFocus){
39816             this.el.focus();
39817         }
39818     },
39819
39820     // private
39821     restrictHeight : function(){
39822         this.innerList.dom.style.height = '';
39823         var inner = this.innerList.dom;
39824         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39825         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39826         this.list.beginUpdate();
39827         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39828         this.list.alignTo(this.el, this.listAlign);
39829         this.list.endUpdate();
39830     },
39831
39832     // private
39833     onEmptyResults : function(){
39834         this.collapse();
39835     },
39836
39837     /**
39838      * Returns true if the dropdown list is expanded, else false.
39839      */
39840     isExpanded : function(){
39841         return this.list.isVisible();
39842     },
39843
39844     /**
39845      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39846      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39847      * @param {String} value The data value of the item to select
39848      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39849      * selected item if it is not currently in view (defaults to true)
39850      * @return {Boolean} True if the value matched an item in the list, else false
39851      */
39852     selectByValue : function(v, scrollIntoView){
39853         if(v !== undefined && v !== null){
39854             var r = this.findRecord(this.valueField || this.displayField, v);
39855             if(r){
39856                 this.select(this.store.indexOf(r), scrollIntoView);
39857                 return true;
39858             }
39859         }
39860         return false;
39861     },
39862
39863     /**
39864      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39865      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39866      * @param {Number} index The zero-based index of the list item to select
39867      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39868      * selected item if it is not currently in view (defaults to true)
39869      */
39870     select : function(index, scrollIntoView){
39871         this.selectedIndex = index;
39872         this.view.select(index);
39873         if(scrollIntoView !== false){
39874             var el = this.view.getNode(index);
39875             if(el){
39876                 this.innerList.scrollChildIntoView(el, false);
39877             }
39878         }
39879     },
39880
39881     // private
39882     selectNext : function(){
39883         var ct = this.store.getCount();
39884         if(ct > 0){
39885             if(this.selectedIndex == -1){
39886                 this.select(0);
39887             }else if(this.selectedIndex < ct-1){
39888                 this.select(this.selectedIndex+1);
39889             }
39890         }
39891     },
39892
39893     // private
39894     selectPrev : function(){
39895         var ct = this.store.getCount();
39896         if(ct > 0){
39897             if(this.selectedIndex == -1){
39898                 this.select(0);
39899             }else if(this.selectedIndex != 0){
39900                 this.select(this.selectedIndex-1);
39901             }
39902         }
39903     },
39904
39905     // private
39906     onKeyUp : function(e){
39907         if(this.editable !== false && !e.isSpecialKey()){
39908             this.lastKey = e.getKey();
39909             this.dqTask.delay(this.queryDelay);
39910         }
39911     },
39912
39913     // private
39914     validateBlur : function(){
39915         return !this.list || !this.list.isVisible();   
39916     },
39917
39918     // private
39919     initQuery : function(){
39920         this.doQuery(this.getRawValue());
39921     },
39922
39923     // private
39924     doForce : function(){
39925         if(this.el.dom.value.length > 0){
39926             this.el.dom.value =
39927                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39928              
39929         }
39930     },
39931
39932     /**
39933      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39934      * query allowing the query action to be canceled if needed.
39935      * @param {String} query The SQL query to execute
39936      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39937      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39938      * saved in the current store (defaults to false)
39939      */
39940     doQuery : function(q, forceAll){
39941         if(q === undefined || q === null){
39942             q = '';
39943         }
39944         var qe = {
39945             query: q,
39946             forceAll: forceAll,
39947             combo: this,
39948             cancel:false
39949         };
39950         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39951             return false;
39952         }
39953         q = qe.query;
39954         forceAll = qe.forceAll;
39955         if(forceAll === true || (q.length >= this.minChars)){
39956             if(this.lastQuery != q || this.alwaysQuery){
39957                 this.lastQuery = q;
39958                 if(this.mode == 'local'){
39959                     this.selectedIndex = -1;
39960                     if(forceAll){
39961                         this.store.clearFilter();
39962                     }else{
39963                         this.store.filter(this.displayField, q);
39964                     }
39965                     this.onLoad();
39966                 }else{
39967                     this.store.baseParams[this.queryParam] = q;
39968                     this.store.load({
39969                         params: this.getParams(q)
39970                     });
39971                     this.expand();
39972                 }
39973             }else{
39974                 this.selectedIndex = -1;
39975                 this.onLoad();   
39976             }
39977         }
39978     },
39979
39980     // private
39981     getParams : function(q){
39982         var p = {};
39983         //p[this.queryParam] = q;
39984         if(this.pageSize){
39985             p.start = 0;
39986             p.limit = this.pageSize;
39987         }
39988         return p;
39989     },
39990
39991     /**
39992      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39993      */
39994     collapse : function(){
39995         if(!this.isExpanded()){
39996             return;
39997         }
39998         this.list.hide();
39999         Roo.get(document).un('mousedown', this.collapseIf, this);
40000         Roo.get(document).un('mousewheel', this.collapseIf, this);
40001         if (!this.editable) {
40002             Roo.get(document).un('keydown', this.listKeyPress, this);
40003         }
40004         this.fireEvent('collapse', this);
40005     },
40006
40007     // private
40008     collapseIf : function(e){
40009         if(!e.within(this.wrap) && !e.within(this.list)){
40010             this.collapse();
40011         }
40012     },
40013
40014     /**
40015      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40016      */
40017     expand : function(){
40018         if(this.isExpanded() || !this.hasFocus){
40019             return;
40020         }
40021         this.list.alignTo(this.el, this.listAlign);
40022         this.list.show();
40023         Roo.get(document).on('mousedown', this.collapseIf, this);
40024         Roo.get(document).on('mousewheel', this.collapseIf, this);
40025         if (!this.editable) {
40026             Roo.get(document).on('keydown', this.listKeyPress, this);
40027         }
40028         
40029         this.fireEvent('expand', this);
40030     },
40031
40032     // private
40033     // Implements the default empty TriggerField.onTriggerClick function
40034     onTriggerClick : function(){
40035         if(this.disabled){
40036             return;
40037         }
40038         if(this.isExpanded()){
40039             this.collapse();
40040             if (!this.blockFocus) {
40041                 this.el.focus();
40042             }
40043             
40044         }else {
40045             this.hasFocus = true;
40046             if(this.triggerAction == 'all') {
40047                 this.doQuery(this.allQuery, true);
40048             } else {
40049                 this.doQuery(this.getRawValue());
40050             }
40051             if (!this.blockFocus) {
40052                 this.el.focus();
40053             }
40054         }
40055     },
40056     listKeyPress : function(e)
40057     {
40058         //Roo.log('listkeypress');
40059         // scroll to first matching element based on key pres..
40060         if (e.isSpecialKey()) {
40061             return false;
40062         }
40063         var k = String.fromCharCode(e.getKey()).toUpperCase();
40064         //Roo.log(k);
40065         var match  = false;
40066         var csel = this.view.getSelectedNodes();
40067         var cselitem = false;
40068         if (csel.length) {
40069             var ix = this.view.indexOf(csel[0]);
40070             cselitem  = this.store.getAt(ix);
40071             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40072                 cselitem = false;
40073             }
40074             
40075         }
40076         
40077         this.store.each(function(v) { 
40078             if (cselitem) {
40079                 // start at existing selection.
40080                 if (cselitem.id == v.id) {
40081                     cselitem = false;
40082                 }
40083                 return;
40084             }
40085                 
40086             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40087                 match = this.store.indexOf(v);
40088                 return false;
40089             }
40090         }, this);
40091         
40092         if (match === false) {
40093             return true; // no more action?
40094         }
40095         // scroll to?
40096         this.view.select(match);
40097         var sn = Roo.get(this.view.getSelectedNodes()[0])
40098         sn.scrollIntoView(sn.dom.parentNode, false);
40099     }
40100
40101     /** 
40102     * @cfg {Boolean} grow 
40103     * @hide 
40104     */
40105     /** 
40106     * @cfg {Number} growMin 
40107     * @hide 
40108     */
40109     /** 
40110     * @cfg {Number} growMax 
40111     * @hide 
40112     */
40113     /**
40114      * @hide
40115      * @method autoSize
40116      */
40117 });/*
40118  * Copyright(c) 2010-2012, Roo J Solutions Limited
40119  *
40120  * Licence LGPL
40121  *
40122  */
40123
40124 /**
40125  * @class Roo.form.ComboBoxArray
40126  * @extends Roo.form.TextField
40127  * A facebook style adder... for lists of email / people / countries  etc...
40128  * pick multiple items from a combo box, and shows each one.
40129  *
40130  *  Fred [x]  Brian [x]  [Pick another |v]
40131  *
40132  *
40133  *  For this to work: it needs various extra information
40134  *    - normal combo problay has
40135  *      name, hiddenName
40136  *    + displayField, valueField
40137  *
40138  *    For our purpose...
40139  *
40140  *
40141  *   If we change from 'extends' to wrapping...
40142  *   
40143  *  
40144  *
40145  
40146  
40147  * @constructor
40148  * Create a new ComboBoxArray.
40149  * @param {Object} config Configuration options
40150  */
40151  
40152
40153 Roo.form.ComboBoxArray = function(config)
40154 {
40155     
40156     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40157     
40158     this.items = new Roo.util.MixedCollection(false);
40159     
40160     // construct the child combo...
40161     
40162     
40163     
40164     
40165    
40166     
40167 }
40168
40169  
40170 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40171
40172     /**
40173      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40174      */
40175     
40176     lastData : false,
40177     
40178     // behavies liek a hiddne field
40179     inputType:      'hidden',
40180     /**
40181      * @cfg {Number} width The width of the box that displays the selected element
40182      */ 
40183     width:          300,
40184
40185     
40186     
40187     /**
40188      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40189      */
40190     name : false,
40191     /**
40192      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40193      */
40194     hiddenName : false,
40195     
40196     
40197     // private the array of items that are displayed..
40198     items  : false,
40199     // private - the hidden field el.
40200     hiddenEl : false,
40201     // private - the filed el..
40202     el : false,
40203     
40204     //validateValue : function() { return true; }, // all values are ok!
40205     //onAddClick: function() { },
40206     
40207     onRender : function(ct, position) 
40208     {
40209         
40210         // create the standard hidden element
40211         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40212         
40213         
40214         // give fake names to child combo;
40215         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40216         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40217         
40218         this.combo = Roo.factory(this.combo, Roo.form);
40219         this.combo.onRender(ct, position);
40220         if (typeof(this.combo.width) != 'undefined') {
40221             this.combo.onResize(this.combo.width,0);
40222         }
40223         
40224         this.combo.initEvents();
40225         
40226         // assigned so form know we need to do this..
40227         this.store          = this.combo.store;
40228         this.valueField     = this.combo.valueField;
40229         this.displayField   = this.combo.displayField ;
40230         
40231         
40232         this.combo.wrap.addClass('x-cbarray-grp');
40233         
40234         var cbwrap = this.combo.wrap.createChild(
40235             {tag: 'div', cls: 'x-cbarray-cb'},
40236             this.combo.el.dom
40237         );
40238         
40239              
40240         this.hiddenEl = this.combo.wrap.createChild({
40241             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40242         });
40243         this.el = this.combo.wrap.createChild({
40244             tag: 'input',  type:'hidden' , name: this.name, value : ''
40245         });
40246          //   this.el.dom.removeAttribute("name");
40247         
40248         
40249         this.outerWrap = this.combo.wrap;
40250         this.wrap = cbwrap;
40251         
40252         this.outerWrap.setWidth(this.width);
40253         this.outerWrap.dom.removeChild(this.el.dom);
40254         
40255         this.wrap.dom.appendChild(this.el.dom);
40256         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40257         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40258         
40259         this.combo.trigger.setStyle('position','relative');
40260         this.combo.trigger.setStyle('left', '0px');
40261         this.combo.trigger.setStyle('top', '2px');
40262         
40263         this.combo.el.setStyle('vertical-align', 'text-bottom');
40264         
40265         //this.trigger.setStyle('vertical-align', 'top');
40266         
40267         // this should use the code from combo really... on('add' ....)
40268         if (this.adder) {
40269             
40270         
40271             this.adder = this.outerWrap.createChild(
40272                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40273             var _t = this;
40274             this.adder.on('click', function(e) {
40275                 _t.fireEvent('adderclick', this, e);
40276             }, _t);
40277         }
40278         //var _t = this;
40279         //this.adder.on('click', this.onAddClick, _t);
40280         
40281         
40282         this.combo.on('select', function(cb, rec, ix) {
40283             this.addItem(rec.data);
40284             
40285             cb.setValue('');
40286             cb.el.dom.value = '';
40287             //cb.lastData = rec.data;
40288             // add to list
40289             
40290         }, this);
40291         
40292         
40293     },
40294     
40295     
40296     getName: function()
40297     {
40298         // returns hidden if it's set..
40299         if (!this.rendered) {return ''};
40300         return  this.hiddenName ? this.hiddenName : this.name;
40301         
40302     },
40303     
40304     
40305     onResize: function(w, h){
40306         
40307         return;
40308         // not sure if this is needed..
40309         //this.combo.onResize(w,h);
40310         
40311         if(typeof w != 'number'){
40312             // we do not handle it!?!?
40313             return;
40314         }
40315         var tw = this.combo.trigger.getWidth();
40316         tw += this.addicon ? this.addicon.getWidth() : 0;
40317         tw += this.editicon ? this.editicon.getWidth() : 0;
40318         var x = w - tw;
40319         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40320             
40321         this.combo.trigger.setStyle('left', '0px');
40322         
40323         if(this.list && this.listWidth === undefined){
40324             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40325             this.list.setWidth(lw);
40326             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40327         }
40328         
40329     
40330         
40331     },
40332     
40333     addItem: function(rec)
40334     {
40335         var valueField = this.combo.valueField;
40336         var displayField = this.combo.displayField;
40337         if (this.items.indexOfKey(rec[valueField]) > -1) {
40338             //console.log("GOT " + rec.data.id);
40339             return;
40340         }
40341         
40342         var x = new Roo.form.ComboBoxArray.Item({
40343             //id : rec[this.idField],
40344             data : rec,
40345             displayField : displayField ,
40346             tipField : displayField ,
40347             cb : this
40348         });
40349         // use the 
40350         this.items.add(rec[valueField],x);
40351         // add it before the element..
40352         this.updateHiddenEl();
40353         x.render(this.outerWrap, this.wrap.dom);
40354         // add the image handler..
40355     },
40356     
40357     updateHiddenEl : function()
40358     {
40359         this.validate();
40360         if (!this.hiddenEl) {
40361             return;
40362         }
40363         var ar = [];
40364         var idField = this.combo.valueField;
40365         
40366         this.items.each(function(f) {
40367             ar.push(f.data[idField]);
40368            
40369         });
40370         this.hiddenEl.dom.value = ar.join(',');
40371         this.validate();
40372     },
40373     
40374     reset : function()
40375     {
40376         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40377         this.items.each(function(f) {
40378            f.remove(); 
40379         });
40380         this.el.dom.value = '';
40381         if (this.hiddenEl) {
40382             this.hiddenEl.dom.value = '';
40383         }
40384         
40385     },
40386     getValue: function()
40387     {
40388         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40389     },
40390     setValue: function(v) // not a valid action - must use addItems..
40391     {
40392          
40393         this.reset();
40394         
40395         
40396         
40397         if (this.store.isLocal && (typeof(v) == 'string')) {
40398             // then we can use the store to find the values..
40399             // comma seperated at present.. this needs to allow JSON based encoding..
40400             this.hiddenEl.value  = v;
40401             var v_ar = [];
40402             Roo.each(v.split(','), function(k) {
40403                 Roo.log("CHECK " + this.valueField + ',' + k);
40404                 var li = this.store.query(this.valueField, k);
40405                 if (!li.length) {
40406                     return;
40407                 }
40408                 var add = {};
40409                 add[this.valueField] = k;
40410                 add[this.displayField] = li.item(0).data[this.displayField];
40411                 
40412                 this.addItem(add);
40413             }, this) 
40414              
40415         }
40416         if (typeof(v) == 'object') {
40417             // then let's assume it's an array of objects..
40418             Roo.each(v, function(l) {
40419                 this.addItem(l);
40420             }, this);
40421              
40422         }
40423         
40424         
40425     },
40426     setFromData: function(v)
40427     {
40428         // this recieves an object, if setValues is called.
40429         this.reset();
40430         this.el.dom.value = v[this.displayField];
40431         this.hiddenEl.dom.value = v[this.valueField];
40432         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40433             return;
40434         }
40435         var kv = v[this.valueField];
40436         var dv = v[this.displayField];
40437         kv = typeof(kv) != 'string' ? '' : kv;
40438         dv = typeof(dv) != 'string' ? '' : dv;
40439         
40440         
40441         var keys = kv.split(',');
40442         var display = dv.split(',');
40443         for (var i = 0 ; i < keys.length; i++) {
40444             
40445             add = {};
40446             add[this.valueField] = keys[i];
40447             add[this.displayField] = display[i];
40448             this.addItem(add);
40449         }
40450       
40451         
40452     },
40453     
40454     /**
40455      * Validates the combox array value
40456      * @return {Boolean} True if the value is valid, else false
40457      */
40458     validate : function(){
40459         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40460             this.clearInvalid();
40461             return true;
40462         }
40463         return false;
40464     },
40465     
40466     validateValue : function(value){
40467         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40468         
40469     },
40470     
40471     /*@
40472      * overide
40473      * 
40474      */
40475     isDirty : function() {
40476         if(this.disabled) {
40477             return false;
40478         }
40479         
40480         try {
40481             var d = Roo.decode(String(this.originalValue));
40482         } catch (e) {
40483             return String(this.getValue()) !== String(this.originalValue);
40484         }
40485         
40486         var originalValue = [];
40487         
40488         for (var i = 0; i < d.length; i++){
40489             originalValue.push(d[i][this.valueField]);
40490         }
40491         
40492         return String(this.getValue()) !== String(originalValue.join(','));
40493         
40494     }
40495     
40496 });
40497
40498
40499
40500 /**
40501  * @class Roo.form.ComboBoxArray.Item
40502  * @extends Roo.BoxComponent
40503  * A selected item in the list
40504  *  Fred [x]  Brian [x]  [Pick another |v]
40505  * 
40506  * @constructor
40507  * Create a new item.
40508  * @param {Object} config Configuration options
40509  */
40510  
40511 Roo.form.ComboBoxArray.Item = function(config) {
40512     config.id = Roo.id();
40513     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40514 }
40515
40516 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40517     data : {},
40518     cb: false,
40519     displayField : false,
40520     tipField : false,
40521     
40522     
40523     defaultAutoCreate : {
40524         tag: 'div',
40525         cls: 'x-cbarray-item',
40526         cn : [ 
40527             { tag: 'div' },
40528             {
40529                 tag: 'img',
40530                 width:16,
40531                 height : 16,
40532                 src : Roo.BLANK_IMAGE_URL ,
40533                 align: 'center'
40534             }
40535         ]
40536         
40537     },
40538     
40539  
40540     onRender : function(ct, position)
40541     {
40542         Roo.form.Field.superclass.onRender.call(this, ct, position);
40543         
40544         if(!this.el){
40545             var cfg = this.getAutoCreate();
40546             this.el = ct.createChild(cfg, position);
40547         }
40548         
40549         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40550         
40551         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40552             this.cb.renderer(this.data) :
40553             String.format('{0}',this.data[this.displayField]);
40554         
40555             
40556         this.el.child('div').dom.setAttribute('qtip',
40557                         String.format('{0}',this.data[this.tipField])
40558         );
40559         
40560         this.el.child('img').on('click', this.remove, this);
40561         
40562     },
40563    
40564     remove : function()
40565     {
40566         
40567         this.cb.items.remove(this);
40568         this.el.child('img').un('click', this.remove, this);
40569         this.el.remove();
40570         this.cb.updateHiddenEl();
40571     }
40572 });/*
40573  * Based on:
40574  * Ext JS Library 1.1.1
40575  * Copyright(c) 2006-2007, Ext JS, LLC.
40576  *
40577  * Originally Released Under LGPL - original licence link has changed is not relivant.
40578  *
40579  * Fork - LGPL
40580  * <script type="text/javascript">
40581  */
40582 /**
40583  * @class Roo.form.Checkbox
40584  * @extends Roo.form.Field
40585  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40586  * @constructor
40587  * Creates a new Checkbox
40588  * @param {Object} config Configuration options
40589  */
40590 Roo.form.Checkbox = function(config){
40591     Roo.form.Checkbox.superclass.constructor.call(this, config);
40592     this.addEvents({
40593         /**
40594          * @event check
40595          * Fires when the checkbox is checked or unchecked.
40596              * @param {Roo.form.Checkbox} this This checkbox
40597              * @param {Boolean} checked The new checked value
40598              */
40599         check : true
40600     });
40601 };
40602
40603 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40604     /**
40605      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40606      */
40607     focusClass : undefined,
40608     /**
40609      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40610      */
40611     fieldClass: "x-form-field",
40612     /**
40613      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40614      */
40615     checked: false,
40616     /**
40617      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40618      * {tag: "input", type: "checkbox", autocomplete: "off"})
40619      */
40620     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40621     /**
40622      * @cfg {String} boxLabel The text that appears beside the checkbox
40623      */
40624     boxLabel : "",
40625     /**
40626      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40627      */  
40628     inputValue : '1',
40629     /**
40630      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40631      */
40632      valueOff: '0', // value when not checked..
40633
40634     actionMode : 'viewEl', 
40635     //
40636     // private
40637     itemCls : 'x-menu-check-item x-form-item',
40638     groupClass : 'x-menu-group-item',
40639     inputType : 'hidden',
40640     
40641     
40642     inSetChecked: false, // check that we are not calling self...
40643     
40644     inputElement: false, // real input element?
40645     basedOn: false, // ????
40646     
40647     isFormField: true, // not sure where this is needed!!!!
40648
40649     onResize : function(){
40650         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40651         if(!this.boxLabel){
40652             this.el.alignTo(this.wrap, 'c-c');
40653         }
40654     },
40655
40656     initEvents : function(){
40657         Roo.form.Checkbox.superclass.initEvents.call(this);
40658         this.el.on("click", this.onClick,  this);
40659         this.el.on("change", this.onClick,  this);
40660     },
40661
40662
40663     getResizeEl : function(){
40664         return this.wrap;
40665     },
40666
40667     getPositionEl : function(){
40668         return this.wrap;
40669     },
40670
40671     // private
40672     onRender : function(ct, position){
40673         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40674         /*
40675         if(this.inputValue !== undefined){
40676             this.el.dom.value = this.inputValue;
40677         }
40678         */
40679         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40680         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40681         var viewEl = this.wrap.createChild({ 
40682             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40683         this.viewEl = viewEl;   
40684         this.wrap.on('click', this.onClick,  this); 
40685         
40686         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40687         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40688         
40689         
40690         
40691         if(this.boxLabel){
40692             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40693         //    viewEl.on('click', this.onClick,  this); 
40694         }
40695         //if(this.checked){
40696             this.setChecked(this.checked);
40697         //}else{
40698             //this.checked = this.el.dom;
40699         //}
40700
40701     },
40702
40703     // private
40704     initValue : Roo.emptyFn,
40705
40706     /**
40707      * Returns the checked state of the checkbox.
40708      * @return {Boolean} True if checked, else false
40709      */
40710     getValue : function(){
40711         if(this.el){
40712             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40713         }
40714         return this.valueOff;
40715         
40716     },
40717
40718         // private
40719     onClick : function(){ 
40720         this.setChecked(!this.checked);
40721
40722         //if(this.el.dom.checked != this.checked){
40723         //    this.setValue(this.el.dom.checked);
40724        // }
40725     },
40726
40727     /**
40728      * Sets the checked state of the checkbox.
40729      * On is always based on a string comparison between inputValue and the param.
40730      * @param {Boolean/String} value - the value to set 
40731      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40732      */
40733     setValue : function(v,suppressEvent){
40734         
40735         
40736         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40737         //if(this.el && this.el.dom){
40738         //    this.el.dom.checked = this.checked;
40739         //    this.el.dom.defaultChecked = this.checked;
40740         //}
40741         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40742         //this.fireEvent("check", this, this.checked);
40743     },
40744     // private..
40745     setChecked : function(state,suppressEvent)
40746     {
40747         if (this.inSetChecked) {
40748             this.checked = state;
40749             return;
40750         }
40751         
40752     
40753         if(this.wrap){
40754             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40755         }
40756         this.checked = state;
40757         if(suppressEvent !== true){
40758             this.fireEvent('check', this, state);
40759         }
40760         this.inSetChecked = true;
40761         this.el.dom.value = state ? this.inputValue : this.valueOff;
40762         this.inSetChecked = false;
40763         
40764     },
40765     // handle setting of hidden value by some other method!!?!?
40766     setFromHidden: function()
40767     {
40768         if(!this.el){
40769             return;
40770         }
40771         //console.log("SET FROM HIDDEN");
40772         //alert('setFrom hidden');
40773         this.setValue(this.el.dom.value);
40774     },
40775     
40776     onDestroy : function()
40777     {
40778         if(this.viewEl){
40779             Roo.get(this.viewEl).remove();
40780         }
40781          
40782         Roo.form.Checkbox.superclass.onDestroy.call(this);
40783     }
40784
40785 });/*
40786  * Based on:
40787  * Ext JS Library 1.1.1
40788  * Copyright(c) 2006-2007, Ext JS, LLC.
40789  *
40790  * Originally Released Under LGPL - original licence link has changed is not relivant.
40791  *
40792  * Fork - LGPL
40793  * <script type="text/javascript">
40794  */
40795  
40796 /**
40797  * @class Roo.form.Radio
40798  * @extends Roo.form.Checkbox
40799  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40800  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40801  * @constructor
40802  * Creates a new Radio
40803  * @param {Object} config Configuration options
40804  */
40805 Roo.form.Radio = function(){
40806     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40807 };
40808 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40809     inputType: 'radio',
40810
40811     /**
40812      * If this radio is part of a group, it will return the selected value
40813      * @return {String}
40814      */
40815     getGroupValue : function(){
40816         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40817     },
40818     
40819     
40820     onRender : function(ct, position){
40821         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40822         
40823         if(this.inputValue !== undefined){
40824             this.el.dom.value = this.inputValue;
40825         }
40826          
40827         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40828         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40829         //var viewEl = this.wrap.createChild({ 
40830         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40831         //this.viewEl = viewEl;   
40832         //this.wrap.on('click', this.onClick,  this); 
40833         
40834         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40835         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40836         
40837         
40838         
40839         if(this.boxLabel){
40840             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40841         //    viewEl.on('click', this.onClick,  this); 
40842         }
40843          if(this.checked){
40844             this.el.dom.checked =   'checked' ;
40845         }
40846          
40847     } 
40848     
40849     
40850 });//<script type="text/javascript">
40851
40852 /*
40853  * Based  Ext JS Library 1.1.1
40854  * Copyright(c) 2006-2007, Ext JS, LLC.
40855  * LGPL
40856  *
40857  */
40858  
40859 /**
40860  * @class Roo.HtmlEditorCore
40861  * @extends Roo.Component
40862  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
40863  *
40864  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40865  */
40866
40867 Roo.HtmlEditorCore = function(config){
40868     
40869     
40870     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
40871     this.addEvents({
40872         /**
40873          * @event initialize
40874          * Fires when the editor is fully initialized (including the iframe)
40875          * @param {Roo.HtmlEditorCore} this
40876          */
40877         initialize: true,
40878         /**
40879          * @event activate
40880          * Fires when the editor is first receives the focus. Any insertion must wait
40881          * until after this event.
40882          * @param {Roo.HtmlEditorCore} this
40883          */
40884         activate: true,
40885          /**
40886          * @event beforesync
40887          * Fires before the textarea is updated with content from the editor iframe. Return false
40888          * to cancel the sync.
40889          * @param {Roo.HtmlEditorCore} this
40890          * @param {String} html
40891          */
40892         beforesync: true,
40893          /**
40894          * @event beforepush
40895          * Fires before the iframe editor is updated with content from the textarea. Return false
40896          * to cancel the push.
40897          * @param {Roo.HtmlEditorCore} this
40898          * @param {String} html
40899          */
40900         beforepush: true,
40901          /**
40902          * @event sync
40903          * Fires when the textarea is updated with content from the editor iframe.
40904          * @param {Roo.HtmlEditorCore} this
40905          * @param {String} html
40906          */
40907         sync: true,
40908          /**
40909          * @event push
40910          * Fires when the iframe editor is updated with content from the textarea.
40911          * @param {Roo.HtmlEditorCore} this
40912          * @param {String} html
40913          */
40914         push: true,
40915         
40916         /**
40917          * @event editorevent
40918          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40919          * @param {Roo.HtmlEditorCore} this
40920          */
40921         editorevent: true
40922     });
40923      
40924 };
40925
40926
40927 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
40928
40929
40930      /**
40931      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
40932      */
40933     
40934     owner : false,
40935     
40936      /**
40937      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40938      *                        Roo.resizable.
40939      */
40940     resizable : false,
40941      /**
40942      * @cfg {Number} height (in pixels)
40943      */   
40944     height: 300,
40945    /**
40946      * @cfg {Number} width (in pixels)
40947      */   
40948     width: 500,
40949     
40950     /**
40951      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40952      * 
40953      */
40954     stylesheets: false,
40955     
40956     // id of frame..
40957     frameId: false,
40958     
40959     // private properties
40960     validationEvent : false,
40961     deferHeight: true,
40962     initialized : false,
40963     activated : false,
40964     sourceEditMode : false,
40965     onFocus : Roo.emptyFn,
40966     iframePad:3,
40967     hideMode:'offsets',
40968     
40969      
40970     
40971
40972     /**
40973      * Protected method that will not generally be called directly. It
40974      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40975      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40976      */
40977     getDocMarkup : function(){
40978         // body styles..
40979         var st = '';
40980         Roo.log(this.stylesheets);
40981         
40982         // inherit styels from page...?? 
40983         if (this.stylesheets === false) {
40984             
40985             Roo.get(document.head).select('style').each(function(node) {
40986                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40987             });
40988             
40989             Roo.get(document.head).select('link').each(function(node) { 
40990                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40991             });
40992             
40993         } else if (!this.stylesheets.length) {
40994                 // simple..
40995                 st = '<style type="text/css">' +
40996                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40997                    '</style>';
40998         } else {
40999             Roo.each(this.stylesheets, function(s) {
41000                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41001             });
41002             
41003         }
41004         
41005         st +=  '<style type="text/css">' +
41006             'IMG { cursor: pointer } ' +
41007         '</style>';
41008
41009         
41010         return '<html><head>' + st  +
41011             //<style type="text/css">' +
41012             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41013             //'</style>' +
41014             ' </head><body class="roo-htmleditor-body"></body></html>';
41015     },
41016
41017     // private
41018     onRender : function(ct, position)
41019     {
41020         var _t = this;
41021         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41022         this.el = this.owner.el;
41023         
41024         
41025         this.el.dom.style.border = '0 none';
41026         this.el.dom.setAttribute('tabIndex', -1);
41027         this.el.addClass('x-hidden');
41028         
41029         
41030         
41031         if(Roo.isIE){ // fix IE 1px bogus margin
41032             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41033         }
41034        
41035         
41036         this.frameId = Roo.id();
41037         
41038          
41039         
41040         var iframe = this.owner.wrap.createChild({
41041             tag: 'iframe',
41042             id: this.frameId,
41043             name: this.frameId,
41044             frameBorder : 'no',
41045             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41046         }, this.el
41047         );
41048         
41049         
41050         this.iframe = iframe.dom;
41051
41052          this.assignDocWin();
41053         
41054         this.doc.designMode = 'on';
41055        
41056         this.doc.open();
41057         this.doc.write(this.getDocMarkup());
41058         this.doc.close();
41059
41060         
41061         var task = { // must defer to wait for browser to be ready
41062             run : function(){
41063                 //console.log("run task?" + this.doc.readyState);
41064                 this.assignDocWin();
41065                 if(this.doc.body || this.doc.readyState == 'complete'){
41066                     try {
41067                         this.doc.designMode="on";
41068                     } catch (e) {
41069                         return;
41070                     }
41071                     Roo.TaskMgr.stop(task);
41072                     this.initEditor.defer(10, this);
41073                 }
41074             },
41075             interval : 10,
41076             duration: 10000,
41077             scope: this
41078         };
41079         Roo.TaskMgr.start(task);
41080
41081         
41082          
41083     },
41084
41085     // private
41086     onResize : function(w, h)
41087     {
41088         //Roo.log('resize: ' +w + ',' + h );
41089         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41090         if(!this.iframe){
41091             return;
41092         }
41093         if(typeof w == 'number'){
41094             
41095             this.iframe.style.width = w + 'px';
41096         }
41097         if(typeof h == 'number'){
41098             
41099             this.iframe.style.height = h + 'px';
41100             if(this.doc){
41101                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41102             }
41103         }
41104         
41105     },
41106
41107     /**
41108      * Toggles the editor between standard and source edit mode.
41109      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41110      */
41111     toggleSourceEdit : function(sourceEditMode){
41112         
41113         this.sourceEditMode = sourceEditMode === true;
41114         
41115         if(this.sourceEditMode){
41116  
41117             this.iframe.className = 'x-hidden';     //FIXME - what's the BS styles for these
41118             
41119         }else{
41120  
41121             this.iframe.className = '';
41122             this.deferFocus();
41123         }
41124         //this.setSize(this.owner.wrap.getSize());
41125         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41126     },
41127
41128     
41129   
41130
41131     /**
41132      * Protected method that will not generally be called directly. If you need/want
41133      * custom HTML cleanup, this is the method you should override.
41134      * @param {String} html The HTML to be cleaned
41135      * return {String} The cleaned HTML
41136      */
41137     cleanHtml : function(html){
41138         html = String(html);
41139         if(html.length > 5){
41140             if(Roo.isSafari){ // strip safari nonsense
41141                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41142             }
41143         }
41144         if(html == '&nbsp;'){
41145             html = '';
41146         }
41147         return html;
41148     },
41149
41150     /**
41151      * HTML Editor -> Textarea
41152      * Protected method that will not generally be called directly. Syncs the contents
41153      * of the editor iframe with the textarea.
41154      */
41155     syncValue : function(){
41156         if(this.initialized){
41157             var bd = (this.doc.body || this.doc.documentElement);
41158             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41159             var html = bd.innerHTML;
41160             if(Roo.isSafari){
41161                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41162                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41163                 if(m && m[1]){
41164                     html = '<div style="'+m[0]+'">' + html + '</div>';
41165                 }
41166             }
41167             html = this.cleanHtml(html);
41168             // fix up the special chars.. normaly like back quotes in word...
41169             // however we do not want to do this with chinese..
41170             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41171                 var cc = b.charCodeAt();
41172                 if (
41173                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41174                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41175                     (cc >= 0xf900 && cc < 0xfb00 )
41176                 ) {
41177                         return b;
41178                 }
41179                 return "&#"+cc+";" 
41180             });
41181             if(this.owner.fireEvent('beforesync', this, html) !== false){
41182                 this.el.dom.value = html;
41183                 this.owner.fireEvent('sync', this, html);
41184             }
41185         }
41186     },
41187
41188     /**
41189      * Protected method that will not generally be called directly. Pushes the value of the textarea
41190      * into the iframe editor.
41191      */
41192     pushValue : function(){
41193         if(this.initialized){
41194             var v = this.el.dom.value;
41195             
41196             if(v.length < 1){
41197                 v = '&#160;';
41198             }
41199             
41200             if(this.owner.fireEvent('beforepush', this, v) !== false){
41201                 var d = (this.doc.body || this.doc.documentElement);
41202                 d.innerHTML = v;
41203                 this.cleanUpPaste();
41204                 this.el.dom.value = d.innerHTML;
41205                 this.owner.fireEvent('push', this, v);
41206             }
41207         }
41208     },
41209
41210     // private
41211     deferFocus : function(){
41212         this.focus.defer(10, this);
41213     },
41214
41215     // doc'ed in Field
41216     focus : function(){
41217         if(this.win && !this.sourceEditMode){
41218             this.win.focus();
41219         }else{
41220             this.el.focus();
41221         }
41222     },
41223     
41224     assignDocWin: function()
41225     {
41226         var iframe = this.iframe;
41227         
41228          if(Roo.isIE){
41229             this.doc = iframe.contentWindow.document;
41230             this.win = iframe.contentWindow;
41231         } else {
41232             if (!Roo.get(this.frameId)) {
41233                 return;
41234             }
41235             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41236             this.win = Roo.get(this.frameId).dom.contentWindow;
41237         }
41238     },
41239     
41240     // private
41241     initEditor : function(){
41242         //console.log("INIT EDITOR");
41243         this.assignDocWin();
41244         
41245         
41246         
41247         this.doc.designMode="on";
41248         this.doc.open();
41249         this.doc.write(this.getDocMarkup());
41250         this.doc.close();
41251         
41252         var dbody = (this.doc.body || this.doc.documentElement);
41253         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41254         // this copies styles from the containing element into thsi one..
41255         // not sure why we need all of this..
41256         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41257         ss['background-attachment'] = 'fixed'; // w3c
41258         dbody.bgProperties = 'fixed'; // ie
41259         Roo.DomHelper.applyStyles(dbody, ss);
41260         Roo.EventManager.on(this.doc, {
41261             //'mousedown': this.onEditorEvent,
41262             'mouseup': this.onEditorEvent,
41263             'dblclick': this.onEditorEvent,
41264             'click': this.onEditorEvent,
41265             'keyup': this.onEditorEvent,
41266             buffer:100,
41267             scope: this
41268         });
41269         if(Roo.isGecko){
41270             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41271         }
41272         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41273             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41274         }
41275         this.initialized = true;
41276
41277         this.owner.fireEvent('initialize', this);
41278         this.pushValue();
41279     },
41280
41281     // private
41282     onDestroy : function(){
41283         
41284         
41285         
41286         if(this.rendered){
41287             
41288             //for (var i =0; i < this.toolbars.length;i++) {
41289             //    // fixme - ask toolbars for heights?
41290             //    this.toolbars[i].onDestroy();
41291            // }
41292             
41293             //this.wrap.dom.innerHTML = '';
41294             //this.wrap.remove();
41295         }
41296     },
41297
41298     // private
41299     onFirstFocus : function(){
41300         
41301         this.assignDocWin();
41302         
41303         
41304         this.activated = true;
41305          
41306     
41307         if(Roo.isGecko){ // prevent silly gecko errors
41308             this.win.focus();
41309             var s = this.win.getSelection();
41310             if(!s.focusNode || s.focusNode.nodeType != 3){
41311                 var r = s.getRangeAt(0);
41312                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41313                 r.collapse(true);
41314                 this.deferFocus();
41315             }
41316             try{
41317                 this.execCmd('useCSS', true);
41318                 this.execCmd('styleWithCSS', false);
41319             }catch(e){}
41320         }
41321         this.owner.fireEvent('activate', this);
41322     },
41323
41324     // private
41325     adjustFont: function(btn){
41326         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41327         //if(Roo.isSafari){ // safari
41328         //    adjust *= 2;
41329        // }
41330         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41331         if(Roo.isSafari){ // safari
41332             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41333             v =  (v < 10) ? 10 : v;
41334             v =  (v > 48) ? 48 : v;
41335             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41336             
41337         }
41338         
41339         
41340         v = Math.max(1, v+adjust);
41341         
41342         this.execCmd('FontSize', v  );
41343     },
41344
41345     onEditorEvent : function(e){
41346         this.owner.fireEvent('editorevent', this, e);
41347       //  this.updateToolbar();
41348         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41349     },
41350
41351     insertTag : function(tg)
41352     {
41353         // could be a bit smarter... -> wrap the current selected tRoo..
41354         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41355             
41356             range = this.createRange(this.getSelection());
41357             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41358             wrappingNode.appendChild(range.extractContents());
41359             range.insertNode(wrappingNode);
41360
41361             return;
41362             
41363             
41364             
41365         }
41366         this.execCmd("formatblock",   tg);
41367         
41368     },
41369     
41370     insertText : function(txt)
41371     {
41372         
41373         
41374         var range = this.createRange();
41375         range.deleteContents();
41376                //alert(Sender.getAttribute('label'));
41377                
41378         range.insertNode(this.doc.createTextNode(txt));
41379     } ,
41380     
41381      
41382
41383     /**
41384      * Executes a Midas editor command on the editor document and performs necessary focus and
41385      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41386      * @param {String} cmd The Midas command
41387      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41388      */
41389     relayCmd : function(cmd, value){
41390         this.win.focus();
41391         this.execCmd(cmd, value);
41392         this.owner.fireEvent('editorevent', this);
41393         //this.updateToolbar();
41394         this.owner.deferFocus();
41395     },
41396
41397     /**
41398      * Executes a Midas editor command directly on the editor document.
41399      * For visual commands, you should use {@link #relayCmd} instead.
41400      * <b>This should only be called after the editor is initialized.</b>
41401      * @param {String} cmd The Midas command
41402      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41403      */
41404     execCmd : function(cmd, value){
41405         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41406         this.syncValue();
41407     },
41408  
41409  
41410    
41411     /**
41412      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41413      * to insert tRoo.
41414      * @param {String} text | dom node.. 
41415      */
41416     insertAtCursor : function(text)
41417     {
41418         
41419         
41420         
41421         if(!this.activated){
41422             return;
41423         }
41424         /*
41425         if(Roo.isIE){
41426             this.win.focus();
41427             var r = this.doc.selection.createRange();
41428             if(r){
41429                 r.collapse(true);
41430                 r.pasteHTML(text);
41431                 this.syncValue();
41432                 this.deferFocus();
41433             
41434             }
41435             return;
41436         }
41437         */
41438         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41439             this.win.focus();
41440             
41441             
41442             // from jquery ui (MIT licenced)
41443             var range, node;
41444             var win = this.win;
41445             
41446             if (win.getSelection && win.getSelection().getRangeAt) {
41447                 range = win.getSelection().getRangeAt(0);
41448                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41449                 range.insertNode(node);
41450             } else if (win.document.selection && win.document.selection.createRange) {
41451                 // no firefox support
41452                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41453                 win.document.selection.createRange().pasteHTML(txt);
41454             } else {
41455                 // no firefox support
41456                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41457                 this.execCmd('InsertHTML', txt);
41458             } 
41459             
41460             this.syncValue();
41461             
41462             this.deferFocus();
41463         }
41464     },
41465  // private
41466     mozKeyPress : function(e){
41467         if(e.ctrlKey){
41468             var c = e.getCharCode(), cmd;
41469           
41470             if(c > 0){
41471                 c = String.fromCharCode(c).toLowerCase();
41472                 switch(c){
41473                     case 'b':
41474                         cmd = 'bold';
41475                         break;
41476                     case 'i':
41477                         cmd = 'italic';
41478                         break;
41479                     
41480                     case 'u':
41481                         cmd = 'underline';
41482                         break;
41483                     
41484                     case 'v':
41485                         this.cleanUpPaste.defer(100, this);
41486                         return;
41487                         
41488                 }
41489                 if(cmd){
41490                     this.win.focus();
41491                     this.execCmd(cmd);
41492                     this.deferFocus();
41493                     e.preventDefault();
41494                 }
41495                 
41496             }
41497         }
41498     },
41499
41500     // private
41501     fixKeys : function(){ // load time branching for fastest keydown performance
41502         if(Roo.isIE){
41503             return function(e){
41504                 var k = e.getKey(), r;
41505                 if(k == e.TAB){
41506                     e.stopEvent();
41507                     r = this.doc.selection.createRange();
41508                     if(r){
41509                         r.collapse(true);
41510                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41511                         this.deferFocus();
41512                     }
41513                     return;
41514                 }
41515                 
41516                 if(k == e.ENTER){
41517                     r = this.doc.selection.createRange();
41518                     if(r){
41519                         var target = r.parentElement();
41520                         if(!target || target.tagName.toLowerCase() != 'li'){
41521                             e.stopEvent();
41522                             r.pasteHTML('<br />');
41523                             r.collapse(false);
41524                             r.select();
41525                         }
41526                     }
41527                 }
41528                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41529                     this.cleanUpPaste.defer(100, this);
41530                     return;
41531                 }
41532                 
41533                 
41534             };
41535         }else if(Roo.isOpera){
41536             return function(e){
41537                 var k = e.getKey();
41538                 if(k == e.TAB){
41539                     e.stopEvent();
41540                     this.win.focus();
41541                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41542                     this.deferFocus();
41543                 }
41544                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41545                     this.cleanUpPaste.defer(100, this);
41546                     return;
41547                 }
41548                 
41549             };
41550         }else if(Roo.isSafari){
41551             return function(e){
41552                 var k = e.getKey();
41553                 
41554                 if(k == e.TAB){
41555                     e.stopEvent();
41556                     this.execCmd('InsertText','\t');
41557                     this.deferFocus();
41558                     return;
41559                 }
41560                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41561                     this.cleanUpPaste.defer(100, this);
41562                     return;
41563                 }
41564                 
41565              };
41566         }
41567     }(),
41568     
41569     getAllAncestors: function()
41570     {
41571         var p = this.getSelectedNode();
41572         var a = [];
41573         if (!p) {
41574             a.push(p); // push blank onto stack..
41575             p = this.getParentElement();
41576         }
41577         
41578         
41579         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41580             a.push(p);
41581             p = p.parentNode;
41582         }
41583         a.push(this.doc.body);
41584         return a;
41585     },
41586     lastSel : false,
41587     lastSelNode : false,
41588     
41589     
41590     getSelection : function() 
41591     {
41592         this.assignDocWin();
41593         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41594     },
41595     
41596     getSelectedNode: function() 
41597     {
41598         // this may only work on Gecko!!!
41599         
41600         // should we cache this!!!!
41601         
41602         
41603         
41604          
41605         var range = this.createRange(this.getSelection()).cloneRange();
41606         
41607         if (Roo.isIE) {
41608             var parent = range.parentElement();
41609             while (true) {
41610                 var testRange = range.duplicate();
41611                 testRange.moveToElementText(parent);
41612                 if (testRange.inRange(range)) {
41613                     break;
41614                 }
41615                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41616                     break;
41617                 }
41618                 parent = parent.parentElement;
41619             }
41620             return parent;
41621         }
41622         
41623         // is ancestor a text element.
41624         var ac =  range.commonAncestorContainer;
41625         if (ac.nodeType == 3) {
41626             ac = ac.parentNode;
41627         }
41628         
41629         var ar = ac.childNodes;
41630          
41631         var nodes = [];
41632         var other_nodes = [];
41633         var has_other_nodes = false;
41634         for (var i=0;i<ar.length;i++) {
41635             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41636                 continue;
41637             }
41638             // fullly contained node.
41639             
41640             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41641                 nodes.push(ar[i]);
41642                 continue;
41643             }
41644             
41645             // probably selected..
41646             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41647                 other_nodes.push(ar[i]);
41648                 continue;
41649             }
41650             // outer..
41651             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41652                 continue;
41653             }
41654             
41655             
41656             has_other_nodes = true;
41657         }
41658         if (!nodes.length && other_nodes.length) {
41659             nodes= other_nodes;
41660         }
41661         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41662             return false;
41663         }
41664         
41665         return nodes[0];
41666     },
41667     createRange: function(sel)
41668     {
41669         // this has strange effects when using with 
41670         // top toolbar - not sure if it's a great idea.
41671         //this.editor.contentWindow.focus();
41672         if (typeof sel != "undefined") {
41673             try {
41674                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41675             } catch(e) {
41676                 return this.doc.createRange();
41677             }
41678         } else {
41679             return this.doc.createRange();
41680         }
41681     },
41682     getParentElement: function()
41683     {
41684         
41685         this.assignDocWin();
41686         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41687         
41688         var range = this.createRange(sel);
41689          
41690         try {
41691             var p = range.commonAncestorContainer;
41692             while (p.nodeType == 3) { // text node
41693                 p = p.parentNode;
41694             }
41695             return p;
41696         } catch (e) {
41697             return null;
41698         }
41699     
41700     },
41701     /***
41702      *
41703      * Range intersection.. the hard stuff...
41704      *  '-1' = before
41705      *  '0' = hits..
41706      *  '1' = after.
41707      *         [ -- selected range --- ]
41708      *   [fail]                        [fail]
41709      *
41710      *    basically..
41711      *      if end is before start or  hits it. fail.
41712      *      if start is after end or hits it fail.
41713      *
41714      *   if either hits (but other is outside. - then it's not 
41715      *   
41716      *    
41717      **/
41718     
41719     
41720     // @see http://www.thismuchiknow.co.uk/?p=64.
41721     rangeIntersectsNode : function(range, node)
41722     {
41723         var nodeRange = node.ownerDocument.createRange();
41724         try {
41725             nodeRange.selectNode(node);
41726         } catch (e) {
41727             nodeRange.selectNodeContents(node);
41728         }
41729     
41730         var rangeStartRange = range.cloneRange();
41731         rangeStartRange.collapse(true);
41732     
41733         var rangeEndRange = range.cloneRange();
41734         rangeEndRange.collapse(false);
41735     
41736         var nodeStartRange = nodeRange.cloneRange();
41737         nodeStartRange.collapse(true);
41738     
41739         var nodeEndRange = nodeRange.cloneRange();
41740         nodeEndRange.collapse(false);
41741     
41742         return rangeStartRange.compareBoundaryPoints(
41743                  Range.START_TO_START, nodeEndRange) == -1 &&
41744                rangeEndRange.compareBoundaryPoints(
41745                  Range.START_TO_START, nodeStartRange) == 1;
41746         
41747          
41748     },
41749     rangeCompareNode : function(range, node)
41750     {
41751         var nodeRange = node.ownerDocument.createRange();
41752         try {
41753             nodeRange.selectNode(node);
41754         } catch (e) {
41755             nodeRange.selectNodeContents(node);
41756         }
41757         
41758         
41759         range.collapse(true);
41760     
41761         nodeRange.collapse(true);
41762      
41763         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41764         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41765          
41766         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41767         
41768         var nodeIsBefore   =  ss == 1;
41769         var nodeIsAfter    = ee == -1;
41770         
41771         if (nodeIsBefore && nodeIsAfter)
41772             return 0; // outer
41773         if (!nodeIsBefore && nodeIsAfter)
41774             return 1; //right trailed.
41775         
41776         if (nodeIsBefore && !nodeIsAfter)
41777             return 2;  // left trailed.
41778         // fully contined.
41779         return 3;
41780     },
41781
41782     // private? - in a new class?
41783     cleanUpPaste :  function()
41784     {
41785         // cleans up the whole document..
41786          Roo.log('cleanuppaste');
41787         this.cleanUpChildren(this.doc.body);
41788         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41789         if (clean != this.doc.body.innerHTML) {
41790             this.doc.body.innerHTML = clean;
41791         }
41792         
41793     },
41794     
41795     cleanWordChars : function(input) {// change the chars to hex code
41796         var he = Roo.HtmlEditorCore;
41797         
41798         var output = input;
41799         Roo.each(he.swapCodes, function(sw) { 
41800             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41801             
41802             output = output.replace(swapper, sw[1]);
41803         });
41804         
41805         return output;
41806     },
41807     
41808     
41809     cleanUpChildren : function (n)
41810     {
41811         if (!n.childNodes.length) {
41812             return;
41813         }
41814         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41815            this.cleanUpChild(n.childNodes[i]);
41816         }
41817     },
41818     
41819     
41820         
41821     
41822     cleanUpChild : function (node)
41823     {
41824         var ed = this;
41825         //console.log(node);
41826         if (node.nodeName == "#text") {
41827             // clean up silly Windows -- stuff?
41828             return; 
41829         }
41830         if (node.nodeName == "#comment") {
41831             node.parentNode.removeChild(node);
41832             // clean up silly Windows -- stuff?
41833             return; 
41834         }
41835         
41836         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
41837             // remove node.
41838             node.parentNode.removeChild(node);
41839             return;
41840             
41841         }
41842         
41843         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41844         
41845         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41846         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41847         
41848         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41849         //    remove_keep_children = true;
41850         //}
41851         
41852         if (remove_keep_children) {
41853             this.cleanUpChildren(node);
41854             // inserts everything just before this node...
41855             while (node.childNodes.length) {
41856                 var cn = node.childNodes[0];
41857                 node.removeChild(cn);
41858                 node.parentNode.insertBefore(cn, node);
41859             }
41860             node.parentNode.removeChild(node);
41861             return;
41862         }
41863         
41864         if (!node.attributes || !node.attributes.length) {
41865             this.cleanUpChildren(node);
41866             return;
41867         }
41868         
41869         function cleanAttr(n,v)
41870         {
41871             
41872             if (v.match(/^\./) || v.match(/^\//)) {
41873                 return;
41874             }
41875             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41876                 return;
41877             }
41878             if (v.match(/^#/)) {
41879                 return;
41880             }
41881 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41882             node.removeAttribute(n);
41883             
41884         }
41885         
41886         function cleanStyle(n,v)
41887         {
41888             if (v.match(/expression/)) { //XSS?? should we even bother..
41889                 node.removeAttribute(n);
41890                 return;
41891             }
41892             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
41893             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
41894             
41895             
41896             var parts = v.split(/;/);
41897             var clean = [];
41898             
41899             Roo.each(parts, function(p) {
41900                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41901                 if (!p.length) {
41902                     return true;
41903                 }
41904                 var l = p.split(':').shift().replace(/\s+/g,'');
41905                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41906                 
41907                 
41908                 if ( cblack.indexOf(l) > -1) {
41909 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41910                     //node.removeAttribute(n);
41911                     return true;
41912                 }
41913                 //Roo.log()
41914                 // only allow 'c whitelisted system attributes'
41915                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41916 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41917                     //node.removeAttribute(n);
41918                     return true;
41919                 }
41920                 
41921                 
41922                  
41923                 
41924                 clean.push(p);
41925                 return true;
41926             });
41927             if (clean.length) { 
41928                 node.setAttribute(n, clean.join(';'));
41929             } else {
41930                 node.removeAttribute(n);
41931             }
41932             
41933         }
41934         
41935         
41936         for (var i = node.attributes.length-1; i > -1 ; i--) {
41937             var a = node.attributes[i];
41938             //console.log(a);
41939             
41940             if (a.name.toLowerCase().substr(0,2)=='on')  {
41941                 node.removeAttribute(a.name);
41942                 continue;
41943             }
41944             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
41945                 node.removeAttribute(a.name);
41946                 continue;
41947             }
41948             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
41949                 cleanAttr(a.name,a.value); // fixme..
41950                 continue;
41951             }
41952             if (a.name == 'style') {
41953                 cleanStyle(a.name,a.value);
41954                 continue;
41955             }
41956             /// clean up MS crap..
41957             // tecnically this should be a list of valid class'es..
41958             
41959             
41960             if (a.name == 'class') {
41961                 if (a.value.match(/^Mso/)) {
41962                     node.className = '';
41963                 }
41964                 
41965                 if (a.value.match(/body/)) {
41966                     node.className = '';
41967                 }
41968                 continue;
41969             }
41970             
41971             // style cleanup!?
41972             // class cleanup?
41973             
41974         }
41975         
41976         
41977         this.cleanUpChildren(node);
41978         
41979         
41980     }
41981     
41982     
41983     // hide stuff that is not compatible
41984     /**
41985      * @event blur
41986      * @hide
41987      */
41988     /**
41989      * @event change
41990      * @hide
41991      */
41992     /**
41993      * @event focus
41994      * @hide
41995      */
41996     /**
41997      * @event specialkey
41998      * @hide
41999      */
42000     /**
42001      * @cfg {String} fieldClass @hide
42002      */
42003     /**
42004      * @cfg {String} focusClass @hide
42005      */
42006     /**
42007      * @cfg {String} autoCreate @hide
42008      */
42009     /**
42010      * @cfg {String} inputType @hide
42011      */
42012     /**
42013      * @cfg {String} invalidClass @hide
42014      */
42015     /**
42016      * @cfg {String} invalidText @hide
42017      */
42018     /**
42019      * @cfg {String} msgFx @hide
42020      */
42021     /**
42022      * @cfg {String} validateOnBlur @hide
42023      */
42024 });
42025
42026 Roo.HtmlEditorCore.white = [
42027         'area', 'br', 'img', 'input', 'hr', 'wbr',
42028         
42029        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42030        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42031        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42032        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42033        'table',   'ul',         'xmp', 
42034        
42035        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42036       'thead',   'tr', 
42037      
42038       'dir', 'menu', 'ol', 'ul', 'dl',
42039        
42040       'embed',  'object'
42041 ];
42042
42043
42044 Roo.HtmlEditorCore.black = [
42045     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42046         'applet', // 
42047         'base',   'basefont', 'bgsound', 'blink',  'body', 
42048         'frame',  'frameset', 'head',    'html',   'ilayer', 
42049         'iframe', 'layer',  'link',     'meta',    'object',   
42050         'script', 'style' ,'title',  'xml' // clean later..
42051 ];
42052 Roo.HtmlEditorCore.clean = [
42053     'script', 'style', 'title', 'xml'
42054 ];
42055 Roo.HtmlEditorCore.remove = [
42056     'font'
42057 ];
42058 // attributes..
42059
42060 Roo.HtmlEditorCore.ablack = [
42061     'on'
42062 ];
42063     
42064 Roo.HtmlEditorCore.aclean = [ 
42065     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42066 ];
42067
42068 // protocols..
42069 Roo.HtmlEditorCore.pwhite= [
42070         'http',  'https',  'mailto'
42071 ];
42072
42073 // white listed style attributes.
42074 Roo.HtmlEditorCore.cwhite= [
42075       //  'text-align', /// default is to allow most things..
42076       
42077          
42078 //        'font-size'//??
42079 ];
42080
42081 // black listed style attributes.
42082 Roo.HtmlEditorCore.cblack= [
42083       //  'font-size' -- this can be set by the project 
42084 ];
42085
42086
42087 Roo.HtmlEditorCore.swapCodes   =[ 
42088     [    8211, "--" ], 
42089     [    8212, "--" ], 
42090     [    8216,  "'" ],  
42091     [    8217, "'" ],  
42092     [    8220, '"' ],  
42093     [    8221, '"' ],  
42094     [    8226, "*" ],  
42095     [    8230, "..." ]
42096 ]; 
42097
42098     //<script type="text/javascript">
42099
42100 /*
42101  * Ext JS Library 1.1.1
42102  * Copyright(c) 2006-2007, Ext JS, LLC.
42103  * Licence LGPL
42104  * 
42105  */
42106  
42107  
42108 Roo.form.HtmlEditor = function(config){
42109     
42110     
42111     
42112     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42113     
42114     if (!this.toolbars) {
42115         this.toolbars = [];
42116     }
42117     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42118     
42119     
42120 };
42121
42122 /**
42123  * @class Ext.form.HtmlEditor
42124  * @extends Ext.form.Field
42125  * Provides a lightweight HTML Editor component.
42126  *
42127  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42128  * 
42129  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42130  * supported by this editor.</b><br/><br/>
42131  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42132  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42133  */
42134 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42135       /**
42136      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42137      */
42138     toolbars : false,
42139    
42140      /**
42141      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42142      *                        Roo.resizable.
42143      */
42144     resizable : false,
42145      /**
42146      * @cfg {Number} height (in pixels)
42147      */   
42148     height: 300,
42149    /**
42150      * @cfg {Number} width (in pixels)
42151      */   
42152     width: 500,
42153     
42154     /**
42155      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42156      * 
42157      */
42158     stylesheets: false,
42159     
42160     // id of frame..
42161     frameId: false,
42162     
42163     // private properties
42164     validationEvent : false,
42165     deferHeight: true,
42166     initialized : false,
42167     activated : false,
42168     
42169     onFocus : Roo.emptyFn,
42170     iframePad:3,
42171     hideMode:'offsets',
42172     
42173     defaultAutoCreate : { // modified by initCompnoent..
42174         tag: "textarea",
42175         style:"width:500px;height:300px;",
42176         autocomplete: "off"
42177     },
42178
42179     // private
42180     initComponent : function(){
42181         this.addEvents({
42182             /**
42183              * @event initialize
42184              * Fires when the editor is fully initialized (including the iframe)
42185              * @param {HtmlEditor} this
42186              */
42187             initialize: true,
42188             /**
42189              * @event activate
42190              * Fires when the editor is first receives the focus. Any insertion must wait
42191              * until after this event.
42192              * @param {HtmlEditor} this
42193              */
42194             activate: true,
42195              /**
42196              * @event beforesync
42197              * Fires before the textarea is updated with content from the editor iframe. Return false
42198              * to cancel the sync.
42199              * @param {HtmlEditor} this
42200              * @param {String} html
42201              */
42202             beforesync: true,
42203              /**
42204              * @event beforepush
42205              * Fires before the iframe editor is updated with content from the textarea. Return false
42206              * to cancel the push.
42207              * @param {HtmlEditor} this
42208              * @param {String} html
42209              */
42210             beforepush: true,
42211              /**
42212              * @event sync
42213              * Fires when the textarea is updated with content from the editor iframe.
42214              * @param {HtmlEditor} this
42215              * @param {String} html
42216              */
42217             sync: true,
42218              /**
42219              * @event push
42220              * Fires when the iframe editor is updated with content from the textarea.
42221              * @param {HtmlEditor} this
42222              * @param {String} html
42223              */
42224             push: true,
42225              /**
42226              * @event editmodechange
42227              * Fires when the editor switches edit modes
42228              * @param {HtmlEditor} this
42229              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42230              */
42231             editmodechange: true,
42232             /**
42233              * @event editorevent
42234              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42235              * @param {HtmlEditor} this
42236              */
42237             editorevent: true,
42238             /**
42239              * @event firstfocus
42240              * Fires when on first focus - needed by toolbars..
42241              * @param {HtmlEditor} this
42242              */
42243             firstfocus: true
42244         });
42245         this.defaultAutoCreate =  {
42246             tag: "textarea",
42247             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42248             autocomplete: "off"
42249         };
42250     },
42251
42252     /**
42253      * Protected method that will not generally be called directly. It
42254      * is called when the editor creates its toolbar. Override this method if you need to
42255      * add custom toolbar buttons.
42256      * @param {HtmlEditor} editor
42257      */
42258     createToolbar : function(editor){
42259         Roo.log("create toolbars");
42260         if (!editor.toolbars || !editor.toolbars.length) {
42261             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42262         }
42263         
42264         for (var i =0 ; i < editor.toolbars.length;i++) {
42265             editor.toolbars[i] = Roo.factory(
42266                     typeof(editor.toolbars[i]) == 'string' ?
42267                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42268                 Roo.form.HtmlEditor);
42269             editor.toolbars[i].init(editor);
42270         }
42271          
42272         
42273     },
42274
42275      
42276     // private
42277     onRender : function(ct, position)
42278     {
42279         var _t = this;
42280         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42281         
42282         this.wrap = this.el.wrap({
42283             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42284         });
42285         
42286         this.editorcore.onRender(ct, position);
42287          
42288         if (this.resizable) {
42289             this.resizeEl = new Roo.Resizable(this.wrap, {
42290                 pinned : true,
42291                 wrap: true,
42292                 dynamic : true,
42293                 minHeight : this.height,
42294                 height: this.height,
42295                 handles : this.resizable,
42296                 width: this.width,
42297                 listeners : {
42298                     resize : function(r, w, h) {
42299                         _t.onResize(w,h); // -something
42300                     }
42301                 }
42302             });
42303             
42304         }
42305         this.createToolbar(this);
42306        
42307         
42308         if(!this.width){
42309             this.setSize(this.wrap.getSize());
42310         }
42311         if (this.resizeEl) {
42312             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42313             // should trigger onReize..
42314         }
42315     },
42316
42317     // private
42318     onResize : function(w, h)
42319     {
42320         //Roo.log('resize: ' +w + ',' + h );
42321         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42322         var ew = false;
42323         var eh = false;
42324         
42325         if(this.el ){
42326             if(typeof w == 'number'){
42327                 var aw = w - this.wrap.getFrameWidth('lr');
42328                 this.el.setWidth(this.adjustWidth('textarea', aw));
42329                 ew = aw;
42330             }
42331             if(typeof h == 'number'){
42332                 var tbh = 0;
42333                 for (var i =0; i < this.toolbars.length;i++) {
42334                     // fixme - ask toolbars for heights?
42335                     tbh += this.toolbars[i].tb.el.getHeight();
42336                     if (this.toolbars[i].footer) {
42337                         tbh += this.toolbars[i].footer.el.getHeight();
42338                     }
42339                 }
42340                 
42341                 
42342                 
42343                 
42344                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42345                 ah -= 5; // knock a few pixes off for look..
42346                 this.el.setHeight(this.adjustWidth('textarea', ah));
42347                 var eh = ah;
42348             }
42349         }
42350         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42351         this.editorcore.onResize(ew,eh);
42352         
42353     },
42354
42355     /**
42356      * Toggles the editor between standard and source edit mode.
42357      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42358      */
42359     toggleSourceEdit : function(sourceEditMode)
42360     {
42361         this.editorcore.toggleSourceEdit(sourceEditMode);
42362         
42363         if(this.editorcore.sourceEditMode){
42364             Roo.log('editor - showing textarea');
42365             
42366 //            Roo.log('in');
42367 //            Roo.log(this.syncValue());
42368             this.editorcore.syncValue();
42369             this.el.removeClass('x-hidden');
42370             this.el.dom.removeAttribute('tabIndex');
42371             this.el.focus();
42372         }else{
42373             Roo.log('editor - hiding textarea');
42374 //            Roo.log('out')
42375 //            Roo.log(this.pushValue()); 
42376             this.editorcore.pushValue();
42377             
42378             this.el.addClass('x-hidden');
42379             this.el.dom.setAttribute('tabIndex', -1);
42380             //this.deferFocus();
42381         }
42382          
42383         this.setSize(this.wrap.getSize());
42384         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42385     },
42386  
42387     // private (for BoxComponent)
42388     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42389
42390     // private (for BoxComponent)
42391     getResizeEl : function(){
42392         return this.wrap;
42393     },
42394
42395     // private (for BoxComponent)
42396     getPositionEl : function(){
42397         return this.wrap;
42398     },
42399
42400     // private
42401     initEvents : function(){
42402         this.originalValue = this.getValue();
42403     },
42404
42405     /**
42406      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42407      * @method
42408      */
42409     markInvalid : Roo.emptyFn,
42410     /**
42411      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42412      * @method
42413      */
42414     clearInvalid : Roo.emptyFn,
42415
42416     setValue : function(v){
42417         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42418         this.editorcore.pushValue();
42419     },
42420
42421      
42422     // private
42423     deferFocus : function(){
42424         this.focus.defer(10, this);
42425     },
42426
42427     // doc'ed in Field
42428     focus : function(){
42429         this.editorcore.focus();
42430         
42431     },
42432       
42433
42434     // private
42435     onDestroy : function(){
42436         
42437         
42438         
42439         if(this.rendered){
42440             
42441             for (var i =0; i < this.toolbars.length;i++) {
42442                 // fixme - ask toolbars for heights?
42443                 this.toolbars[i].onDestroy();
42444             }
42445             
42446             this.wrap.dom.innerHTML = '';
42447             this.wrap.remove();
42448         }
42449     },
42450
42451     // private
42452     onFirstFocus : function(){
42453         //Roo.log("onFirstFocus");
42454         this.editorcore.onFirstFocus();
42455          for (var i =0; i < this.toolbars.length;i++) {
42456             this.toolbars[i].onFirstFocus();
42457         }
42458         
42459     }
42460      
42461     
42462     // hide stuff that is not compatible
42463     /**
42464      * @event blur
42465      * @hide
42466      */
42467     /**
42468      * @event change
42469      * @hide
42470      */
42471     /**
42472      * @event focus
42473      * @hide
42474      */
42475     /**
42476      * @event specialkey
42477      * @hide
42478      */
42479     /**
42480      * @cfg {String} fieldClass @hide
42481      */
42482     /**
42483      * @cfg {String} focusClass @hide
42484      */
42485     /**
42486      * @cfg {String} autoCreate @hide
42487      */
42488     /**
42489      * @cfg {String} inputType @hide
42490      */
42491     /**
42492      * @cfg {String} invalidClass @hide
42493      */
42494     /**
42495      * @cfg {String} invalidText @hide
42496      */
42497     /**
42498      * @cfg {String} msgFx @hide
42499      */
42500     /**
42501      * @cfg {String} validateOnBlur @hide
42502      */
42503 });
42504  
42505     // <script type="text/javascript">
42506 /*
42507  * Based on
42508  * Ext JS Library 1.1.1
42509  * Copyright(c) 2006-2007, Ext JS, LLC.
42510  *  
42511  
42512  */
42513
42514 /**
42515  * @class Roo.form.HtmlEditorToolbar1
42516  * Basic Toolbar
42517  * 
42518  * Usage:
42519  *
42520  new Roo.form.HtmlEditor({
42521     ....
42522     toolbars : [
42523         new Roo.form.HtmlEditorToolbar1({
42524             disable : { fonts: 1 , format: 1, ..., ... , ...],
42525             btns : [ .... ]
42526         })
42527     }
42528      
42529  * 
42530  * @cfg {Object} disable List of elements to disable..
42531  * @cfg {Array} btns List of additional buttons.
42532  * 
42533  * 
42534  * NEEDS Extra CSS? 
42535  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42536  */
42537  
42538 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42539 {
42540     
42541     Roo.apply(this, config);
42542     
42543     // default disabled, based on 'good practice'..
42544     this.disable = this.disable || {};
42545     Roo.applyIf(this.disable, {
42546         fontSize : true,
42547         colors : true,
42548         specialElements : true
42549     });
42550     
42551     
42552     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42553     // dont call parent... till later.
42554 }
42555
42556 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42557     
42558     tb: false,
42559     
42560     rendered: false,
42561     
42562     editor : false,
42563     editorcore : false,
42564     /**
42565      * @cfg {Object} disable  List of toolbar elements to disable
42566          
42567      */
42568     disable : false,
42569     
42570     
42571      /**
42572      * @cfg {String} createLinkText The default text for the create link prompt
42573      */
42574     createLinkText : 'Please enter the URL for the link:',
42575     /**
42576      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42577      */
42578     defaultLinkValue : 'http:/'+'/',
42579    
42580     
42581       /**
42582      * @cfg {Array} fontFamilies An array of available font families
42583      */
42584     fontFamilies : [
42585         'Arial',
42586         'Courier New',
42587         'Tahoma',
42588         'Times New Roman',
42589         'Verdana'
42590     ],
42591     
42592     specialChars : [
42593            "&#169;",
42594           "&#174;",     
42595           "&#8482;",    
42596           "&#163;" ,    
42597          // "&#8212;",    
42598           "&#8230;",    
42599           "&#247;" ,    
42600         //  "&#225;" ,     ?? a acute?
42601            "&#8364;"    , //Euro
42602        //   "&#8220;"    ,
42603         //  "&#8221;"    ,
42604         //  "&#8226;"    ,
42605           "&#176;"  //   , // degrees
42606
42607          // "&#233;"     , // e ecute
42608          // "&#250;"     , // u ecute?
42609     ],
42610     
42611     specialElements : [
42612         {
42613             text: "Insert Table",
42614             xtype: 'MenuItem',
42615             xns : Roo.Menu,
42616             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42617                 
42618         },
42619         {    
42620             text: "Insert Image",
42621             xtype: 'MenuItem',
42622             xns : Roo.Menu,
42623             ihtml : '<img src="about:blank"/>'
42624             
42625         }
42626         
42627          
42628     ],
42629     
42630     
42631     inputElements : [ 
42632             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42633             "input:submit", "input:button", "select", "textarea", "label" ],
42634     formats : [
42635         ["p"] ,  
42636         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42637         ["pre"],[ "code"], 
42638         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42639         ['div'],['span']
42640     ],
42641     
42642     cleanStyles : [
42643         "font-size"
42644     ],
42645      /**
42646      * @cfg {String} defaultFont default font to use.
42647      */
42648     defaultFont: 'tahoma',
42649    
42650     fontSelect : false,
42651     
42652     
42653     formatCombo : false,
42654     
42655     init : function(editor)
42656     {
42657         this.editor = editor;
42658         this.editorcore = editor.editorcore ? editor.editorcore : editor;
42659         var editorcore = this.editorcore;
42660         
42661         var _t = this;
42662         
42663         var fid = editorcore.frameId;
42664         var etb = this;
42665         function btn(id, toggle, handler){
42666             var xid = fid + '-'+ id ;
42667             return {
42668                 id : xid,
42669                 cmd : id,
42670                 cls : 'x-btn-icon x-edit-'+id,
42671                 enableToggle:toggle !== false,
42672                 scope: _t, // was editor...
42673                 handler:handler||_t.relayBtnCmd,
42674                 clickEvent:'mousedown',
42675                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42676                 tabIndex:-1
42677             };
42678         }
42679         
42680         
42681         
42682         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42683         this.tb = tb;
42684          // stop form submits
42685         tb.el.on('click', function(e){
42686             e.preventDefault(); // what does this do?
42687         });
42688
42689         if(!this.disable.font) { // && !Roo.isSafari){
42690             /* why no safari for fonts 
42691             editor.fontSelect = tb.el.createChild({
42692                 tag:'select',
42693                 tabIndex: -1,
42694                 cls:'x-font-select',
42695                 html: this.createFontOptions()
42696             });
42697             
42698             editor.fontSelect.on('change', function(){
42699                 var font = editor.fontSelect.dom.value;
42700                 editor.relayCmd('fontname', font);
42701                 editor.deferFocus();
42702             }, editor);
42703             
42704             tb.add(
42705                 editor.fontSelect.dom,
42706                 '-'
42707             );
42708             */
42709             
42710         };
42711         if(!this.disable.formats){
42712             this.formatCombo = new Roo.form.ComboBox({
42713                 store: new Roo.data.SimpleStore({
42714                     id : 'tag',
42715                     fields: ['tag'],
42716                     data : this.formats // from states.js
42717                 }),
42718                 blockFocus : true,
42719                 name : '',
42720                 //autoCreate : {tag: "div",  size: "20"},
42721                 displayField:'tag',
42722                 typeAhead: false,
42723                 mode: 'local',
42724                 editable : false,
42725                 triggerAction: 'all',
42726                 emptyText:'Add tag',
42727                 selectOnFocus:true,
42728                 width:135,
42729                 listeners : {
42730                     'select': function(c, r, i) {
42731                         editorcore.insertTag(r.get('tag'));
42732                         editor.focus();
42733                     }
42734                 }
42735
42736             });
42737             tb.addField(this.formatCombo);
42738             
42739         }
42740         
42741         if(!this.disable.format){
42742             tb.add(
42743                 btn('bold'),
42744                 btn('italic'),
42745                 btn('underline')
42746             );
42747         };
42748         if(!this.disable.fontSize){
42749             tb.add(
42750                 '-',
42751                 
42752                 
42753                 btn('increasefontsize', false, editorcore.adjustFont),
42754                 btn('decreasefontsize', false, editorcore.adjustFont)
42755             );
42756         };
42757         
42758         
42759         if(!this.disable.colors){
42760             tb.add(
42761                 '-', {
42762                     id:editorcore.frameId +'-forecolor',
42763                     cls:'x-btn-icon x-edit-forecolor',
42764                     clickEvent:'mousedown',
42765                     tooltip: this.buttonTips['forecolor'] || undefined,
42766                     tabIndex:-1,
42767                     menu : new Roo.menu.ColorMenu({
42768                         allowReselect: true,
42769                         focus: Roo.emptyFn,
42770                         value:'000000',
42771                         plain:true,
42772                         selectHandler: function(cp, color){
42773                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42774                             editor.deferFocus();
42775                         },
42776                         scope: editorcore,
42777                         clickEvent:'mousedown'
42778                     })
42779                 }, {
42780                     id:editorcore.frameId +'backcolor',
42781                     cls:'x-btn-icon x-edit-backcolor',
42782                     clickEvent:'mousedown',
42783                     tooltip: this.buttonTips['backcolor'] || undefined,
42784                     tabIndex:-1,
42785                     menu : new Roo.menu.ColorMenu({
42786                         focus: Roo.emptyFn,
42787                         value:'FFFFFF',
42788                         plain:true,
42789                         allowReselect: true,
42790                         selectHandler: function(cp, color){
42791                             if(Roo.isGecko){
42792                                 editorcore.execCmd('useCSS', false);
42793                                 editorcore.execCmd('hilitecolor', color);
42794                                 editorcore.execCmd('useCSS', true);
42795                                 editor.deferFocus();
42796                             }else{
42797                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42798                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42799                                 editor.deferFocus();
42800                             }
42801                         },
42802                         scope:editorcore,
42803                         clickEvent:'mousedown'
42804                     })
42805                 }
42806             );
42807         };
42808         // now add all the items...
42809         
42810
42811         if(!this.disable.alignments){
42812             tb.add(
42813                 '-',
42814                 btn('justifyleft'),
42815                 btn('justifycenter'),
42816                 btn('justifyright')
42817             );
42818         };
42819
42820         //if(!Roo.isSafari){
42821             if(!this.disable.links){
42822                 tb.add(
42823                     '-',
42824                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
42825                 );
42826             };
42827
42828             if(!this.disable.lists){
42829                 tb.add(
42830                     '-',
42831                     btn('insertorderedlist'),
42832                     btn('insertunorderedlist')
42833                 );
42834             }
42835             if(!this.disable.sourceEdit){
42836                 tb.add(
42837                     '-',
42838                     btn('sourceedit', true, function(btn){
42839                         Roo.log(this);
42840                         this.toggleSourceEdit(btn.pressed);
42841                     })
42842                 );
42843             }
42844         //}
42845         
42846         var smenu = { };
42847         // special menu.. - needs to be tidied up..
42848         if (!this.disable.special) {
42849             smenu = {
42850                 text: "&#169;",
42851                 cls: 'x-edit-none',
42852                 
42853                 menu : {
42854                     items : []
42855                 }
42856             };
42857             for (var i =0; i < this.specialChars.length; i++) {
42858                 smenu.menu.items.push({
42859                     
42860                     html: this.specialChars[i],
42861                     handler: function(a,b) {
42862                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42863                         //editor.insertAtCursor(a.html);
42864                         
42865                     },
42866                     tabIndex:-1
42867                 });
42868             }
42869             
42870             
42871             tb.add(smenu);
42872             
42873             
42874         }
42875         
42876         var cmenu = { };
42877         if (!this.disable.cleanStyles) {
42878             cmenu = {
42879                 cls: 'x-btn-icon x-btn-clear',
42880                 
42881                 menu : {
42882                     items : []
42883                 }
42884             };
42885             for (var i =0; i < this.cleanStyles.length; i++) {
42886                 cmenu.menu.items.push({
42887                     actiontype : this.cleanStyles[i],
42888                     html: 'Remove ' + this.cleanStyles[i],
42889                     handler: function(a,b) {
42890                         Roo.log(a);
42891                         Roo.log(b);
42892                         var c = Roo.get(editorcore.doc.body);
42893                         c.select('[style]').each(function(s) {
42894                             s.dom.style.removeProperty(a.actiontype);
42895                         });
42896                         
42897                     },
42898                     tabIndex:-1
42899                 });
42900             }
42901             
42902             tb.add(cmenu);
42903         }
42904          
42905         if (!this.disable.specialElements) {
42906             var semenu = {
42907                 text: "Other;",
42908                 cls: 'x-edit-none',
42909                 menu : {
42910                     items : []
42911                 }
42912             };
42913             for (var i =0; i < this.specialElements.length; i++) {
42914                 semenu.menu.items.push(
42915                     Roo.apply({ 
42916                         handler: function(a,b) {
42917                             editor.insertAtCursor(this.ihtml);
42918                         }
42919                     }, this.specialElements[i])
42920                 );
42921                     
42922             }
42923             
42924             tb.add(semenu);
42925             
42926             
42927         }
42928          
42929         
42930         if (this.btns) {
42931             for(var i =0; i< this.btns.length;i++) {
42932                 var b = Roo.factory(this.btns[i],Roo.form);
42933                 b.cls =  'x-edit-none';
42934                 b.scope = editorcore;
42935                 tb.add(b);
42936             }
42937         
42938         }
42939         
42940         
42941         
42942         // disable everything...
42943         
42944         this.tb.items.each(function(item){
42945            if(item.id != editorcore.frameId+ '-sourceedit'){
42946                 item.disable();
42947             }
42948         });
42949         this.rendered = true;
42950         
42951         // the all the btns;
42952         editor.on('editorevent', this.updateToolbar, this);
42953         // other toolbars need to implement this..
42954         //editor.on('editmodechange', this.updateToolbar, this);
42955     },
42956     
42957     
42958     relayBtnCmd : function(btn) {
42959         this.editorcore.relayCmd(btn.cmd);
42960     },
42961     // private used internally
42962     createLink : function(){
42963         Roo.log("create link?");
42964         var url = prompt(this.createLinkText, this.defaultLinkValue);
42965         if(url && url != 'http:/'+'/'){
42966             this.editorcore.relayCmd('createlink', url);
42967         }
42968     },
42969
42970     
42971     /**
42972      * Protected method that will not generally be called directly. It triggers
42973      * a toolbar update by reading the markup state of the current selection in the editor.
42974      */
42975     updateToolbar: function(){
42976
42977         if(!this.editorcore.activated){
42978             this.editor.onFirstFocus();
42979             return;
42980         }
42981
42982         var btns = this.tb.items.map, 
42983             doc = this.editorcore.doc,
42984             frameId = this.editorcore.frameId;
42985
42986         if(!this.disable.font && !Roo.isSafari){
42987             /*
42988             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42989             if(name != this.fontSelect.dom.value){
42990                 this.fontSelect.dom.value = name;
42991             }
42992             */
42993         }
42994         if(!this.disable.format){
42995             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42996             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42997             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42998         }
42999         if(!this.disable.alignments){
43000             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43001             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43002             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43003         }
43004         if(!Roo.isSafari && !this.disable.lists){
43005             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43006             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43007         }
43008         
43009         var ans = this.editorcore.getAllAncestors();
43010         if (this.formatCombo) {
43011             
43012             
43013             var store = this.formatCombo.store;
43014             this.formatCombo.setValue("");
43015             for (var i =0; i < ans.length;i++) {
43016                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43017                     // select it..
43018                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43019                     break;
43020                 }
43021             }
43022         }
43023         
43024         
43025         
43026         // hides menus... - so this cant be on a menu...
43027         Roo.menu.MenuMgr.hideAll();
43028
43029         //this.editorsyncValue();
43030     },
43031    
43032     
43033     createFontOptions : function(){
43034         var buf = [], fs = this.fontFamilies, ff, lc;
43035         
43036         
43037         
43038         for(var i = 0, len = fs.length; i< len; i++){
43039             ff = fs[i];
43040             lc = ff.toLowerCase();
43041             buf.push(
43042                 '<option value="',lc,'" style="font-family:',ff,';"',
43043                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43044                     ff,
43045                 '</option>'
43046             );
43047         }
43048         return buf.join('');
43049     },
43050     
43051     toggleSourceEdit : function(sourceEditMode){
43052         
43053         Roo.log("toolbar toogle");
43054         if(sourceEditMode === undefined){
43055             sourceEditMode = !this.sourceEditMode;
43056         }
43057         this.sourceEditMode = sourceEditMode === true;
43058         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43059         // just toggle the button?
43060         if(btn.pressed !== this.sourceEditMode){
43061             btn.toggle(this.sourceEditMode);
43062             return;
43063         }
43064         
43065         if(sourceEditMode){
43066             Roo.log("disabling buttons");
43067             this.tb.items.each(function(item){
43068                 if(item.cmd != 'sourceedit'){
43069                     item.disable();
43070                 }
43071             });
43072           
43073         }else{
43074             Roo.log("enabling buttons");
43075             if(this.editorcore.initialized){
43076                 this.tb.items.each(function(item){
43077                     item.enable();
43078                 });
43079             }
43080             
43081         }
43082         Roo.log("calling toggole on editor");
43083         // tell the editor that it's been pressed..
43084         this.editor.toggleSourceEdit(sourceEditMode);
43085        
43086     },
43087      /**
43088      * Object collection of toolbar tooltips for the buttons in the editor. The key
43089      * is the command id associated with that button and the value is a valid QuickTips object.
43090      * For example:
43091 <pre><code>
43092 {
43093     bold : {
43094         title: 'Bold (Ctrl+B)',
43095         text: 'Make the selected text bold.',
43096         cls: 'x-html-editor-tip'
43097     },
43098     italic : {
43099         title: 'Italic (Ctrl+I)',
43100         text: 'Make the selected text italic.',
43101         cls: 'x-html-editor-tip'
43102     },
43103     ...
43104 </code></pre>
43105     * @type Object
43106      */
43107     buttonTips : {
43108         bold : {
43109             title: 'Bold (Ctrl+B)',
43110             text: 'Make the selected text bold.',
43111             cls: 'x-html-editor-tip'
43112         },
43113         italic : {
43114             title: 'Italic (Ctrl+I)',
43115             text: 'Make the selected text italic.',
43116             cls: 'x-html-editor-tip'
43117         },
43118         underline : {
43119             title: 'Underline (Ctrl+U)',
43120             text: 'Underline the selected text.',
43121             cls: 'x-html-editor-tip'
43122         },
43123         increasefontsize : {
43124             title: 'Grow Text',
43125             text: 'Increase the font size.',
43126             cls: 'x-html-editor-tip'
43127         },
43128         decreasefontsize : {
43129             title: 'Shrink Text',
43130             text: 'Decrease the font size.',
43131             cls: 'x-html-editor-tip'
43132         },
43133         backcolor : {
43134             title: 'Text Highlight Color',
43135             text: 'Change the background color of the selected text.',
43136             cls: 'x-html-editor-tip'
43137         },
43138         forecolor : {
43139             title: 'Font Color',
43140             text: 'Change the color of the selected text.',
43141             cls: 'x-html-editor-tip'
43142         },
43143         justifyleft : {
43144             title: 'Align Text Left',
43145             text: 'Align text to the left.',
43146             cls: 'x-html-editor-tip'
43147         },
43148         justifycenter : {
43149             title: 'Center Text',
43150             text: 'Center text in the editor.',
43151             cls: 'x-html-editor-tip'
43152         },
43153         justifyright : {
43154             title: 'Align Text Right',
43155             text: 'Align text to the right.',
43156             cls: 'x-html-editor-tip'
43157         },
43158         insertunorderedlist : {
43159             title: 'Bullet List',
43160             text: 'Start a bulleted list.',
43161             cls: 'x-html-editor-tip'
43162         },
43163         insertorderedlist : {
43164             title: 'Numbered List',
43165             text: 'Start a numbered list.',
43166             cls: 'x-html-editor-tip'
43167         },
43168         createlink : {
43169             title: 'Hyperlink',
43170             text: 'Make the selected text a hyperlink.',
43171             cls: 'x-html-editor-tip'
43172         },
43173         sourceedit : {
43174             title: 'Source Edit',
43175             text: 'Switch to source editing mode.',
43176             cls: 'x-html-editor-tip'
43177         }
43178     },
43179     // private
43180     onDestroy : function(){
43181         if(this.rendered){
43182             
43183             this.tb.items.each(function(item){
43184                 if(item.menu){
43185                     item.menu.removeAll();
43186                     if(item.menu.el){
43187                         item.menu.el.destroy();
43188                     }
43189                 }
43190                 item.destroy();
43191             });
43192              
43193         }
43194     },
43195     onFirstFocus: function() {
43196         this.tb.items.each(function(item){
43197            item.enable();
43198         });
43199     }
43200 });
43201
43202
43203
43204
43205 // <script type="text/javascript">
43206 /*
43207  * Based on
43208  * Ext JS Library 1.1.1
43209  * Copyright(c) 2006-2007, Ext JS, LLC.
43210  *  
43211  
43212  */
43213
43214  
43215 /**
43216  * @class Roo.form.HtmlEditor.ToolbarContext
43217  * Context Toolbar
43218  * 
43219  * Usage:
43220  *
43221  new Roo.form.HtmlEditor({
43222     ....
43223     toolbars : [
43224         { xtype: 'ToolbarStandard', styles : {} }
43225         { xtype: 'ToolbarContext', disable : {} }
43226     ]
43227 })
43228
43229      
43230  * 
43231  * @config : {Object} disable List of elements to disable.. (not done yet.)
43232  * @config : {Object} styles  Map of styles available.
43233  * 
43234  */
43235
43236 Roo.form.HtmlEditor.ToolbarContext = function(config)
43237 {
43238     
43239     Roo.apply(this, config);
43240     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43241     // dont call parent... till later.
43242     this.styles = this.styles || {};
43243 }
43244
43245  
43246
43247 Roo.form.HtmlEditor.ToolbarContext.types = {
43248     'IMG' : {
43249         width : {
43250             title: "Width",
43251             width: 40
43252         },
43253         height:  {
43254             title: "Height",
43255             width: 40
43256         },
43257         align: {
43258             title: "Align",
43259             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43260             width : 80
43261             
43262         },
43263         border: {
43264             title: "Border",
43265             width: 40
43266         },
43267         alt: {
43268             title: "Alt",
43269             width: 120
43270         },
43271         src : {
43272             title: "Src",
43273             width: 220
43274         }
43275         
43276     },
43277     'A' : {
43278         name : {
43279             title: "Name",
43280             width: 50
43281         },
43282         target:  {
43283             title: "Target",
43284             width: 120
43285         },
43286         href:  {
43287             title: "Href",
43288             width: 220
43289         } // border?
43290         
43291     },
43292     'TABLE' : {
43293         rows : {
43294             title: "Rows",
43295             width: 20
43296         },
43297         cols : {
43298             title: "Cols",
43299             width: 20
43300         },
43301         width : {
43302             title: "Width",
43303             width: 40
43304         },
43305         height : {
43306             title: "Height",
43307             width: 40
43308         },
43309         border : {
43310             title: "Border",
43311             width: 20
43312         }
43313     },
43314     'TD' : {
43315         width : {
43316             title: "Width",
43317             width: 40
43318         },
43319         height : {
43320             title: "Height",
43321             width: 40
43322         },   
43323         align: {
43324             title: "Align",
43325             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43326             width: 80
43327         },
43328         valign: {
43329             title: "Valign",
43330             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43331             width: 80
43332         },
43333         colspan: {
43334             title: "Colspan",
43335             width: 20
43336             
43337         },
43338          'font-family'  : {
43339             title : "Font",
43340             style : 'fontFamily',
43341             displayField: 'display',
43342             optname : 'font-family',
43343             width: 140
43344         }
43345     },
43346     'INPUT' : {
43347         name : {
43348             title: "name",
43349             width: 120
43350         },
43351         value : {
43352             title: "Value",
43353             width: 120
43354         },
43355         width : {
43356             title: "Width",
43357             width: 40
43358         }
43359     },
43360     'LABEL' : {
43361         'for' : {
43362             title: "For",
43363             width: 120
43364         }
43365     },
43366     'TEXTAREA' : {
43367           name : {
43368             title: "name",
43369             width: 120
43370         },
43371         rows : {
43372             title: "Rows",
43373             width: 20
43374         },
43375         cols : {
43376             title: "Cols",
43377             width: 20
43378         }
43379     },
43380     'SELECT' : {
43381         name : {
43382             title: "name",
43383             width: 120
43384         },
43385         selectoptions : {
43386             title: "Options",
43387             width: 200
43388         }
43389     },
43390     
43391     // should we really allow this??
43392     // should this just be 
43393     'BODY' : {
43394         title : {
43395             title: "Title",
43396             width: 200,
43397             disabled : true
43398         }
43399     },
43400     'SPAN' : {
43401         'font-family'  : {
43402             title : "Font",
43403             style : 'fontFamily',
43404             displayField: 'display',
43405             optname : 'font-family',
43406             width: 140
43407         }
43408     },
43409     'DIV' : {
43410         'font-family'  : {
43411             title : "Font",
43412             style : 'fontFamily',
43413             displayField: 'display',
43414             optname : 'font-family',
43415             width: 140
43416         }
43417     },
43418      'P' : {
43419         'font-family'  : {
43420             title : "Font",
43421             style : 'fontFamily',
43422             displayField: 'display',
43423             optname : 'font-family',
43424             width: 140
43425         }
43426     },
43427     
43428     '*' : {
43429         // empty..
43430     }
43431
43432 };
43433
43434 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43435 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43436
43437 Roo.form.HtmlEditor.ToolbarContext.options = {
43438         'font-family'  : [ 
43439                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43440                 [ 'Courier New', 'Courier New'],
43441                 [ 'Tahoma', 'Tahoma'],
43442                 [ 'Times New Roman,serif', 'Times'],
43443                 [ 'Verdana','Verdana' ]
43444         ]
43445 };
43446
43447 // fixme - these need to be configurable..
43448  
43449
43450 Roo.form.HtmlEditor.ToolbarContext.types
43451
43452
43453 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43454     
43455     tb: false,
43456     
43457     rendered: false,
43458     
43459     editor : false,
43460     editorcore : false,
43461     /**
43462      * @cfg {Object} disable  List of toolbar elements to disable
43463          
43464      */
43465     disable : false,
43466     /**
43467      * @cfg {Object} styles List of styles 
43468      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43469      *
43470      * These must be defined in the page, so they get rendered correctly..
43471      * .headline { }
43472      * TD.underline { }
43473      * 
43474      */
43475     styles : false,
43476     
43477     options: false,
43478     
43479     toolbars : false,
43480     
43481     init : function(editor)
43482     {
43483         this.editor = editor;
43484         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43485         var editorcore = this.editorcore;
43486         
43487         var fid = editorcore.frameId;
43488         var etb = this;
43489         function btn(id, toggle, handler){
43490             var xid = fid + '-'+ id ;
43491             return {
43492                 id : xid,
43493                 cmd : id,
43494                 cls : 'x-btn-icon x-edit-'+id,
43495                 enableToggle:toggle !== false,
43496                 scope: editorcore, // was editor...
43497                 handler:handler||editorcore.relayBtnCmd,
43498                 clickEvent:'mousedown',
43499                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43500                 tabIndex:-1
43501             };
43502         }
43503         // create a new element.
43504         var wdiv = editor.wrap.createChild({
43505                 tag: 'div'
43506             }, editor.wrap.dom.firstChild.nextSibling, true);
43507         
43508         // can we do this more than once??
43509         
43510          // stop form submits
43511       
43512  
43513         // disable everything...
43514         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43515         this.toolbars = {};
43516            
43517         for (var i in  ty) {
43518           
43519             this.toolbars[i] = this.buildToolbar(ty[i],i);
43520         }
43521         this.tb = this.toolbars.BODY;
43522         this.tb.el.show();
43523         this.buildFooter();
43524         this.footer.show();
43525         editor.on('hide', function( ) { this.footer.hide() }, this);
43526         editor.on('show', function( ) { this.footer.show() }, this);
43527         
43528          
43529         this.rendered = true;
43530         
43531         // the all the btns;
43532         editor.on('editorevent', this.updateToolbar, this);
43533         // other toolbars need to implement this..
43534         //editor.on('editmodechange', this.updateToolbar, this);
43535     },
43536     
43537     
43538     
43539     /**
43540      * Protected method that will not generally be called directly. It triggers
43541      * a toolbar update by reading the markup state of the current selection in the editor.
43542      */
43543     updateToolbar: function(editor,ev,sel){
43544
43545         //Roo.log(ev);
43546         // capture mouse up - this is handy for selecting images..
43547         // perhaps should go somewhere else...
43548         if(!this.editorcore.activated){
43549              this.editor.onFirstFocus();
43550             return;
43551         }
43552         
43553         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43554         // selectNode - might want to handle IE?
43555         if (ev &&
43556             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43557             ev.target && ev.target.tagName == 'IMG') {
43558             // they have click on an image...
43559             // let's see if we can change the selection...
43560             sel = ev.target;
43561          
43562               var nodeRange = sel.ownerDocument.createRange();
43563             try {
43564                 nodeRange.selectNode(sel);
43565             } catch (e) {
43566                 nodeRange.selectNodeContents(sel);
43567             }
43568             //nodeRange.collapse(true);
43569             var s = this.editorcore.win.getSelection();
43570             s.removeAllRanges();
43571             s.addRange(nodeRange);
43572         }  
43573         
43574       
43575         var updateFooter = sel ? false : true;
43576         
43577         
43578         var ans = this.editorcore.getAllAncestors();
43579         
43580         // pick
43581         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43582         
43583         if (!sel) { 
43584             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43585             sel = sel ? sel : this.editorcore.doc.body;
43586             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43587             
43588         }
43589         // pick a menu that exists..
43590         var tn = sel.tagName.toUpperCase();
43591         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43592         
43593         tn = sel.tagName.toUpperCase();
43594         
43595         var lastSel = this.tb.selectedNode
43596         
43597         this.tb.selectedNode = sel;
43598         
43599         // if current menu does not match..
43600         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43601                 
43602             this.tb.el.hide();
43603             ///console.log("show: " + tn);
43604             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43605             this.tb.el.show();
43606             // update name
43607             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43608             
43609             
43610             // update attributes
43611             if (this.tb.fields) {
43612                 this.tb.fields.each(function(e) {
43613                     if (e.stylename) {
43614                         e.setValue(sel.style[e.stylename]);
43615                         return;
43616                     } 
43617                    e.setValue(sel.getAttribute(e.attrname));
43618                 });
43619             }
43620             
43621             var hasStyles = false;
43622             for(var i in this.styles) {
43623                 hasStyles = true;
43624                 break;
43625             }
43626             
43627             // update styles
43628             if (hasStyles) { 
43629                 var st = this.tb.fields.item(0);
43630                 
43631                 st.store.removeAll();
43632                
43633                 
43634                 var cn = sel.className.split(/\s+/);
43635                 
43636                 var avs = [];
43637                 if (this.styles['*']) {
43638                     
43639                     Roo.each(this.styles['*'], function(v) {
43640                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43641                     });
43642                 }
43643                 if (this.styles[tn]) { 
43644                     Roo.each(this.styles[tn], function(v) {
43645                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43646                     });
43647                 }
43648                 
43649                 st.store.loadData(avs);
43650                 st.collapse();
43651                 st.setValue(cn);
43652             }
43653             // flag our selected Node.
43654             this.tb.selectedNode = sel;
43655            
43656            
43657             Roo.menu.MenuMgr.hideAll();
43658
43659         }
43660         
43661         if (!updateFooter) {
43662             //this.footDisp.dom.innerHTML = ''; 
43663             return;
43664         }
43665         // update the footer
43666         //
43667         var html = '';
43668         
43669         this.footerEls = ans.reverse();
43670         Roo.each(this.footerEls, function(a,i) {
43671             if (!a) { return; }
43672             html += html.length ? ' &gt; '  :  '';
43673             
43674             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43675             
43676         });
43677        
43678         // 
43679         var sz = this.footDisp.up('td').getSize();
43680         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43681         this.footDisp.dom.style.marginLeft = '5px';
43682         
43683         this.footDisp.dom.style.overflow = 'hidden';
43684         
43685         this.footDisp.dom.innerHTML = html;
43686             
43687         //this.editorsyncValue();
43688     },
43689      
43690     
43691    
43692        
43693     // private
43694     onDestroy : function(){
43695         if(this.rendered){
43696             
43697             this.tb.items.each(function(item){
43698                 if(item.menu){
43699                     item.menu.removeAll();
43700                     if(item.menu.el){
43701                         item.menu.el.destroy();
43702                     }
43703                 }
43704                 item.destroy();
43705             });
43706              
43707         }
43708     },
43709     onFirstFocus: function() {
43710         // need to do this for all the toolbars..
43711         this.tb.items.each(function(item){
43712            item.enable();
43713         });
43714     },
43715     buildToolbar: function(tlist, nm)
43716     {
43717         var editor = this.editor;
43718         var editorcore = this.editorcore;
43719          // create a new element.
43720         var wdiv = editor.wrap.createChild({
43721                 tag: 'div'
43722             }, editor.wrap.dom.firstChild.nextSibling, true);
43723         
43724        
43725         var tb = new Roo.Toolbar(wdiv);
43726         // add the name..
43727         
43728         tb.add(nm+ ":&nbsp;");
43729         
43730         var styles = [];
43731         for(var i in this.styles) {
43732             styles.push(i);
43733         }
43734         
43735         // styles...
43736         if (styles && styles.length) {
43737             
43738             // this needs a multi-select checkbox...
43739             tb.addField( new Roo.form.ComboBox({
43740                 store: new Roo.data.SimpleStore({
43741                     id : 'val',
43742                     fields: ['val', 'selected'],
43743                     data : [] 
43744                 }),
43745                 name : '-roo-edit-className',
43746                 attrname : 'className',
43747                 displayField: 'val',
43748                 typeAhead: false,
43749                 mode: 'local',
43750                 editable : false,
43751                 triggerAction: 'all',
43752                 emptyText:'Select Style',
43753                 selectOnFocus:true,
43754                 width: 130,
43755                 listeners : {
43756                     'select': function(c, r, i) {
43757                         // initial support only for on class per el..
43758                         tb.selectedNode.className =  r ? r.get('val') : '';
43759                         editorcore.syncValue();
43760                     }
43761                 }
43762     
43763             }));
43764         }
43765         
43766         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43767         var tbops = tbc.options;
43768         
43769         for (var i in tlist) {
43770             
43771             var item = tlist[i];
43772             tb.add(item.title + ":&nbsp;");
43773             
43774             
43775             //optname == used so you can configure the options available..
43776             var opts = item.opts ? item.opts : false;
43777             if (item.optname) {
43778                 opts = tbops[item.optname];
43779            
43780             }
43781             
43782             if (opts) {
43783                 // opts == pulldown..
43784                 tb.addField( new Roo.form.ComboBox({
43785                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43786                         id : 'val',
43787                         fields: ['val', 'display'],
43788                         data : opts  
43789                     }),
43790                     name : '-roo-edit-' + i,
43791                     attrname : i,
43792                     stylename : item.style ? item.style : false,
43793                     displayField: item.displayField ? item.displayField : 'val',
43794                     valueField :  'val',
43795                     typeAhead: false,
43796                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43797                     editable : false,
43798                     triggerAction: 'all',
43799                     emptyText:'Select',
43800                     selectOnFocus:true,
43801                     width: item.width ? item.width  : 130,
43802                     listeners : {
43803                         'select': function(c, r, i) {
43804                             if (c.stylename) {
43805                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43806                                 return;
43807                             }
43808                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43809                         }
43810                     }
43811
43812                 }));
43813                 continue;
43814                     
43815                  
43816                 
43817                 tb.addField( new Roo.form.TextField({
43818                     name: i,
43819                     width: 100,
43820                     //allowBlank:false,
43821                     value: ''
43822                 }));
43823                 continue;
43824             }
43825             tb.addField( new Roo.form.TextField({
43826                 name: '-roo-edit-' + i,
43827                 attrname : i,
43828                 
43829                 width: item.width,
43830                 //allowBlank:true,
43831                 value: '',
43832                 listeners: {
43833                     'change' : function(f, nv, ov) {
43834                         tb.selectedNode.setAttribute(f.attrname, nv);
43835                     }
43836                 }
43837             }));
43838              
43839         }
43840         tb.addFill();
43841         var _this = this;
43842         tb.addButton( {
43843             text: 'Remove Tag',
43844     
43845             listeners : {
43846                 click : function ()
43847                 {
43848                     // remove
43849                     // undo does not work.
43850                      
43851                     var sn = tb.selectedNode;
43852                     
43853                     var pn = sn.parentNode;
43854                     
43855                     var stn =  sn.childNodes[0];
43856                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43857                     while (sn.childNodes.length) {
43858                         var node = sn.childNodes[0];
43859                         sn.removeChild(node);
43860                         //Roo.log(node);
43861                         pn.insertBefore(node, sn);
43862                         
43863                     }
43864                     pn.removeChild(sn);
43865                     var range = editorcore.createRange();
43866         
43867                     range.setStart(stn,0);
43868                     range.setEnd(en,0); //????
43869                     //range.selectNode(sel);
43870                     
43871                     
43872                     var selection = editorcore.getSelection();
43873                     selection.removeAllRanges();
43874                     selection.addRange(range);
43875                     
43876                     
43877                     
43878                     //_this.updateToolbar(null, null, pn);
43879                     _this.updateToolbar(null, null, null);
43880                     _this.footDisp.dom.innerHTML = ''; 
43881                 }
43882             }
43883             
43884                     
43885                 
43886             
43887         });
43888         
43889         
43890         tb.el.on('click', function(e){
43891             e.preventDefault(); // what does this do?
43892         });
43893         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43894         tb.el.hide();
43895         tb.name = nm;
43896         // dont need to disable them... as they will get hidden
43897         return tb;
43898          
43899         
43900     },
43901     buildFooter : function()
43902     {
43903         
43904         var fel = this.editor.wrap.createChild();
43905         this.footer = new Roo.Toolbar(fel);
43906         // toolbar has scrolly on left / right?
43907         var footDisp= new Roo.Toolbar.Fill();
43908         var _t = this;
43909         this.footer.add(
43910             {
43911                 text : '&lt;',
43912                 xtype: 'Button',
43913                 handler : function() {
43914                     _t.footDisp.scrollTo('left',0,true)
43915                 }
43916             }
43917         );
43918         this.footer.add( footDisp );
43919         this.footer.add( 
43920             {
43921                 text : '&gt;',
43922                 xtype: 'Button',
43923                 handler : function() {
43924                     // no animation..
43925                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43926                 }
43927             }
43928         );
43929         var fel = Roo.get(footDisp.el);
43930         fel.addClass('x-editor-context');
43931         this.footDispWrap = fel; 
43932         this.footDispWrap.overflow  = 'hidden';
43933         
43934         this.footDisp = fel.createChild();
43935         this.footDispWrap.on('click', this.onContextClick, this)
43936         
43937         
43938     },
43939     onContextClick : function (ev,dom)
43940     {
43941         ev.preventDefault();
43942         var  cn = dom.className;
43943         //Roo.log(cn);
43944         if (!cn.match(/x-ed-loc-/)) {
43945             return;
43946         }
43947         var n = cn.split('-').pop();
43948         var ans = this.footerEls;
43949         var sel = ans[n];
43950         
43951          // pick
43952         var range = this.editorcore.createRange();
43953         
43954         range.selectNodeContents(sel);
43955         //range.selectNode(sel);
43956         
43957         
43958         var selection = this.editorcore.getSelection();
43959         selection.removeAllRanges();
43960         selection.addRange(range);
43961         
43962         
43963         
43964         this.updateToolbar(null, null, sel);
43965         
43966         
43967     }
43968     
43969     
43970     
43971     
43972     
43973 });
43974
43975
43976
43977
43978
43979 /*
43980  * Based on:
43981  * Ext JS Library 1.1.1
43982  * Copyright(c) 2006-2007, Ext JS, LLC.
43983  *
43984  * Originally Released Under LGPL - original licence link has changed is not relivant.
43985  *
43986  * Fork - LGPL
43987  * <script type="text/javascript">
43988  */
43989  
43990 /**
43991  * @class Roo.form.BasicForm
43992  * @extends Roo.util.Observable
43993  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43994  * @constructor
43995  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43996  * @param {Object} config Configuration options
43997  */
43998 Roo.form.BasicForm = function(el, config){
43999     this.allItems = [];
44000     this.childForms = [];
44001     Roo.apply(this, config);
44002     /*
44003      * The Roo.form.Field items in this form.
44004      * @type MixedCollection
44005      */
44006      
44007      
44008     this.items = new Roo.util.MixedCollection(false, function(o){
44009         return o.id || (o.id = Roo.id());
44010     });
44011     this.addEvents({
44012         /**
44013          * @event beforeaction
44014          * Fires before any action is performed. Return false to cancel the action.
44015          * @param {Form} this
44016          * @param {Action} action The action to be performed
44017          */
44018         beforeaction: true,
44019         /**
44020          * @event actionfailed
44021          * Fires when an action fails.
44022          * @param {Form} this
44023          * @param {Action} action The action that failed
44024          */
44025         actionfailed : true,
44026         /**
44027          * @event actioncomplete
44028          * Fires when an action is completed.
44029          * @param {Form} this
44030          * @param {Action} action The action that completed
44031          */
44032         actioncomplete : true
44033     });
44034     if(el){
44035         this.initEl(el);
44036     }
44037     Roo.form.BasicForm.superclass.constructor.call(this);
44038 };
44039
44040 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44041     /**
44042      * @cfg {String} method
44043      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44044      */
44045     /**
44046      * @cfg {DataReader} reader
44047      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44048      * This is optional as there is built-in support for processing JSON.
44049      */
44050     /**
44051      * @cfg {DataReader} errorReader
44052      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44053      * This is completely optional as there is built-in support for processing JSON.
44054      */
44055     /**
44056      * @cfg {String} url
44057      * The URL to use for form actions if one isn't supplied in the action options.
44058      */
44059     /**
44060      * @cfg {Boolean} fileUpload
44061      * Set to true if this form is a file upload.
44062      */
44063      
44064     /**
44065      * @cfg {Object} baseParams
44066      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44067      */
44068      /**
44069      
44070     /**
44071      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44072      */
44073     timeout: 30,
44074
44075     // private
44076     activeAction : null,
44077
44078     /**
44079      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44080      * or setValues() data instead of when the form was first created.
44081      */
44082     trackResetOnLoad : false,
44083     
44084     
44085     /**
44086      * childForms - used for multi-tab forms
44087      * @type {Array}
44088      */
44089     childForms : false,
44090     
44091     /**
44092      * allItems - full list of fields.
44093      * @type {Array}
44094      */
44095     allItems : false,
44096     
44097     /**
44098      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44099      * element by passing it or its id or mask the form itself by passing in true.
44100      * @type Mixed
44101      */
44102     waitMsgTarget : false,
44103
44104     // private
44105     initEl : function(el){
44106         this.el = Roo.get(el);
44107         this.id = this.el.id || Roo.id();
44108         this.el.on('submit', this.onSubmit, this);
44109         this.el.addClass('x-form');
44110     },
44111
44112     // private
44113     onSubmit : function(e){
44114         e.stopEvent();
44115     },
44116
44117     /**
44118      * Returns true if client-side validation on the form is successful.
44119      * @return Boolean
44120      */
44121     isValid : function(){
44122         var valid = true;
44123         this.items.each(function(f){
44124            if(!f.validate()){
44125                valid = false;
44126            }
44127         });
44128         return valid;
44129     },
44130
44131     /**
44132      * Returns true if any fields in this form have changed since their original load.
44133      * @return Boolean
44134      */
44135     isDirty : function(){
44136         var dirty = false;
44137         this.items.each(function(f){
44138            if(f.isDirty()){
44139                dirty = true;
44140                return false;
44141            }
44142         });
44143         return dirty;
44144     },
44145
44146     /**
44147      * Performs a predefined action (submit or load) or custom actions you define on this form.
44148      * @param {String} actionName The name of the action type
44149      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44150      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44151      * accept other config options):
44152      * <pre>
44153 Property          Type             Description
44154 ----------------  ---------------  ----------------------------------------------------------------------------------
44155 url               String           The url for the action (defaults to the form's url)
44156 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44157 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44158 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44159                                    validate the form on the client (defaults to false)
44160      * </pre>
44161      * @return {BasicForm} this
44162      */
44163     doAction : function(action, options){
44164         if(typeof action == 'string'){
44165             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44166         }
44167         if(this.fireEvent('beforeaction', this, action) !== false){
44168             this.beforeAction(action);
44169             action.run.defer(100, action);
44170         }
44171         return this;
44172     },
44173
44174     /**
44175      * Shortcut to do a submit action.
44176      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44177      * @return {BasicForm} this
44178      */
44179     submit : function(options){
44180         this.doAction('submit', options);
44181         return this;
44182     },
44183
44184     /**
44185      * Shortcut to do a load action.
44186      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44187      * @return {BasicForm} this
44188      */
44189     load : function(options){
44190         this.doAction('load', options);
44191         return this;
44192     },
44193
44194     /**
44195      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44196      * @param {Record} record The record to edit
44197      * @return {BasicForm} this
44198      */
44199     updateRecord : function(record){
44200         record.beginEdit();
44201         var fs = record.fields;
44202         fs.each(function(f){
44203             var field = this.findField(f.name);
44204             if(field){
44205                 record.set(f.name, field.getValue());
44206             }
44207         }, this);
44208         record.endEdit();
44209         return this;
44210     },
44211
44212     /**
44213      * Loads an Roo.data.Record into this form.
44214      * @param {Record} record The record to load
44215      * @return {BasicForm} this
44216      */
44217     loadRecord : function(record){
44218         this.setValues(record.data);
44219         return this;
44220     },
44221
44222     // private
44223     beforeAction : function(action){
44224         var o = action.options;
44225         
44226        
44227         if(this.waitMsgTarget === true){
44228             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44229         }else if(this.waitMsgTarget){
44230             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44231             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44232         }else {
44233             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44234         }
44235          
44236     },
44237
44238     // private
44239     afterAction : function(action, success){
44240         this.activeAction = null;
44241         var o = action.options;
44242         
44243         if(this.waitMsgTarget === true){
44244             this.el.unmask();
44245         }else if(this.waitMsgTarget){
44246             this.waitMsgTarget.unmask();
44247         }else{
44248             Roo.MessageBox.updateProgress(1);
44249             Roo.MessageBox.hide();
44250         }
44251          
44252         if(success){
44253             if(o.reset){
44254                 this.reset();
44255             }
44256             Roo.callback(o.success, o.scope, [this, action]);
44257             this.fireEvent('actioncomplete', this, action);
44258             
44259         }else{
44260             
44261             // failure condition..
44262             // we have a scenario where updates need confirming.
44263             // eg. if a locking scenario exists..
44264             // we look for { errors : { needs_confirm : true }} in the response.
44265             if (
44266                 (typeof(action.result) != 'undefined')  &&
44267                 (typeof(action.result.errors) != 'undefined')  &&
44268                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44269            ){
44270                 var _t = this;
44271                 Roo.MessageBox.confirm(
44272                     "Change requires confirmation",
44273                     action.result.errorMsg,
44274                     function(r) {
44275                         if (r != 'yes') {
44276                             return;
44277                         }
44278                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44279                     }
44280                     
44281                 );
44282                 
44283                 
44284                 
44285                 return;
44286             }
44287             
44288             Roo.callback(o.failure, o.scope, [this, action]);
44289             // show an error message if no failed handler is set..
44290             if (!this.hasListener('actionfailed')) {
44291                 Roo.MessageBox.alert("Error",
44292                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44293                         action.result.errorMsg :
44294                         "Saving Failed, please check your entries or try again"
44295                 );
44296             }
44297             
44298             this.fireEvent('actionfailed', this, action);
44299         }
44300         
44301     },
44302
44303     /**
44304      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44305      * @param {String} id The value to search for
44306      * @return Field
44307      */
44308     findField : function(id){
44309         var field = this.items.get(id);
44310         if(!field){
44311             this.items.each(function(f){
44312                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44313                     field = f;
44314                     return false;
44315                 }
44316             });
44317         }
44318         return field || null;
44319     },
44320
44321     /**
44322      * Add a secondary form to this one, 
44323      * Used to provide tabbed forms. One form is primary, with hidden values 
44324      * which mirror the elements from the other forms.
44325      * 
44326      * @param {Roo.form.Form} form to add.
44327      * 
44328      */
44329     addForm : function(form)
44330     {
44331        
44332         if (this.childForms.indexOf(form) > -1) {
44333             // already added..
44334             return;
44335         }
44336         this.childForms.push(form);
44337         var n = '';
44338         Roo.each(form.allItems, function (fe) {
44339             
44340             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44341             if (this.findField(n)) { // already added..
44342                 return;
44343             }
44344             var add = new Roo.form.Hidden({
44345                 name : n
44346             });
44347             add.render(this.el);
44348             
44349             this.add( add );
44350         }, this);
44351         
44352     },
44353     /**
44354      * Mark fields in this form invalid in bulk.
44355      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44356      * @return {BasicForm} this
44357      */
44358     markInvalid : function(errors){
44359         if(errors instanceof Array){
44360             for(var i = 0, len = errors.length; i < len; i++){
44361                 var fieldError = errors[i];
44362                 var f = this.findField(fieldError.id);
44363                 if(f){
44364                     f.markInvalid(fieldError.msg);
44365                 }
44366             }
44367         }else{
44368             var field, id;
44369             for(id in errors){
44370                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44371                     field.markInvalid(errors[id]);
44372                 }
44373             }
44374         }
44375         Roo.each(this.childForms || [], function (f) {
44376             f.markInvalid(errors);
44377         });
44378         
44379         return this;
44380     },
44381
44382     /**
44383      * Set values for fields in this form in bulk.
44384      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44385      * @return {BasicForm} this
44386      */
44387     setValues : function(values){
44388         if(values instanceof Array){ // array of objects
44389             for(var i = 0, len = values.length; i < len; i++){
44390                 var v = values[i];
44391                 var f = this.findField(v.id);
44392                 if(f){
44393                     f.setValue(v.value);
44394                     if(this.trackResetOnLoad){
44395                         f.originalValue = f.getValue();
44396                     }
44397                 }
44398             }
44399         }else{ // object hash
44400             var field, id;
44401             for(id in values){
44402                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44403                     
44404                     if (field.setFromData && 
44405                         field.valueField && 
44406                         field.displayField &&
44407                         // combos' with local stores can 
44408                         // be queried via setValue()
44409                         // to set their value..
44410                         (field.store && !field.store.isLocal)
44411                         ) {
44412                         // it's a combo
44413                         var sd = { };
44414                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44415                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44416                         field.setFromData(sd);
44417                         
44418                     } else {
44419                         field.setValue(values[id]);
44420                     }
44421                     
44422                     
44423                     if(this.trackResetOnLoad){
44424                         field.originalValue = field.getValue();
44425                     }
44426                 }
44427             }
44428         }
44429          
44430         Roo.each(this.childForms || [], function (f) {
44431             f.setValues(values);
44432         });
44433                 
44434         return this;
44435     },
44436
44437     /**
44438      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44439      * they are returned as an array.
44440      * @param {Boolean} asString
44441      * @return {Object}
44442      */
44443     getValues : function(asString){
44444         if (this.childForms) {
44445             // copy values from the child forms
44446             Roo.each(this.childForms, function (f) {
44447                 this.setValues(f.getValues());
44448             }, this);
44449         }
44450         
44451         
44452         
44453         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44454         if(asString === true){
44455             return fs;
44456         }
44457         return Roo.urlDecode(fs);
44458     },
44459     
44460     /**
44461      * Returns the fields in this form as an object with key/value pairs. 
44462      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44463      * @return {Object}
44464      */
44465     getFieldValues : function(with_hidden)
44466     {
44467         if (this.childForms) {
44468             // copy values from the child forms
44469             // should this call getFieldValues - probably not as we do not currently copy
44470             // hidden fields when we generate..
44471             Roo.each(this.childForms, function (f) {
44472                 this.setValues(f.getValues());
44473             }, this);
44474         }
44475         
44476         var ret = {};
44477         this.items.each(function(f){
44478             if (!f.getName()) {
44479                 return;
44480             }
44481             var v = f.getValue();
44482             if (f.inputType =='radio') {
44483                 if (typeof(ret[f.getName()]) == 'undefined') {
44484                     ret[f.getName()] = ''; // empty..
44485                 }
44486                 
44487                 if (!f.el.dom.checked) {
44488                     return;
44489                     
44490                 }
44491                 v = f.el.dom.value;
44492                 
44493             }
44494             
44495             // not sure if this supported any more..
44496             if ((typeof(v) == 'object') && f.getRawValue) {
44497                 v = f.getRawValue() ; // dates..
44498             }
44499             // combo boxes where name != hiddenName...
44500             if (f.name != f.getName()) {
44501                 ret[f.name] = f.getRawValue();
44502             }
44503             ret[f.getName()] = v;
44504         });
44505         
44506         return ret;
44507     },
44508
44509     /**
44510      * Clears all invalid messages in this form.
44511      * @return {BasicForm} this
44512      */
44513     clearInvalid : function(){
44514         this.items.each(function(f){
44515            f.clearInvalid();
44516         });
44517         
44518         Roo.each(this.childForms || [], function (f) {
44519             f.clearInvalid();
44520         });
44521         
44522         
44523         return this;
44524     },
44525
44526     /**
44527      * Resets this form.
44528      * @return {BasicForm} this
44529      */
44530     reset : function(){
44531         this.items.each(function(f){
44532             f.reset();
44533         });
44534         
44535         Roo.each(this.childForms || [], function (f) {
44536             f.reset();
44537         });
44538        
44539         
44540         return this;
44541     },
44542
44543     /**
44544      * Add Roo.form components to this form.
44545      * @param {Field} field1
44546      * @param {Field} field2 (optional)
44547      * @param {Field} etc (optional)
44548      * @return {BasicForm} this
44549      */
44550     add : function(){
44551         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44552         return this;
44553     },
44554
44555
44556     /**
44557      * Removes a field from the items collection (does NOT remove its markup).
44558      * @param {Field} field
44559      * @return {BasicForm} this
44560      */
44561     remove : function(field){
44562         this.items.remove(field);
44563         return this;
44564     },
44565
44566     /**
44567      * Looks at the fields in this form, checks them for an id attribute,
44568      * and calls applyTo on the existing dom element with that id.
44569      * @return {BasicForm} this
44570      */
44571     render : function(){
44572         this.items.each(function(f){
44573             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44574                 f.applyTo(f.id);
44575             }
44576         });
44577         return this;
44578     },
44579
44580     /**
44581      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44582      * @param {Object} values
44583      * @return {BasicForm} this
44584      */
44585     applyToFields : function(o){
44586         this.items.each(function(f){
44587            Roo.apply(f, o);
44588         });
44589         return this;
44590     },
44591
44592     /**
44593      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44594      * @param {Object} values
44595      * @return {BasicForm} this
44596      */
44597     applyIfToFields : function(o){
44598         this.items.each(function(f){
44599            Roo.applyIf(f, o);
44600         });
44601         return this;
44602     }
44603 });
44604
44605 // back compat
44606 Roo.BasicForm = Roo.form.BasicForm;/*
44607  * Based on:
44608  * Ext JS Library 1.1.1
44609  * Copyright(c) 2006-2007, Ext JS, LLC.
44610  *
44611  * Originally Released Under LGPL - original licence link has changed is not relivant.
44612  *
44613  * Fork - LGPL
44614  * <script type="text/javascript">
44615  */
44616
44617 /**
44618  * @class Roo.form.Form
44619  * @extends Roo.form.BasicForm
44620  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44621  * @constructor
44622  * @param {Object} config Configuration options
44623  */
44624 Roo.form.Form = function(config){
44625     var xitems =  [];
44626     if (config.items) {
44627         xitems = config.items;
44628         delete config.items;
44629     }
44630    
44631     
44632     Roo.form.Form.superclass.constructor.call(this, null, config);
44633     this.url = this.url || this.action;
44634     if(!this.root){
44635         this.root = new Roo.form.Layout(Roo.applyIf({
44636             id: Roo.id()
44637         }, config));
44638     }
44639     this.active = this.root;
44640     /**
44641      * Array of all the buttons that have been added to this form via {@link addButton}
44642      * @type Array
44643      */
44644     this.buttons = [];
44645     this.allItems = [];
44646     this.addEvents({
44647         /**
44648          * @event clientvalidation
44649          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44650          * @param {Form} this
44651          * @param {Boolean} valid true if the form has passed client-side validation
44652          */
44653         clientvalidation: true,
44654         /**
44655          * @event rendered
44656          * Fires when the form is rendered
44657          * @param {Roo.form.Form} form
44658          */
44659         rendered : true
44660     });
44661     
44662     if (this.progressUrl) {
44663             // push a hidden field onto the list of fields..
44664             this.addxtype( {
44665                     xns: Roo.form, 
44666                     xtype : 'Hidden', 
44667                     name : 'UPLOAD_IDENTIFIER' 
44668             });
44669         }
44670         
44671     
44672     Roo.each(xitems, this.addxtype, this);
44673     
44674     
44675     
44676 };
44677
44678 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44679     /**
44680      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44681      */
44682     /**
44683      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44684      */
44685     /**
44686      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44687      */
44688     buttonAlign:'center',
44689
44690     /**
44691      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44692      */
44693     minButtonWidth:75,
44694
44695     /**
44696      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44697      * This property cascades to child containers if not set.
44698      */
44699     labelAlign:'left',
44700
44701     /**
44702      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44703      * fires a looping event with that state. This is required to bind buttons to the valid
44704      * state using the config value formBind:true on the button.
44705      */
44706     monitorValid : false,
44707
44708     /**
44709      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44710      */
44711     monitorPoll : 200,
44712     
44713     /**
44714      * @cfg {String} progressUrl - Url to return progress data 
44715      */
44716     
44717     progressUrl : false,
44718   
44719     /**
44720      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44721      * fields are added and the column is closed. If no fields are passed the column remains open
44722      * until end() is called.
44723      * @param {Object} config The config to pass to the column
44724      * @param {Field} field1 (optional)
44725      * @param {Field} field2 (optional)
44726      * @param {Field} etc (optional)
44727      * @return Column The column container object
44728      */
44729     column : function(c){
44730         var col = new Roo.form.Column(c);
44731         this.start(col);
44732         if(arguments.length > 1){ // duplicate code required because of Opera
44733             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44734             this.end();
44735         }
44736         return col;
44737     },
44738
44739     /**
44740      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44741      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44742      * until end() is called.
44743      * @param {Object} config The config to pass to the fieldset
44744      * @param {Field} field1 (optional)
44745      * @param {Field} field2 (optional)
44746      * @param {Field} etc (optional)
44747      * @return FieldSet The fieldset container object
44748      */
44749     fieldset : function(c){
44750         var fs = new Roo.form.FieldSet(c);
44751         this.start(fs);
44752         if(arguments.length > 1){ // duplicate code required because of Opera
44753             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44754             this.end();
44755         }
44756         return fs;
44757     },
44758
44759     /**
44760      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44761      * fields are added and the container is closed. If no fields are passed the container remains open
44762      * until end() is called.
44763      * @param {Object} config The config to pass to the Layout
44764      * @param {Field} field1 (optional)
44765      * @param {Field} field2 (optional)
44766      * @param {Field} etc (optional)
44767      * @return Layout The container object
44768      */
44769     container : function(c){
44770         var l = new Roo.form.Layout(c);
44771         this.start(l);
44772         if(arguments.length > 1){ // duplicate code required because of Opera
44773             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44774             this.end();
44775         }
44776         return l;
44777     },
44778
44779     /**
44780      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44781      * @param {Object} container A Roo.form.Layout or subclass of Layout
44782      * @return {Form} this
44783      */
44784     start : function(c){
44785         // cascade label info
44786         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44787         this.active.stack.push(c);
44788         c.ownerCt = this.active;
44789         this.active = c;
44790         return this;
44791     },
44792
44793     /**
44794      * Closes the current open container
44795      * @return {Form} this
44796      */
44797     end : function(){
44798         if(this.active == this.root){
44799             return this;
44800         }
44801         this.active = this.active.ownerCt;
44802         return this;
44803     },
44804
44805     /**
44806      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44807      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44808      * as the label of the field.
44809      * @param {Field} field1
44810      * @param {Field} field2 (optional)
44811      * @param {Field} etc. (optional)
44812      * @return {Form} this
44813      */
44814     add : function(){
44815         this.active.stack.push.apply(this.active.stack, arguments);
44816         this.allItems.push.apply(this.allItems,arguments);
44817         var r = [];
44818         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44819             if(a[i].isFormField){
44820                 r.push(a[i]);
44821             }
44822         }
44823         if(r.length > 0){
44824             Roo.form.Form.superclass.add.apply(this, r);
44825         }
44826         return this;
44827     },
44828     
44829
44830     
44831     
44832     
44833      /**
44834      * Find any element that has been added to a form, using it's ID or name
44835      * This can include framesets, columns etc. along with regular fields..
44836      * @param {String} id - id or name to find.
44837      
44838      * @return {Element} e - or false if nothing found.
44839      */
44840     findbyId : function(id)
44841     {
44842         var ret = false;
44843         if (!id) {
44844             return ret;
44845         }
44846         Roo.each(this.allItems, function(f){
44847             if (f.id == id || f.name == id ){
44848                 ret = f;
44849                 return false;
44850             }
44851         });
44852         return ret;
44853     },
44854
44855     
44856     
44857     /**
44858      * Render this form into the passed container. This should only be called once!
44859      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44860      * @return {Form} this
44861      */
44862     render : function(ct)
44863     {
44864         
44865         
44866         
44867         ct = Roo.get(ct);
44868         var o = this.autoCreate || {
44869             tag: 'form',
44870             method : this.method || 'POST',
44871             id : this.id || Roo.id()
44872         };
44873         this.initEl(ct.createChild(o));
44874
44875         this.root.render(this.el);
44876         
44877        
44878              
44879         this.items.each(function(f){
44880             f.render('x-form-el-'+f.id);
44881         });
44882
44883         if(this.buttons.length > 0){
44884             // tables are required to maintain order and for correct IE layout
44885             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44886                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44887                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44888             }}, null, true);
44889             var tr = tb.getElementsByTagName('tr')[0];
44890             for(var i = 0, len = this.buttons.length; i < len; i++) {
44891                 var b = this.buttons[i];
44892                 var td = document.createElement('td');
44893                 td.className = 'x-form-btn-td';
44894                 b.render(tr.appendChild(td));
44895             }
44896         }
44897         if(this.monitorValid){ // initialize after render
44898             this.startMonitoring();
44899         }
44900         this.fireEvent('rendered', this);
44901         return this;
44902     },
44903
44904     /**
44905      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44906      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44907      * object or a valid Roo.DomHelper element config
44908      * @param {Function} handler The function called when the button is clicked
44909      * @param {Object} scope (optional) The scope of the handler function
44910      * @return {Roo.Button}
44911      */
44912     addButton : function(config, handler, scope){
44913         var bc = {
44914             handler: handler,
44915             scope: scope,
44916             minWidth: this.minButtonWidth,
44917             hideParent:true
44918         };
44919         if(typeof config == "string"){
44920             bc.text = config;
44921         }else{
44922             Roo.apply(bc, config);
44923         }
44924         var btn = new Roo.Button(null, bc);
44925         this.buttons.push(btn);
44926         return btn;
44927     },
44928
44929      /**
44930      * Adds a series of form elements (using the xtype property as the factory method.
44931      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44932      * @param {Object} config 
44933      */
44934     
44935     addxtype : function()
44936     {
44937         var ar = Array.prototype.slice.call(arguments, 0);
44938         var ret = false;
44939         for(var i = 0; i < ar.length; i++) {
44940             if (!ar[i]) {
44941                 continue; // skip -- if this happends something invalid got sent, we 
44942                 // should ignore it, as basically that interface element will not show up
44943                 // and that should be pretty obvious!!
44944             }
44945             
44946             if (Roo.form[ar[i].xtype]) {
44947                 ar[i].form = this;
44948                 var fe = Roo.factory(ar[i], Roo.form);
44949                 if (!ret) {
44950                     ret = fe;
44951                 }
44952                 fe.form = this;
44953                 if (fe.store) {
44954                     fe.store.form = this;
44955                 }
44956                 if (fe.isLayout) {  
44957                          
44958                     this.start(fe);
44959                     this.allItems.push(fe);
44960                     if (fe.items && fe.addxtype) {
44961                         fe.addxtype.apply(fe, fe.items);
44962                         delete fe.items;
44963                     }
44964                      this.end();
44965                     continue;
44966                 }
44967                 
44968                 
44969                  
44970                 this.add(fe);
44971               //  console.log('adding ' + ar[i].xtype);
44972             }
44973             if (ar[i].xtype == 'Button') {  
44974                 //console.log('adding button');
44975                 //console.log(ar[i]);
44976                 this.addButton(ar[i]);
44977                 this.allItems.push(fe);
44978                 continue;
44979             }
44980             
44981             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44982                 alert('end is not supported on xtype any more, use items');
44983             //    this.end();
44984             //    //console.log('adding end');
44985             }
44986             
44987         }
44988         return ret;
44989     },
44990     
44991     /**
44992      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44993      * option "monitorValid"
44994      */
44995     startMonitoring : function(){
44996         if(!this.bound){
44997             this.bound = true;
44998             Roo.TaskMgr.start({
44999                 run : this.bindHandler,
45000                 interval : this.monitorPoll || 200,
45001                 scope: this
45002             });
45003         }
45004     },
45005
45006     /**
45007      * Stops monitoring of the valid state of this form
45008      */
45009     stopMonitoring : function(){
45010         this.bound = false;
45011     },
45012
45013     // private
45014     bindHandler : function(){
45015         if(!this.bound){
45016             return false; // stops binding
45017         }
45018         var valid = true;
45019         this.items.each(function(f){
45020             if(!f.isValid(true)){
45021                 valid = false;
45022                 return false;
45023             }
45024         });
45025         for(var i = 0, len = this.buttons.length; i < len; i++){
45026             var btn = this.buttons[i];
45027             if(btn.formBind === true && btn.disabled === valid){
45028                 btn.setDisabled(!valid);
45029             }
45030         }
45031         this.fireEvent('clientvalidation', this, valid);
45032     }
45033     
45034     
45035     
45036     
45037     
45038     
45039     
45040     
45041 });
45042
45043
45044 // back compat
45045 Roo.Form = Roo.form.Form;
45046 /*
45047  * Based on:
45048  * Ext JS Library 1.1.1
45049  * Copyright(c) 2006-2007, Ext JS, LLC.
45050  *
45051  * Originally Released Under LGPL - original licence link has changed is not relivant.
45052  *
45053  * Fork - LGPL
45054  * <script type="text/javascript">
45055  */
45056
45057 // as we use this in bootstrap.
45058 Roo.namespace('Roo.form');
45059  /**
45060  * @class Roo.form.Action
45061  * Internal Class used to handle form actions
45062  * @constructor
45063  * @param {Roo.form.BasicForm} el The form element or its id
45064  * @param {Object} config Configuration options
45065  */
45066
45067  
45068  
45069 // define the action interface
45070 Roo.form.Action = function(form, options){
45071     this.form = form;
45072     this.options = options || {};
45073 };
45074 /**
45075  * Client Validation Failed
45076  * @const 
45077  */
45078 Roo.form.Action.CLIENT_INVALID = 'client';
45079 /**
45080  * Server Validation Failed
45081  * @const 
45082  */
45083 Roo.form.Action.SERVER_INVALID = 'server';
45084  /**
45085  * Connect to Server Failed
45086  * @const 
45087  */
45088 Roo.form.Action.CONNECT_FAILURE = 'connect';
45089 /**
45090  * Reading Data from Server Failed
45091  * @const 
45092  */
45093 Roo.form.Action.LOAD_FAILURE = 'load';
45094
45095 Roo.form.Action.prototype = {
45096     type : 'default',
45097     failureType : undefined,
45098     response : undefined,
45099     result : undefined,
45100
45101     // interface method
45102     run : function(options){
45103
45104     },
45105
45106     // interface method
45107     success : function(response){
45108
45109     },
45110
45111     // interface method
45112     handleResponse : function(response){
45113
45114     },
45115
45116     // default connection failure
45117     failure : function(response){
45118         
45119         this.response = response;
45120         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45121         this.form.afterAction(this, false);
45122     },
45123
45124     processResponse : function(response){
45125         this.response = response;
45126         if(!response.responseText){
45127             return true;
45128         }
45129         this.result = this.handleResponse(response);
45130         return this.result;
45131     },
45132
45133     // utility functions used internally
45134     getUrl : function(appendParams){
45135         var url = this.options.url || this.form.url || this.form.el.dom.action;
45136         if(appendParams){
45137             var p = this.getParams();
45138             if(p){
45139                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45140             }
45141         }
45142         return url;
45143     },
45144
45145     getMethod : function(){
45146         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45147     },
45148
45149     getParams : function(){
45150         var bp = this.form.baseParams;
45151         var p = this.options.params;
45152         if(p){
45153             if(typeof p == "object"){
45154                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45155             }else if(typeof p == 'string' && bp){
45156                 p += '&' + Roo.urlEncode(bp);
45157             }
45158         }else if(bp){
45159             p = Roo.urlEncode(bp);
45160         }
45161         return p;
45162     },
45163
45164     createCallback : function(){
45165         return {
45166             success: this.success,
45167             failure: this.failure,
45168             scope: this,
45169             timeout: (this.form.timeout*1000),
45170             upload: this.form.fileUpload ? this.success : undefined
45171         };
45172     }
45173 };
45174
45175 Roo.form.Action.Submit = function(form, options){
45176     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45177 };
45178
45179 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45180     type : 'submit',
45181
45182     haveProgress : false,
45183     uploadComplete : false,
45184     
45185     // uploadProgress indicator.
45186     uploadProgress : function()
45187     {
45188         if (!this.form.progressUrl) {
45189             return;
45190         }
45191         
45192         if (!this.haveProgress) {
45193             Roo.MessageBox.progress("Uploading", "Uploading");
45194         }
45195         if (this.uploadComplete) {
45196            Roo.MessageBox.hide();
45197            return;
45198         }
45199         
45200         this.haveProgress = true;
45201    
45202         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45203         
45204         var c = new Roo.data.Connection();
45205         c.request({
45206             url : this.form.progressUrl,
45207             params: {
45208                 id : uid
45209             },
45210             method: 'GET',
45211             success : function(req){
45212                //console.log(data);
45213                 var rdata = false;
45214                 var edata;
45215                 try  {
45216                    rdata = Roo.decode(req.responseText)
45217                 } catch (e) {
45218                     Roo.log("Invalid data from server..");
45219                     Roo.log(edata);
45220                     return;
45221                 }
45222                 if (!rdata || !rdata.success) {
45223                     Roo.log(rdata);
45224                     Roo.MessageBox.alert(Roo.encode(rdata));
45225                     return;
45226                 }
45227                 var data = rdata.data;
45228                 
45229                 if (this.uploadComplete) {
45230                    Roo.MessageBox.hide();
45231                    return;
45232                 }
45233                    
45234                 if (data){
45235                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45236                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45237                     );
45238                 }
45239                 this.uploadProgress.defer(2000,this);
45240             },
45241        
45242             failure: function(data) {
45243                 Roo.log('progress url failed ');
45244                 Roo.log(data);
45245             },
45246             scope : this
45247         });
45248            
45249     },
45250     
45251     
45252     run : function()
45253     {
45254         // run get Values on the form, so it syncs any secondary forms.
45255         this.form.getValues();
45256         
45257         var o = this.options;
45258         var method = this.getMethod();
45259         var isPost = method == 'POST';
45260         if(o.clientValidation === false || this.form.isValid()){
45261             
45262             if (this.form.progressUrl) {
45263                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45264                     (new Date() * 1) + '' + Math.random());
45265                     
45266             } 
45267             
45268             
45269             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45270                 form:this.form.el.dom,
45271                 url:this.getUrl(!isPost),
45272                 method: method,
45273                 params:isPost ? this.getParams() : null,
45274                 isUpload: this.form.fileUpload
45275             }));
45276             
45277             this.uploadProgress();
45278
45279         }else if (o.clientValidation !== false){ // client validation failed
45280             this.failureType = Roo.form.Action.CLIENT_INVALID;
45281             this.form.afterAction(this, false);
45282         }
45283     },
45284
45285     success : function(response)
45286     {
45287         this.uploadComplete= true;
45288         if (this.haveProgress) {
45289             Roo.MessageBox.hide();
45290         }
45291         
45292         
45293         var result = this.processResponse(response);
45294         if(result === true || result.success){
45295             this.form.afterAction(this, true);
45296             return;
45297         }
45298         if(result.errors){
45299             this.form.markInvalid(result.errors);
45300             this.failureType = Roo.form.Action.SERVER_INVALID;
45301         }
45302         this.form.afterAction(this, false);
45303     },
45304     failure : function(response)
45305     {
45306         this.uploadComplete= true;
45307         if (this.haveProgress) {
45308             Roo.MessageBox.hide();
45309         }
45310         
45311         this.response = response;
45312         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45313         this.form.afterAction(this, false);
45314     },
45315     
45316     handleResponse : function(response){
45317         if(this.form.errorReader){
45318             var rs = this.form.errorReader.read(response);
45319             var errors = [];
45320             if(rs.records){
45321                 for(var i = 0, len = rs.records.length; i < len; i++) {
45322                     var r = rs.records[i];
45323                     errors[i] = r.data;
45324                 }
45325             }
45326             if(errors.length < 1){
45327                 errors = null;
45328             }
45329             return {
45330                 success : rs.success,
45331                 errors : errors
45332             };
45333         }
45334         var ret = false;
45335         try {
45336             ret = Roo.decode(response.responseText);
45337         } catch (e) {
45338             ret = {
45339                 success: false,
45340                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45341                 errors : []
45342             };
45343         }
45344         return ret;
45345         
45346     }
45347 });
45348
45349
45350 Roo.form.Action.Load = function(form, options){
45351     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45352     this.reader = this.form.reader;
45353 };
45354
45355 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45356     type : 'load',
45357
45358     run : function(){
45359         
45360         Roo.Ajax.request(Roo.apply(
45361                 this.createCallback(), {
45362                     method:this.getMethod(),
45363                     url:this.getUrl(false),
45364                     params:this.getParams()
45365         }));
45366     },
45367
45368     success : function(response){
45369         
45370         var result = this.processResponse(response);
45371         if(result === true || !result.success || !result.data){
45372             this.failureType = Roo.form.Action.LOAD_FAILURE;
45373             this.form.afterAction(this, false);
45374             return;
45375         }
45376         this.form.clearInvalid();
45377         this.form.setValues(result.data);
45378         this.form.afterAction(this, true);
45379     },
45380
45381     handleResponse : function(response){
45382         if(this.form.reader){
45383             var rs = this.form.reader.read(response);
45384             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45385             return {
45386                 success : rs.success,
45387                 data : data
45388             };
45389         }
45390         return Roo.decode(response.responseText);
45391     }
45392 });
45393
45394 Roo.form.Action.ACTION_TYPES = {
45395     'load' : Roo.form.Action.Load,
45396     'submit' : Roo.form.Action.Submit
45397 };/*
45398  * Based on:
45399  * Ext JS Library 1.1.1
45400  * Copyright(c) 2006-2007, Ext JS, LLC.
45401  *
45402  * Originally Released Under LGPL - original licence link has changed is not relivant.
45403  *
45404  * Fork - LGPL
45405  * <script type="text/javascript">
45406  */
45407  
45408 /**
45409  * @class Roo.form.Layout
45410  * @extends Roo.Component
45411  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45412  * @constructor
45413  * @param {Object} config Configuration options
45414  */
45415 Roo.form.Layout = function(config){
45416     var xitems = [];
45417     if (config.items) {
45418         xitems = config.items;
45419         delete config.items;
45420     }
45421     Roo.form.Layout.superclass.constructor.call(this, config);
45422     this.stack = [];
45423     Roo.each(xitems, this.addxtype, this);
45424      
45425 };
45426
45427 Roo.extend(Roo.form.Layout, Roo.Component, {
45428     /**
45429      * @cfg {String/Object} autoCreate
45430      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45431      */
45432     /**
45433      * @cfg {String/Object/Function} style
45434      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45435      * a function which returns such a specification.
45436      */
45437     /**
45438      * @cfg {String} labelAlign
45439      * Valid values are "left," "top" and "right" (defaults to "left")
45440      */
45441     /**
45442      * @cfg {Number} labelWidth
45443      * Fixed width in pixels of all field labels (defaults to undefined)
45444      */
45445     /**
45446      * @cfg {Boolean} clear
45447      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45448      */
45449     clear : true,
45450     /**
45451      * @cfg {String} labelSeparator
45452      * The separator to use after field labels (defaults to ':')
45453      */
45454     labelSeparator : ':',
45455     /**
45456      * @cfg {Boolean} hideLabels
45457      * True to suppress the display of field labels in this layout (defaults to false)
45458      */
45459     hideLabels : false,
45460
45461     // private
45462     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45463     
45464     isLayout : true,
45465     
45466     // private
45467     onRender : function(ct, position){
45468         if(this.el){ // from markup
45469             this.el = Roo.get(this.el);
45470         }else {  // generate
45471             var cfg = this.getAutoCreate();
45472             this.el = ct.createChild(cfg, position);
45473         }
45474         if(this.style){
45475             this.el.applyStyles(this.style);
45476         }
45477         if(this.labelAlign){
45478             this.el.addClass('x-form-label-'+this.labelAlign);
45479         }
45480         if(this.hideLabels){
45481             this.labelStyle = "display:none";
45482             this.elementStyle = "padding-left:0;";
45483         }else{
45484             if(typeof this.labelWidth == 'number'){
45485                 this.labelStyle = "width:"+this.labelWidth+"px;";
45486                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45487             }
45488             if(this.labelAlign == 'top'){
45489                 this.labelStyle = "width:auto;";
45490                 this.elementStyle = "padding-left:0;";
45491             }
45492         }
45493         var stack = this.stack;
45494         var slen = stack.length;
45495         if(slen > 0){
45496             if(!this.fieldTpl){
45497                 var t = new Roo.Template(
45498                     '<div class="x-form-item {5}">',
45499                         '<label for="{0}" style="{2}">{1}{4}</label>',
45500                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45501                         '</div>',
45502                     '</div><div class="x-form-clear-left"></div>'
45503                 );
45504                 t.disableFormats = true;
45505                 t.compile();
45506                 Roo.form.Layout.prototype.fieldTpl = t;
45507             }
45508             for(var i = 0; i < slen; i++) {
45509                 if(stack[i].isFormField){
45510                     this.renderField(stack[i]);
45511                 }else{
45512                     this.renderComponent(stack[i]);
45513                 }
45514             }
45515         }
45516         if(this.clear){
45517             this.el.createChild({cls:'x-form-clear'});
45518         }
45519     },
45520
45521     // private
45522     renderField : function(f){
45523         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45524                f.id, //0
45525                f.fieldLabel, //1
45526                f.labelStyle||this.labelStyle||'', //2
45527                this.elementStyle||'', //3
45528                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45529                f.itemCls||this.itemCls||''  //5
45530        ], true).getPrevSibling());
45531     },
45532
45533     // private
45534     renderComponent : function(c){
45535         c.render(c.isLayout ? this.el : this.el.createChild());    
45536     },
45537     /**
45538      * Adds a object form elements (using the xtype property as the factory method.)
45539      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45540      * @param {Object} config 
45541      */
45542     addxtype : function(o)
45543     {
45544         // create the lement.
45545         o.form = this.form;
45546         var fe = Roo.factory(o, Roo.form);
45547         this.form.allItems.push(fe);
45548         this.stack.push(fe);
45549         
45550         if (fe.isFormField) {
45551             this.form.items.add(fe);
45552         }
45553          
45554         return fe;
45555     }
45556 });
45557
45558 /**
45559  * @class Roo.form.Column
45560  * @extends Roo.form.Layout
45561  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45562  * @constructor
45563  * @param {Object} config Configuration options
45564  */
45565 Roo.form.Column = function(config){
45566     Roo.form.Column.superclass.constructor.call(this, config);
45567 };
45568
45569 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45570     /**
45571      * @cfg {Number/String} width
45572      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45573      */
45574     /**
45575      * @cfg {String/Object} autoCreate
45576      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45577      */
45578
45579     // private
45580     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45581
45582     // private
45583     onRender : function(ct, position){
45584         Roo.form.Column.superclass.onRender.call(this, ct, position);
45585         if(this.width){
45586             this.el.setWidth(this.width);
45587         }
45588     }
45589 });
45590
45591
45592 /**
45593  * @class Roo.form.Row
45594  * @extends Roo.form.Layout
45595  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45596  * @constructor
45597  * @param {Object} config Configuration options
45598  */
45599
45600  
45601 Roo.form.Row = function(config){
45602     Roo.form.Row.superclass.constructor.call(this, config);
45603 };
45604  
45605 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45606       /**
45607      * @cfg {Number/String} width
45608      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45609      */
45610     /**
45611      * @cfg {Number/String} height
45612      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45613      */
45614     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45615     
45616     padWidth : 20,
45617     // private
45618     onRender : function(ct, position){
45619         //console.log('row render');
45620         if(!this.rowTpl){
45621             var t = new Roo.Template(
45622                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45623                     '<label for="{0}" style="{2}">{1}{4}</label>',
45624                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45625                     '</div>',
45626                 '</div>'
45627             );
45628             t.disableFormats = true;
45629             t.compile();
45630             Roo.form.Layout.prototype.rowTpl = t;
45631         }
45632         this.fieldTpl = this.rowTpl;
45633         
45634         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45635         var labelWidth = 100;
45636         
45637         if ((this.labelAlign != 'top')) {
45638             if (typeof this.labelWidth == 'number') {
45639                 labelWidth = this.labelWidth
45640             }
45641             this.padWidth =  20 + labelWidth;
45642             
45643         }
45644         
45645         Roo.form.Column.superclass.onRender.call(this, ct, position);
45646         if(this.width){
45647             this.el.setWidth(this.width);
45648         }
45649         if(this.height){
45650             this.el.setHeight(this.height);
45651         }
45652     },
45653     
45654     // private
45655     renderField : function(f){
45656         f.fieldEl = this.fieldTpl.append(this.el, [
45657                f.id, f.fieldLabel,
45658                f.labelStyle||this.labelStyle||'',
45659                this.elementStyle||'',
45660                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45661                f.itemCls||this.itemCls||'',
45662                f.width ? f.width + this.padWidth : 160 + this.padWidth
45663        ],true);
45664     }
45665 });
45666  
45667
45668 /**
45669  * @class Roo.form.FieldSet
45670  * @extends Roo.form.Layout
45671  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45672  * @constructor
45673  * @param {Object} config Configuration options
45674  */
45675 Roo.form.FieldSet = function(config){
45676     Roo.form.FieldSet.superclass.constructor.call(this, config);
45677 };
45678
45679 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45680     /**
45681      * @cfg {String} legend
45682      * The text to display as the legend for the FieldSet (defaults to '')
45683      */
45684     /**
45685      * @cfg {String/Object} autoCreate
45686      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45687      */
45688
45689     // private
45690     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45691
45692     // private
45693     onRender : function(ct, position){
45694         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45695         if(this.legend){
45696             this.setLegend(this.legend);
45697         }
45698     },
45699
45700     // private
45701     setLegend : function(text){
45702         if(this.rendered){
45703             this.el.child('legend').update(text);
45704         }
45705     }
45706 });/*
45707  * Based on:
45708  * Ext JS Library 1.1.1
45709  * Copyright(c) 2006-2007, Ext JS, LLC.
45710  *
45711  * Originally Released Under LGPL - original licence link has changed is not relivant.
45712  *
45713  * Fork - LGPL
45714  * <script type="text/javascript">
45715  */
45716 /**
45717  * @class Roo.form.VTypes
45718  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45719  * @singleton
45720  */
45721 Roo.form.VTypes = function(){
45722     // closure these in so they are only created once.
45723     var alpha = /^[a-zA-Z_]+$/;
45724     var alphanum = /^[a-zA-Z0-9_]+$/;
45725     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45726     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45727
45728     // All these messages and functions are configurable
45729     return {
45730         /**
45731          * The function used to validate email addresses
45732          * @param {String} value The email address
45733          */
45734         'email' : function(v){
45735             return email.test(v);
45736         },
45737         /**
45738          * The error text to display when the email validation function returns false
45739          * @type String
45740          */
45741         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45742         /**
45743          * The keystroke filter mask to be applied on email input
45744          * @type RegExp
45745          */
45746         'emailMask' : /[a-z0-9_\.\-@]/i,
45747
45748         /**
45749          * The function used to validate URLs
45750          * @param {String} value The URL
45751          */
45752         'url' : function(v){
45753             return url.test(v);
45754         },
45755         /**
45756          * The error text to display when the url validation function returns false
45757          * @type String
45758          */
45759         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45760         
45761         /**
45762          * The function used to validate alpha values
45763          * @param {String} value The value
45764          */
45765         'alpha' : function(v){
45766             return alpha.test(v);
45767         },
45768         /**
45769          * The error text to display when the alpha validation function returns false
45770          * @type String
45771          */
45772         'alphaText' : 'This field should only contain letters and _',
45773         /**
45774          * The keystroke filter mask to be applied on alpha input
45775          * @type RegExp
45776          */
45777         'alphaMask' : /[a-z_]/i,
45778
45779         /**
45780          * The function used to validate alphanumeric values
45781          * @param {String} value The value
45782          */
45783         'alphanum' : function(v){
45784             return alphanum.test(v);
45785         },
45786         /**
45787          * The error text to display when the alphanumeric validation function returns false
45788          * @type String
45789          */
45790         'alphanumText' : 'This field should only contain letters, numbers and _',
45791         /**
45792          * The keystroke filter mask to be applied on alphanumeric input
45793          * @type RegExp
45794          */
45795         'alphanumMask' : /[a-z0-9_]/i
45796     };
45797 }();//<script type="text/javascript">
45798
45799 /**
45800  * @class Roo.form.FCKeditor
45801  * @extends Roo.form.TextArea
45802  * Wrapper around the FCKEditor http://www.fckeditor.net
45803  * @constructor
45804  * Creates a new FCKeditor
45805  * @param {Object} config Configuration options
45806  */
45807 Roo.form.FCKeditor = function(config){
45808     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45809     this.addEvents({
45810          /**
45811          * @event editorinit
45812          * Fired when the editor is initialized - you can add extra handlers here..
45813          * @param {FCKeditor} this
45814          * @param {Object} the FCK object.
45815          */
45816         editorinit : true
45817     });
45818     
45819     
45820 };
45821 Roo.form.FCKeditor.editors = { };
45822 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45823 {
45824     //defaultAutoCreate : {
45825     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45826     //},
45827     // private
45828     /**
45829      * @cfg {Object} fck options - see fck manual for details.
45830      */
45831     fckconfig : false,
45832     
45833     /**
45834      * @cfg {Object} fck toolbar set (Basic or Default)
45835      */
45836     toolbarSet : 'Basic',
45837     /**
45838      * @cfg {Object} fck BasePath
45839      */ 
45840     basePath : '/fckeditor/',
45841     
45842     
45843     frame : false,
45844     
45845     value : '',
45846     
45847    
45848     onRender : function(ct, position)
45849     {
45850         if(!this.el){
45851             this.defaultAutoCreate = {
45852                 tag: "textarea",
45853                 style:"width:300px;height:60px;",
45854                 autocomplete: "off"
45855             };
45856         }
45857         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45858         /*
45859         if(this.grow){
45860             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45861             if(this.preventScrollbars){
45862                 this.el.setStyle("overflow", "hidden");
45863             }
45864             this.el.setHeight(this.growMin);
45865         }
45866         */
45867         //console.log('onrender' + this.getId() );
45868         Roo.form.FCKeditor.editors[this.getId()] = this;
45869          
45870
45871         this.replaceTextarea() ;
45872         
45873     },
45874     
45875     getEditor : function() {
45876         return this.fckEditor;
45877     },
45878     /**
45879      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45880      * @param {Mixed} value The value to set
45881      */
45882     
45883     
45884     setValue : function(value)
45885     {
45886         //console.log('setValue: ' + value);
45887         
45888         if(typeof(value) == 'undefined') { // not sure why this is happending...
45889             return;
45890         }
45891         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45892         
45893         //if(!this.el || !this.getEditor()) {
45894         //    this.value = value;
45895             //this.setValue.defer(100,this,[value]);    
45896         //    return;
45897         //} 
45898         
45899         if(!this.getEditor()) {
45900             return;
45901         }
45902         
45903         this.getEditor().SetData(value);
45904         
45905         //
45906
45907     },
45908
45909     /**
45910      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45911      * @return {Mixed} value The field value
45912      */
45913     getValue : function()
45914     {
45915         
45916         if (this.frame && this.frame.dom.style.display == 'none') {
45917             return Roo.form.FCKeditor.superclass.getValue.call(this);
45918         }
45919         
45920         if(!this.el || !this.getEditor()) {
45921            
45922            // this.getValue.defer(100,this); 
45923             return this.value;
45924         }
45925        
45926         
45927         var value=this.getEditor().GetData();
45928         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45929         return Roo.form.FCKeditor.superclass.getValue.call(this);
45930         
45931
45932     },
45933
45934     /**
45935      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45936      * @return {Mixed} value The field value
45937      */
45938     getRawValue : function()
45939     {
45940         if (this.frame && this.frame.dom.style.display == 'none') {
45941             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45942         }
45943         
45944         if(!this.el || !this.getEditor()) {
45945             //this.getRawValue.defer(100,this); 
45946             return this.value;
45947             return;
45948         }
45949         
45950         
45951         
45952         var value=this.getEditor().GetData();
45953         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45954         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45955          
45956     },
45957     
45958     setSize : function(w,h) {
45959         
45960         
45961         
45962         //if (this.frame && this.frame.dom.style.display == 'none') {
45963         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45964         //    return;
45965         //}
45966         //if(!this.el || !this.getEditor()) {
45967         //    this.setSize.defer(100,this, [w,h]); 
45968         //    return;
45969         //}
45970         
45971         
45972         
45973         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45974         
45975         this.frame.dom.setAttribute('width', w);
45976         this.frame.dom.setAttribute('height', h);
45977         this.frame.setSize(w,h);
45978         
45979     },
45980     
45981     toggleSourceEdit : function(value) {
45982         
45983       
45984          
45985         this.el.dom.style.display = value ? '' : 'none';
45986         this.frame.dom.style.display = value ?  'none' : '';
45987         
45988     },
45989     
45990     
45991     focus: function(tag)
45992     {
45993         if (this.frame.dom.style.display == 'none') {
45994             return Roo.form.FCKeditor.superclass.focus.call(this);
45995         }
45996         if(!this.el || !this.getEditor()) {
45997             this.focus.defer(100,this, [tag]); 
45998             return;
45999         }
46000         
46001         
46002         
46003         
46004         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46005         this.getEditor().Focus();
46006         if (tgs.length) {
46007             if (!this.getEditor().Selection.GetSelection()) {
46008                 this.focus.defer(100,this, [tag]); 
46009                 return;
46010             }
46011             
46012             
46013             var r = this.getEditor().EditorDocument.createRange();
46014             r.setStart(tgs[0],0);
46015             r.setEnd(tgs[0],0);
46016             this.getEditor().Selection.GetSelection().removeAllRanges();
46017             this.getEditor().Selection.GetSelection().addRange(r);
46018             this.getEditor().Focus();
46019         }
46020         
46021     },
46022     
46023     
46024     
46025     replaceTextarea : function()
46026     {
46027         if ( document.getElementById( this.getId() + '___Frame' ) )
46028             return ;
46029         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46030         //{
46031             // We must check the elements firstly using the Id and then the name.
46032         var oTextarea = document.getElementById( this.getId() );
46033         
46034         var colElementsByName = document.getElementsByName( this.getId() ) ;
46035          
46036         oTextarea.style.display = 'none' ;
46037
46038         if ( oTextarea.tabIndex ) {            
46039             this.TabIndex = oTextarea.tabIndex ;
46040         }
46041         
46042         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46043         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46044         this.frame = Roo.get(this.getId() + '___Frame')
46045     },
46046     
46047     _getConfigHtml : function()
46048     {
46049         var sConfig = '' ;
46050
46051         for ( var o in this.fckconfig ) {
46052             sConfig += sConfig.length > 0  ? '&amp;' : '';
46053             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46054         }
46055
46056         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46057     },
46058     
46059     
46060     _getIFrameHtml : function()
46061     {
46062         var sFile = 'fckeditor.html' ;
46063         /* no idea what this is about..
46064         try
46065         {
46066             if ( (/fcksource=true/i).test( window.top.location.search ) )
46067                 sFile = 'fckeditor.original.html' ;
46068         }
46069         catch (e) { 
46070         */
46071
46072         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46073         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46074         
46075         
46076         var html = '<iframe id="' + this.getId() +
46077             '___Frame" src="' + sLink +
46078             '" width="' + this.width +
46079             '" height="' + this.height + '"' +
46080             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46081             ' frameborder="0" scrolling="no"></iframe>' ;
46082
46083         return html ;
46084     },
46085     
46086     _insertHtmlBefore : function( html, element )
46087     {
46088         if ( element.insertAdjacentHTML )       {
46089             // IE
46090             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46091         } else { // Gecko
46092             var oRange = document.createRange() ;
46093             oRange.setStartBefore( element ) ;
46094             var oFragment = oRange.createContextualFragment( html );
46095             element.parentNode.insertBefore( oFragment, element ) ;
46096         }
46097     }
46098     
46099     
46100   
46101     
46102     
46103     
46104     
46105
46106 });
46107
46108 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46109
46110 function FCKeditor_OnComplete(editorInstance){
46111     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46112     f.fckEditor = editorInstance;
46113     //console.log("loaded");
46114     f.fireEvent('editorinit', f, editorInstance);
46115
46116   
46117
46118  
46119
46120
46121
46122
46123
46124
46125
46126
46127
46128
46129
46130
46131
46132
46133
46134 //<script type="text/javascript">
46135 /**
46136  * @class Roo.form.GridField
46137  * @extends Roo.form.Field
46138  * Embed a grid (or editable grid into a form)
46139  * STATUS ALPHA
46140  * 
46141  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46142  * it needs 
46143  * xgrid.store = Roo.data.Store
46144  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46145  * xgrid.store.reader = Roo.data.JsonReader 
46146  * 
46147  * 
46148  * @constructor
46149  * Creates a new GridField
46150  * @param {Object} config Configuration options
46151  */
46152 Roo.form.GridField = function(config){
46153     Roo.form.GridField.superclass.constructor.call(this, config);
46154      
46155 };
46156
46157 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46158     /**
46159      * @cfg {Number} width  - used to restrict width of grid..
46160      */
46161     width : 100,
46162     /**
46163      * @cfg {Number} height - used to restrict height of grid..
46164      */
46165     height : 50,
46166      /**
46167      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46168          * 
46169          *}
46170      */
46171     xgrid : false, 
46172     /**
46173      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46174      * {tag: "input", type: "checkbox", autocomplete: "off"})
46175      */
46176    // defaultAutoCreate : { tag: 'div' },
46177     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46178     /**
46179      * @cfg {String} addTitle Text to include for adding a title.
46180      */
46181     addTitle : false,
46182     //
46183     onResize : function(){
46184         Roo.form.Field.superclass.onResize.apply(this, arguments);
46185     },
46186
46187     initEvents : function(){
46188         // Roo.form.Checkbox.superclass.initEvents.call(this);
46189         // has no events...
46190        
46191     },
46192
46193
46194     getResizeEl : function(){
46195         return this.wrap;
46196     },
46197
46198     getPositionEl : function(){
46199         return this.wrap;
46200     },
46201
46202     // private
46203     onRender : function(ct, position){
46204         
46205         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46206         var style = this.style;
46207         delete this.style;
46208         
46209         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46210         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46211         this.viewEl = this.wrap.createChild({ tag: 'div' });
46212         if (style) {
46213             this.viewEl.applyStyles(style);
46214         }
46215         if (this.width) {
46216             this.viewEl.setWidth(this.width);
46217         }
46218         if (this.height) {
46219             this.viewEl.setHeight(this.height);
46220         }
46221         //if(this.inputValue !== undefined){
46222         //this.setValue(this.value);
46223         
46224         
46225         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46226         
46227         
46228         this.grid.render();
46229         this.grid.getDataSource().on('remove', this.refreshValue, this);
46230         this.grid.getDataSource().on('update', this.refreshValue, this);
46231         this.grid.on('afteredit', this.refreshValue, this);
46232  
46233     },
46234      
46235     
46236     /**
46237      * Sets the value of the item. 
46238      * @param {String} either an object  or a string..
46239      */
46240     setValue : function(v){
46241         //this.value = v;
46242         v = v || []; // empty set..
46243         // this does not seem smart - it really only affects memoryproxy grids..
46244         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46245             var ds = this.grid.getDataSource();
46246             // assumes a json reader..
46247             var data = {}
46248             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46249             ds.loadData( data);
46250         }
46251         // clear selection so it does not get stale.
46252         if (this.grid.sm) { 
46253             this.grid.sm.clearSelections();
46254         }
46255         
46256         Roo.form.GridField.superclass.setValue.call(this, v);
46257         this.refreshValue();
46258         // should load data in the grid really....
46259     },
46260     
46261     // private
46262     refreshValue: function() {
46263          var val = [];
46264         this.grid.getDataSource().each(function(r) {
46265             val.push(r.data);
46266         });
46267         this.el.dom.value = Roo.encode(val);
46268     }
46269     
46270      
46271     
46272     
46273 });/*
46274  * Based on:
46275  * Ext JS Library 1.1.1
46276  * Copyright(c) 2006-2007, Ext JS, LLC.
46277  *
46278  * Originally Released Under LGPL - original licence link has changed is not relivant.
46279  *
46280  * Fork - LGPL
46281  * <script type="text/javascript">
46282  */
46283 /**
46284  * @class Roo.form.DisplayField
46285  * @extends Roo.form.Field
46286  * A generic Field to display non-editable data.
46287  * @constructor
46288  * Creates a new Display Field item.
46289  * @param {Object} config Configuration options
46290  */
46291 Roo.form.DisplayField = function(config){
46292     Roo.form.DisplayField.superclass.constructor.call(this, config);
46293     
46294 };
46295
46296 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46297     inputType:      'hidden',
46298     allowBlank:     true,
46299     readOnly:         true,
46300     
46301  
46302     /**
46303      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46304      */
46305     focusClass : undefined,
46306     /**
46307      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46308      */
46309     fieldClass: 'x-form-field',
46310     
46311      /**
46312      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46313      */
46314     valueRenderer: undefined,
46315     
46316     width: 100,
46317     /**
46318      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46319      * {tag: "input", type: "checkbox", autocomplete: "off"})
46320      */
46321      
46322  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46323
46324     onResize : function(){
46325         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46326         
46327     },
46328
46329     initEvents : function(){
46330         // Roo.form.Checkbox.superclass.initEvents.call(this);
46331         // has no events...
46332        
46333     },
46334
46335
46336     getResizeEl : function(){
46337         return this.wrap;
46338     },
46339
46340     getPositionEl : function(){
46341         return this.wrap;
46342     },
46343
46344     // private
46345     onRender : function(ct, position){
46346         
46347         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46348         //if(this.inputValue !== undefined){
46349         this.wrap = this.el.wrap();
46350         
46351         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46352         
46353         if (this.bodyStyle) {
46354             this.viewEl.applyStyles(this.bodyStyle);
46355         }
46356         //this.viewEl.setStyle('padding', '2px');
46357         
46358         this.setValue(this.value);
46359         
46360     },
46361 /*
46362     // private
46363     initValue : Roo.emptyFn,
46364
46365   */
46366
46367         // private
46368     onClick : function(){
46369         
46370     },
46371
46372     /**
46373      * Sets the checked state of the checkbox.
46374      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46375      */
46376     setValue : function(v){
46377         this.value = v;
46378         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46379         // this might be called before we have a dom element..
46380         if (!this.viewEl) {
46381             return;
46382         }
46383         this.viewEl.dom.innerHTML = html;
46384         Roo.form.DisplayField.superclass.setValue.call(this, v);
46385
46386     }
46387 });/*
46388  * 
46389  * Licence- LGPL
46390  * 
46391  */
46392
46393 /**
46394  * @class Roo.form.DayPicker
46395  * @extends Roo.form.Field
46396  * A Day picker show [M] [T] [W] ....
46397  * @constructor
46398  * Creates a new Day Picker
46399  * @param {Object} config Configuration options
46400  */
46401 Roo.form.DayPicker= function(config){
46402     Roo.form.DayPicker.superclass.constructor.call(this, config);
46403      
46404 };
46405
46406 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46407     /**
46408      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46409      */
46410     focusClass : undefined,
46411     /**
46412      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46413      */
46414     fieldClass: "x-form-field",
46415    
46416     /**
46417      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46418      * {tag: "input", type: "checkbox", autocomplete: "off"})
46419      */
46420     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46421     
46422    
46423     actionMode : 'viewEl', 
46424     //
46425     // private
46426  
46427     inputType : 'hidden',
46428     
46429      
46430     inputElement: false, // real input element?
46431     basedOn: false, // ????
46432     
46433     isFormField: true, // not sure where this is needed!!!!
46434
46435     onResize : function(){
46436         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46437         if(!this.boxLabel){
46438             this.el.alignTo(this.wrap, 'c-c');
46439         }
46440     },
46441
46442     initEvents : function(){
46443         Roo.form.Checkbox.superclass.initEvents.call(this);
46444         this.el.on("click", this.onClick,  this);
46445         this.el.on("change", this.onClick,  this);
46446     },
46447
46448
46449     getResizeEl : function(){
46450         return this.wrap;
46451     },
46452
46453     getPositionEl : function(){
46454         return this.wrap;
46455     },
46456
46457     
46458     // private
46459     onRender : function(ct, position){
46460         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46461        
46462         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46463         
46464         var r1 = '<table><tr>';
46465         var r2 = '<tr class="x-form-daypick-icons">';
46466         for (var i=0; i < 7; i++) {
46467             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46468             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46469         }
46470         
46471         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46472         viewEl.select('img').on('click', this.onClick, this);
46473         this.viewEl = viewEl;   
46474         
46475         
46476         // this will not work on Chrome!!!
46477         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46478         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46479         
46480         
46481           
46482
46483     },
46484
46485     // private
46486     initValue : Roo.emptyFn,
46487
46488     /**
46489      * Returns the checked state of the checkbox.
46490      * @return {Boolean} True if checked, else false
46491      */
46492     getValue : function(){
46493         return this.el.dom.value;
46494         
46495     },
46496
46497         // private
46498     onClick : function(e){ 
46499         //this.setChecked(!this.checked);
46500         Roo.get(e.target).toggleClass('x-menu-item-checked');
46501         this.refreshValue();
46502         //if(this.el.dom.checked != this.checked){
46503         //    this.setValue(this.el.dom.checked);
46504        // }
46505     },
46506     
46507     // private
46508     refreshValue : function()
46509     {
46510         var val = '';
46511         this.viewEl.select('img',true).each(function(e,i,n)  {
46512             val += e.is(".x-menu-item-checked") ? String(n) : '';
46513         });
46514         this.setValue(val, true);
46515     },
46516
46517     /**
46518      * Sets the checked state of the checkbox.
46519      * On is always based on a string comparison between inputValue and the param.
46520      * @param {Boolean/String} value - the value to set 
46521      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46522      */
46523     setValue : function(v,suppressEvent){
46524         if (!this.el.dom) {
46525             return;
46526         }
46527         var old = this.el.dom.value ;
46528         this.el.dom.value = v;
46529         if (suppressEvent) {
46530             return ;
46531         }
46532          
46533         // update display..
46534         this.viewEl.select('img',true).each(function(e,i,n)  {
46535             
46536             var on = e.is(".x-menu-item-checked");
46537             var newv = v.indexOf(String(n)) > -1;
46538             if (on != newv) {
46539                 e.toggleClass('x-menu-item-checked');
46540             }
46541             
46542         });
46543         
46544         
46545         this.fireEvent('change', this, v, old);
46546         
46547         
46548     },
46549    
46550     // handle setting of hidden value by some other method!!?!?
46551     setFromHidden: function()
46552     {
46553         if(!this.el){
46554             return;
46555         }
46556         //console.log("SET FROM HIDDEN");
46557         //alert('setFrom hidden');
46558         this.setValue(this.el.dom.value);
46559     },
46560     
46561     onDestroy : function()
46562     {
46563         if(this.viewEl){
46564             Roo.get(this.viewEl).remove();
46565         }
46566          
46567         Roo.form.DayPicker.superclass.onDestroy.call(this);
46568     }
46569
46570 });/*
46571  * RooJS Library 1.1.1
46572  * Copyright(c) 2008-2011  Alan Knowles
46573  *
46574  * License - LGPL
46575  */
46576  
46577
46578 /**
46579  * @class Roo.form.ComboCheck
46580  * @extends Roo.form.ComboBox
46581  * A combobox for multiple select items.
46582  *
46583  * FIXME - could do with a reset button..
46584  * 
46585  * @constructor
46586  * Create a new ComboCheck
46587  * @param {Object} config Configuration options
46588  */
46589 Roo.form.ComboCheck = function(config){
46590     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46591     // should verify some data...
46592     // like
46593     // hiddenName = required..
46594     // displayField = required
46595     // valudField == required
46596     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46597     var _t = this;
46598     Roo.each(req, function(e) {
46599         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46600             throw "Roo.form.ComboCheck : missing value for: " + e;
46601         }
46602     });
46603     
46604     
46605 };
46606
46607 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46608      
46609      
46610     editable : false,
46611      
46612     selectedClass: 'x-menu-item-checked', 
46613     
46614     // private
46615     onRender : function(ct, position){
46616         var _t = this;
46617         
46618         
46619         
46620         if(!this.tpl){
46621             var cls = 'x-combo-list';
46622
46623             
46624             this.tpl =  new Roo.Template({
46625                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46626                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46627                    '<span>{' + this.displayField + '}</span>' +
46628                     '</div>' 
46629                 
46630             });
46631         }
46632  
46633         
46634         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46635         this.view.singleSelect = false;
46636         this.view.multiSelect = true;
46637         this.view.toggleSelect = true;
46638         this.pageTb.add(new Roo.Toolbar.Fill(), {
46639             
46640             text: 'Done',
46641             handler: function()
46642             {
46643                 _t.collapse();
46644             }
46645         });
46646     },
46647     
46648     onViewOver : function(e, t){
46649         // do nothing...
46650         return;
46651         
46652     },
46653     
46654     onViewClick : function(doFocus,index){
46655         return;
46656         
46657     },
46658     select: function () {
46659         //Roo.log("SELECT CALLED");
46660     },
46661      
46662     selectByValue : function(xv, scrollIntoView){
46663         var ar = this.getValueArray();
46664         var sels = [];
46665         
46666         Roo.each(ar, function(v) {
46667             if(v === undefined || v === null){
46668                 return;
46669             }
46670             var r = this.findRecord(this.valueField, v);
46671             if(r){
46672                 sels.push(this.store.indexOf(r))
46673                 
46674             }
46675         },this);
46676         this.view.select(sels);
46677         return false;
46678     },
46679     
46680     
46681     
46682     onSelect : function(record, index){
46683        // Roo.log("onselect Called");
46684        // this is only called by the clear button now..
46685         this.view.clearSelections();
46686         this.setValue('[]');
46687         if (this.value != this.valueBefore) {
46688             this.fireEvent('change', this, this.value, this.valueBefore);
46689             this.valueBefore = this.value;
46690         }
46691     },
46692     getValueArray : function()
46693     {
46694         var ar = [] ;
46695         
46696         try {
46697             //Roo.log(this.value);
46698             if (typeof(this.value) == 'undefined') {
46699                 return [];
46700             }
46701             var ar = Roo.decode(this.value);
46702             return  ar instanceof Array ? ar : []; //?? valid?
46703             
46704         } catch(e) {
46705             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46706             return [];
46707         }
46708          
46709     },
46710     expand : function ()
46711     {
46712         
46713         Roo.form.ComboCheck.superclass.expand.call(this);
46714         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46715         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46716         
46717
46718     },
46719     
46720     collapse : function(){
46721         Roo.form.ComboCheck.superclass.collapse.call(this);
46722         var sl = this.view.getSelectedIndexes();
46723         var st = this.store;
46724         var nv = [];
46725         var tv = [];
46726         var r;
46727         Roo.each(sl, function(i) {
46728             r = st.getAt(i);
46729             nv.push(r.get(this.valueField));
46730         },this);
46731         this.setValue(Roo.encode(nv));
46732         if (this.value != this.valueBefore) {
46733
46734             this.fireEvent('change', this, this.value, this.valueBefore);
46735             this.valueBefore = this.value;
46736         }
46737         
46738     },
46739     
46740     setValue : function(v){
46741         // Roo.log(v);
46742         this.value = v;
46743         
46744         var vals = this.getValueArray();
46745         var tv = [];
46746         Roo.each(vals, function(k) {
46747             var r = this.findRecord(this.valueField, k);
46748             if(r){
46749                 tv.push(r.data[this.displayField]);
46750             }else if(this.valueNotFoundText !== undefined){
46751                 tv.push( this.valueNotFoundText );
46752             }
46753         },this);
46754        // Roo.log(tv);
46755         
46756         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46757         this.hiddenField.value = v;
46758         this.value = v;
46759     }
46760     
46761 });/*
46762  * Based on:
46763  * Ext JS Library 1.1.1
46764  * Copyright(c) 2006-2007, Ext JS, LLC.
46765  *
46766  * Originally Released Under LGPL - original licence link has changed is not relivant.
46767  *
46768  * Fork - LGPL
46769  * <script type="text/javascript">
46770  */
46771  
46772 /**
46773  * @class Roo.form.Signature
46774  * @extends Roo.form.Field
46775  * Signature field.  
46776  * @constructor
46777  * 
46778  * @param {Object} config Configuration options
46779  */
46780
46781 Roo.form.Signature = function(config){
46782     Roo.form.Signature.superclass.constructor.call(this, config);
46783     
46784     this.addEvents({// not in used??
46785          /**
46786          * @event confirm
46787          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46788              * @param {Roo.form.Signature} combo This combo box
46789              */
46790         'confirm' : true,
46791         /**
46792          * @event reset
46793          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46794              * @param {Roo.form.ComboBox} combo This combo box
46795              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46796              */
46797         'reset' : true
46798     });
46799 };
46800
46801 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46802     /**
46803      * @cfg {Object} labels Label to use when rendering a form.
46804      * defaults to 
46805      * labels : { 
46806      *      clear : "Clear",
46807      *      confirm : "Confirm"
46808      *  }
46809      */
46810     labels : { 
46811         clear : "Clear",
46812         confirm : "Confirm"
46813     },
46814     /**
46815      * @cfg {Number} width The signature panel width (defaults to 300)
46816      */
46817     width: 300,
46818     /**
46819      * @cfg {Number} height The signature panel height (defaults to 100)
46820      */
46821     height : 100,
46822     /**
46823      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46824      */
46825     allowBlank : false,
46826     
46827     //private
46828     // {Object} signPanel The signature SVG panel element (defaults to {})
46829     signPanel : {},
46830     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46831     isMouseDown : false,
46832     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46833     isConfirmed : false,
46834     // {String} signatureTmp SVG mapping string (defaults to empty string)
46835     signatureTmp : '',
46836     
46837     
46838     defaultAutoCreate : { // modified by initCompnoent..
46839         tag: "input",
46840         type:"hidden"
46841     },
46842
46843     // private
46844     onRender : function(ct, position){
46845         
46846         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46847         
46848         this.wrap = this.el.wrap({
46849             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46850         });
46851         
46852         this.createToolbar(this);
46853         this.signPanel = this.wrap.createChild({
46854                 tag: 'div',
46855                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46856             }, this.el
46857         );
46858             
46859         this.svgID = Roo.id();
46860         this.svgEl = this.signPanel.createChild({
46861               xmlns : 'http://www.w3.org/2000/svg',
46862               tag : 'svg',
46863               id : this.svgID + "-svg",
46864               width: this.width,
46865               height: this.height,
46866               viewBox: '0 0 '+this.width+' '+this.height,
46867               cn : [
46868                 {
46869                     tag: "rect",
46870                     id: this.svgID + "-svg-r",
46871                     width: this.width,
46872                     height: this.height,
46873                     fill: "#ffa"
46874                 },
46875                 {
46876                     tag: "line",
46877                     id: this.svgID + "-svg-l",
46878                     x1: "0", // start
46879                     y1: (this.height*0.8), // start set the line in 80% of height
46880                     x2: this.width, // end
46881                     y2: (this.height*0.8), // end set the line in 80% of height
46882                     'stroke': "#666",
46883                     'stroke-width': "1",
46884                     'stroke-dasharray': "3",
46885                     'shape-rendering': "crispEdges",
46886                     'pointer-events': "none"
46887                 },
46888                 {
46889                     tag: "path",
46890                     id: this.svgID + "-svg-p",
46891                     'stroke': "navy",
46892                     'stroke-width': "3",
46893                     'fill': "none",
46894                     'pointer-events': 'none'
46895                 }
46896               ]
46897         });
46898         this.createSVG();
46899         this.svgBox = this.svgEl.dom.getScreenCTM();
46900     },
46901     createSVG : function(){ 
46902         var svg = this.signPanel;
46903         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46904         var t = this;
46905
46906         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46907         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46908         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46909         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46910         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46911         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46912         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46913         
46914     },
46915     isTouchEvent : function(e){
46916         return e.type.match(/^touch/);
46917     },
46918     getCoords : function (e) {
46919         var pt    = this.svgEl.dom.createSVGPoint();
46920         pt.x = e.clientX; 
46921         pt.y = e.clientY;
46922         if (this.isTouchEvent(e)) {
46923             pt.x =  e.targetTouches[0].clientX 
46924             pt.y = e.targetTouches[0].clientY;
46925         }
46926         var a = this.svgEl.dom.getScreenCTM();
46927         var b = a.inverse();
46928         var mx = pt.matrixTransform(b);
46929         return mx.x + ',' + mx.y;
46930     },
46931     //mouse event headler 
46932     down : function (e) {
46933         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46934         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46935         
46936         this.isMouseDown = true;
46937         
46938         e.preventDefault();
46939     },
46940     move : function (e) {
46941         if (this.isMouseDown) {
46942             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46943             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46944         }
46945         
46946         e.preventDefault();
46947     },
46948     up : function (e) {
46949         this.isMouseDown = false;
46950         var sp = this.signatureTmp.split(' ');
46951         
46952         if(sp.length > 1){
46953             if(!sp[sp.length-2].match(/^L/)){
46954                 sp.pop();
46955                 sp.pop();
46956                 sp.push("");
46957                 this.signatureTmp = sp.join(" ");
46958             }
46959         }
46960         if(this.getValue() != this.signatureTmp){
46961             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46962             this.isConfirmed = false;
46963         }
46964         e.preventDefault();
46965     },
46966     
46967     /**
46968      * Protected method that will not generally be called directly. It
46969      * is called when the editor creates its toolbar. Override this method if you need to
46970      * add custom toolbar buttons.
46971      * @param {HtmlEditor} editor
46972      */
46973     createToolbar : function(editor){
46974          function btn(id, toggle, handler){
46975             var xid = fid + '-'+ id ;
46976             return {
46977                 id : xid,
46978                 cmd : id,
46979                 cls : 'x-btn-icon x-edit-'+id,
46980                 enableToggle:toggle !== false,
46981                 scope: editor, // was editor...
46982                 handler:handler||editor.relayBtnCmd,
46983                 clickEvent:'mousedown',
46984                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46985                 tabIndex:-1
46986             };
46987         }
46988         
46989         
46990         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46991         this.tb = tb;
46992         this.tb.add(
46993            {
46994                 cls : ' x-signature-btn x-signature-'+id,
46995                 scope: editor, // was editor...
46996                 handler: this.reset,
46997                 clickEvent:'mousedown',
46998                 text: this.labels.clear
46999             },
47000             {
47001                  xtype : 'Fill',
47002                  xns: Roo.Toolbar
47003             }, 
47004             {
47005                 cls : '  x-signature-btn x-signature-'+id,
47006                 scope: editor, // was editor...
47007                 handler: this.confirmHandler,
47008                 clickEvent:'mousedown',
47009                 text: this.labels.confirm
47010             }
47011         );
47012     
47013     },
47014     //public
47015     /**
47016      * when user is clicked confirm then show this image.....
47017      * 
47018      * @return {String} Image Data URI
47019      */
47020     getImageDataURI : function(){
47021         var svg = this.svgEl.dom.parentNode.innerHTML;
47022         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47023         return src; 
47024     },
47025     /**
47026      * 
47027      * @return {Boolean} this.isConfirmed
47028      */
47029     getConfirmed : function(){
47030         return this.isConfirmed;
47031     },
47032     /**
47033      * 
47034      * @return {Number} this.width
47035      */
47036     getWidth : function(){
47037         return this.width;
47038     },
47039     /**
47040      * 
47041      * @return {Number} this.height
47042      */
47043     getHeight : function(){
47044         return this.height;
47045     },
47046     // private
47047     getSignature : function(){
47048         return this.signatureTmp;
47049     },
47050     // private
47051     reset : function(){
47052         this.signatureTmp = '';
47053         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47054         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47055         this.isConfirmed = false;
47056         Roo.form.Signature.superclass.reset.call(this);
47057     },
47058     setSignature : function(s){
47059         this.signatureTmp = s;
47060         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47061         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47062         this.setValue(s);
47063         this.isConfirmed = false;
47064         Roo.form.Signature.superclass.reset.call(this);
47065     }, 
47066     test : function(){
47067 //        Roo.log(this.signPanel.dom.contentWindow.up())
47068     },
47069     //private
47070     setConfirmed : function(){
47071         
47072         
47073         
47074 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47075     },
47076     // private
47077     confirmHandler : function(){
47078         if(!this.getSignature()){
47079             return;
47080         }
47081         
47082         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47083         this.setValue(this.getSignature());
47084         this.isConfirmed = true;
47085         
47086         this.fireEvent('confirm', this);
47087     },
47088     // private
47089     // Subclasses should provide the validation implementation by overriding this
47090     validateValue : function(value){
47091         if(this.allowBlank){
47092             return true;
47093         }
47094         
47095         if(this.isConfirmed){
47096             return true;
47097         }
47098         return false;
47099     }
47100 });/*
47101  * Based on:
47102  * Ext JS Library 1.1.1
47103  * Copyright(c) 2006-2007, Ext JS, LLC.
47104  *
47105  * Originally Released Under LGPL - original licence link has changed is not relivant.
47106  *
47107  * Fork - LGPL
47108  * <script type="text/javascript">
47109  */
47110  
47111
47112 /**
47113  * @class Roo.form.ComboBox
47114  * @extends Roo.form.TriggerField
47115  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47116  * @constructor
47117  * Create a new ComboBox.
47118  * @param {Object} config Configuration options
47119  */
47120 Roo.form.Select = function(config){
47121     Roo.form.Select.superclass.constructor.call(this, config);
47122      
47123 };
47124
47125 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47126     /**
47127      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47128      */
47129     /**
47130      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47131      * rendering into an Roo.Editor, defaults to false)
47132      */
47133     /**
47134      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47135      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47136      */
47137     /**
47138      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47139      */
47140     /**
47141      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47142      * the dropdown list (defaults to undefined, with no header element)
47143      */
47144
47145      /**
47146      * @cfg {String/Roo.Template} tpl The template to use to render the output
47147      */
47148      
47149     // private
47150     defaultAutoCreate : {tag: "select"  },
47151     /**
47152      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47153      */
47154     listWidth: undefined,
47155     /**
47156      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47157      * mode = 'remote' or 'text' if mode = 'local')
47158      */
47159     displayField: undefined,
47160     /**
47161      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47162      * mode = 'remote' or 'value' if mode = 'local'). 
47163      * Note: use of a valueField requires the user make a selection
47164      * in order for a value to be mapped.
47165      */
47166     valueField: undefined,
47167     
47168     
47169     /**
47170      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47171      * field's data value (defaults to the underlying DOM element's name)
47172      */
47173     hiddenName: undefined,
47174     /**
47175      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47176      */
47177     listClass: '',
47178     /**
47179      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47180      */
47181     selectedClass: 'x-combo-selected',
47182     /**
47183      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47184      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47185      * which displays a downward arrow icon).
47186      */
47187     triggerClass : 'x-form-arrow-trigger',
47188     /**
47189      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47190      */
47191     shadow:'sides',
47192     /**
47193      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47194      * anchor positions (defaults to 'tl-bl')
47195      */
47196     listAlign: 'tl-bl?',
47197     /**
47198      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47199      */
47200     maxHeight: 300,
47201     /**
47202      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47203      * query specified by the allQuery config option (defaults to 'query')
47204      */
47205     triggerAction: 'query',
47206     /**
47207      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47208      * (defaults to 4, does not apply if editable = false)
47209      */
47210     minChars : 4,
47211     /**
47212      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47213      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47214      */
47215     typeAhead: false,
47216     /**
47217      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47218      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47219      */
47220     queryDelay: 500,
47221     /**
47222      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47223      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47224      */
47225     pageSize: 0,
47226     /**
47227      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47228      * when editable = true (defaults to false)
47229      */
47230     selectOnFocus:false,
47231     /**
47232      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47233      */
47234     queryParam: 'query',
47235     /**
47236      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47237      * when mode = 'remote' (defaults to 'Loading...')
47238      */
47239     loadingText: 'Loading...',
47240     /**
47241      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47242      */
47243     resizable: false,
47244     /**
47245      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47246      */
47247     handleHeight : 8,
47248     /**
47249      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47250      * traditional select (defaults to true)
47251      */
47252     editable: true,
47253     /**
47254      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47255      */
47256     allQuery: '',
47257     /**
47258      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47259      */
47260     mode: 'remote',
47261     /**
47262      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47263      * listWidth has a higher value)
47264      */
47265     minListWidth : 70,
47266     /**
47267      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47268      * allow the user to set arbitrary text into the field (defaults to false)
47269      */
47270     forceSelection:false,
47271     /**
47272      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47273      * if typeAhead = true (defaults to 250)
47274      */
47275     typeAheadDelay : 250,
47276     /**
47277      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47278      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47279      */
47280     valueNotFoundText : undefined,
47281     
47282     /**
47283      * @cfg {String} defaultValue The value displayed after loading the store.
47284      */
47285     defaultValue: '',
47286     
47287     /**
47288      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47289      */
47290     blockFocus : false,
47291     
47292     /**
47293      * @cfg {Boolean} disableClear Disable showing of clear button.
47294      */
47295     disableClear : false,
47296     /**
47297      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47298      */
47299     alwaysQuery : false,
47300     
47301     //private
47302     addicon : false,
47303     editicon: false,
47304     
47305     // element that contains real text value.. (when hidden is used..)
47306      
47307     // private
47308     onRender : function(ct, position){
47309         Roo.form.Field.prototype.onRender.call(this, ct, position);
47310         
47311         if(this.store){
47312             this.store.on('beforeload', this.onBeforeLoad, this);
47313             this.store.on('load', this.onLoad, this);
47314             this.store.on('loadexception', this.onLoadException, this);
47315             this.store.load({});
47316         }
47317         
47318         
47319         
47320     },
47321
47322     // private
47323     initEvents : function(){
47324         //Roo.form.ComboBox.superclass.initEvents.call(this);
47325  
47326     },
47327
47328     onDestroy : function(){
47329        
47330         if(this.store){
47331             this.store.un('beforeload', this.onBeforeLoad, this);
47332             this.store.un('load', this.onLoad, this);
47333             this.store.un('loadexception', this.onLoadException, this);
47334         }
47335         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47336     },
47337
47338     // private
47339     fireKey : function(e){
47340         if(e.isNavKeyPress() && !this.list.isVisible()){
47341             this.fireEvent("specialkey", this, e);
47342         }
47343     },
47344
47345     // private
47346     onResize: function(w, h){
47347         
47348         return; 
47349     
47350         
47351     },
47352
47353     /**
47354      * Allow or prevent the user from directly editing the field text.  If false is passed,
47355      * the user will only be able to select from the items defined in the dropdown list.  This method
47356      * is the runtime equivalent of setting the 'editable' config option at config time.
47357      * @param {Boolean} value True to allow the user to directly edit the field text
47358      */
47359     setEditable : function(value){
47360          
47361     },
47362
47363     // private
47364     onBeforeLoad : function(){
47365         
47366         Roo.log("Select before load");
47367         return;
47368     
47369         this.innerList.update(this.loadingText ?
47370                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47371         //this.restrictHeight();
47372         this.selectedIndex = -1;
47373     },
47374
47375     // private
47376     onLoad : function(){
47377
47378     
47379         var dom = this.el.dom;
47380         dom.innerHTML = '';
47381          var od = dom.ownerDocument;
47382          
47383         if (this.emptyText) {
47384             var op = od.createElement('option');
47385             op.setAttribute('value', '');
47386             op.innerHTML = String.format('{0}', this.emptyText);
47387             dom.appendChild(op);
47388         }
47389         if(this.store.getCount() > 0){
47390            
47391             var vf = this.valueField;
47392             var df = this.displayField;
47393             this.store.data.each(function(r) {
47394                 // which colmsn to use... testing - cdoe / title..
47395                 var op = od.createElement('option');
47396                 op.setAttribute('value', r.data[vf]);
47397                 op.innerHTML = String.format('{0}', r.data[df]);
47398                 dom.appendChild(op);
47399             });
47400             if (typeof(this.defaultValue != 'undefined')) {
47401                 this.setValue(this.defaultValue);
47402             }
47403             
47404              
47405         }else{
47406             //this.onEmptyResults();
47407         }
47408         //this.el.focus();
47409     },
47410     // private
47411     onLoadException : function()
47412     {
47413         dom.innerHTML = '';
47414             
47415         Roo.log("Select on load exception");
47416         return;
47417     
47418         this.collapse();
47419         Roo.log(this.store.reader.jsonData);
47420         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47421             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47422         }
47423         
47424         
47425     },
47426     // private
47427     onTypeAhead : function(){
47428          
47429     },
47430
47431     // private
47432     onSelect : function(record, index){
47433         Roo.log('on select?');
47434         return;
47435         if(this.fireEvent('beforeselect', this, record, index) !== false){
47436             this.setFromData(index > -1 ? record.data : false);
47437             this.collapse();
47438             this.fireEvent('select', this, record, index);
47439         }
47440     },
47441
47442     /**
47443      * Returns the currently selected field value or empty string if no value is set.
47444      * @return {String} value The selected value
47445      */
47446     getValue : function(){
47447         var dom = this.el.dom;
47448         this.value = dom.options[dom.selectedIndex].value;
47449         return this.value;
47450         
47451     },
47452
47453     /**
47454      * Clears any text/value currently set in the field
47455      */
47456     clearValue : function(){
47457         this.value = '';
47458         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47459         
47460     },
47461
47462     /**
47463      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47464      * will be displayed in the field.  If the value does not match the data value of an existing item,
47465      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47466      * Otherwise the field will be blank (although the value will still be set).
47467      * @param {String} value The value to match
47468      */
47469     setValue : function(v){
47470         var d = this.el.dom;
47471         for (var i =0; i < d.options.length;i++) {
47472             if (v == d.options[i].value) {
47473                 d.selectedIndex = i;
47474                 this.value = v;
47475                 return;
47476             }
47477         }
47478         this.clearValue();
47479     },
47480     /**
47481      * @property {Object} the last set data for the element
47482      */
47483     
47484     lastData : false,
47485     /**
47486      * Sets the value of the field based on a object which is related to the record format for the store.
47487      * @param {Object} value the value to set as. or false on reset?
47488      */
47489     setFromData : function(o){
47490         Roo.log('setfrom data?');
47491          
47492         
47493         
47494     },
47495     // private
47496     reset : function(){
47497         this.clearValue();
47498     },
47499     // private
47500     findRecord : function(prop, value){
47501         
47502         return false;
47503     
47504         var record;
47505         if(this.store.getCount() > 0){
47506             this.store.each(function(r){
47507                 if(r.data[prop] == value){
47508                     record = r;
47509                     return false;
47510                 }
47511                 return true;
47512             });
47513         }
47514         return record;
47515     },
47516     
47517     getName: function()
47518     {
47519         // returns hidden if it's set..
47520         if (!this.rendered) {return ''};
47521         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47522         
47523     },
47524      
47525
47526     
47527
47528     // private
47529     onEmptyResults : function(){
47530         Roo.log('empty results');
47531         //this.collapse();
47532     },
47533
47534     /**
47535      * Returns true if the dropdown list is expanded, else false.
47536      */
47537     isExpanded : function(){
47538         return false;
47539     },
47540
47541     /**
47542      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47543      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47544      * @param {String} value The data value of the item to select
47545      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47546      * selected item if it is not currently in view (defaults to true)
47547      * @return {Boolean} True if the value matched an item in the list, else false
47548      */
47549     selectByValue : function(v, scrollIntoView){
47550         Roo.log('select By Value');
47551         return false;
47552     
47553         if(v !== undefined && v !== null){
47554             var r = this.findRecord(this.valueField || this.displayField, v);
47555             if(r){
47556                 this.select(this.store.indexOf(r), scrollIntoView);
47557                 return true;
47558             }
47559         }
47560         return false;
47561     },
47562
47563     /**
47564      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47565      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47566      * @param {Number} index The zero-based index of the list item to select
47567      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47568      * selected item if it is not currently in view (defaults to true)
47569      */
47570     select : function(index, scrollIntoView){
47571         Roo.log('select ');
47572         return  ;
47573         
47574         this.selectedIndex = index;
47575         this.view.select(index);
47576         if(scrollIntoView !== false){
47577             var el = this.view.getNode(index);
47578             if(el){
47579                 this.innerList.scrollChildIntoView(el, false);
47580             }
47581         }
47582     },
47583
47584       
47585
47586     // private
47587     validateBlur : function(){
47588         
47589         return;
47590         
47591     },
47592
47593     // private
47594     initQuery : function(){
47595         this.doQuery(this.getRawValue());
47596     },
47597
47598     // private
47599     doForce : function(){
47600         if(this.el.dom.value.length > 0){
47601             this.el.dom.value =
47602                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47603              
47604         }
47605     },
47606
47607     /**
47608      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47609      * query allowing the query action to be canceled if needed.
47610      * @param {String} query The SQL query to execute
47611      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47612      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47613      * saved in the current store (defaults to false)
47614      */
47615     doQuery : function(q, forceAll){
47616         
47617         Roo.log('doQuery?');
47618         if(q === undefined || q === null){
47619             q = '';
47620         }
47621         var qe = {
47622             query: q,
47623             forceAll: forceAll,
47624             combo: this,
47625             cancel:false
47626         };
47627         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47628             return false;
47629         }
47630         q = qe.query;
47631         forceAll = qe.forceAll;
47632         if(forceAll === true || (q.length >= this.minChars)){
47633             if(this.lastQuery != q || this.alwaysQuery){
47634                 this.lastQuery = q;
47635                 if(this.mode == 'local'){
47636                     this.selectedIndex = -1;
47637                     if(forceAll){
47638                         this.store.clearFilter();
47639                     }else{
47640                         this.store.filter(this.displayField, q);
47641                     }
47642                     this.onLoad();
47643                 }else{
47644                     this.store.baseParams[this.queryParam] = q;
47645                     this.store.load({
47646                         params: this.getParams(q)
47647                     });
47648                     this.expand();
47649                 }
47650             }else{
47651                 this.selectedIndex = -1;
47652                 this.onLoad();   
47653             }
47654         }
47655     },
47656
47657     // private
47658     getParams : function(q){
47659         var p = {};
47660         //p[this.queryParam] = q;
47661         if(this.pageSize){
47662             p.start = 0;
47663             p.limit = this.pageSize;
47664         }
47665         return p;
47666     },
47667
47668     /**
47669      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47670      */
47671     collapse : function(){
47672         
47673     },
47674
47675     // private
47676     collapseIf : function(e){
47677         
47678     },
47679
47680     /**
47681      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47682      */
47683     expand : function(){
47684         
47685     } ,
47686
47687     // private
47688      
47689
47690     /** 
47691     * @cfg {Boolean} grow 
47692     * @hide 
47693     */
47694     /** 
47695     * @cfg {Number} growMin 
47696     * @hide 
47697     */
47698     /** 
47699     * @cfg {Number} growMax 
47700     * @hide 
47701     */
47702     /**
47703      * @hide
47704      * @method autoSize
47705      */
47706     
47707     setWidth : function()
47708     {
47709         
47710     },
47711     getResizeEl : function(){
47712         return this.el;
47713     }
47714 });//<script type="text/javasscript">
47715  
47716
47717 /**
47718  * @class Roo.DDView
47719  * A DnD enabled version of Roo.View.
47720  * @param {Element/String} container The Element in which to create the View.
47721  * @param {String} tpl The template string used to create the markup for each element of the View
47722  * @param {Object} config The configuration properties. These include all the config options of
47723  * {@link Roo.View} plus some specific to this class.<br>
47724  * <p>
47725  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47726  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47727  * <p>
47728  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47729 .x-view-drag-insert-above {
47730         border-top:1px dotted #3366cc;
47731 }
47732 .x-view-drag-insert-below {
47733         border-bottom:1px dotted #3366cc;
47734 }
47735 </code></pre>
47736  * 
47737  */
47738  
47739 Roo.DDView = function(container, tpl, config) {
47740     Roo.DDView.superclass.constructor.apply(this, arguments);
47741     this.getEl().setStyle("outline", "0px none");
47742     this.getEl().unselectable();
47743     if (this.dragGroup) {
47744                 this.setDraggable(this.dragGroup.split(","));
47745     }
47746     if (this.dropGroup) {
47747                 this.setDroppable(this.dropGroup.split(","));
47748     }
47749     if (this.deletable) {
47750         this.setDeletable();
47751     }
47752     this.isDirtyFlag = false;
47753         this.addEvents({
47754                 "drop" : true
47755         });
47756 };
47757
47758 Roo.extend(Roo.DDView, Roo.View, {
47759 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47760 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47761 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47762 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47763
47764         isFormField: true,
47765
47766         reset: Roo.emptyFn,
47767         
47768         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47769
47770         validate: function() {
47771                 return true;
47772         },
47773         
47774         destroy: function() {
47775                 this.purgeListeners();
47776                 this.getEl.removeAllListeners();
47777                 this.getEl().remove();
47778                 if (this.dragZone) {
47779                         if (this.dragZone.destroy) {
47780                                 this.dragZone.destroy();
47781                         }
47782                 }
47783                 if (this.dropZone) {
47784                         if (this.dropZone.destroy) {
47785                                 this.dropZone.destroy();
47786                         }
47787                 }
47788         },
47789
47790 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47791         getName: function() {
47792                 return this.name;
47793         },
47794
47795 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47796         setValue: function(v) {
47797                 if (!this.store) {
47798                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47799                 }
47800                 var data = {};
47801                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47802                 this.store.proxy = new Roo.data.MemoryProxy(data);
47803                 this.store.load();
47804         },
47805
47806 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47807         getValue: function() {
47808                 var result = '(';
47809                 this.store.each(function(rec) {
47810                         result += rec.id + ',';
47811                 });
47812                 return result.substr(0, result.length - 1) + ')';
47813         },
47814         
47815         getIds: function() {
47816                 var i = 0, result = new Array(this.store.getCount());
47817                 this.store.each(function(rec) {
47818                         result[i++] = rec.id;
47819                 });
47820                 return result;
47821         },
47822         
47823         isDirty: function() {
47824                 return this.isDirtyFlag;
47825         },
47826
47827 /**
47828  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47829  *      whole Element becomes the target, and this causes the drop gesture to append.
47830  */
47831     getTargetFromEvent : function(e) {
47832                 var target = e.getTarget();
47833                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47834                 target = target.parentNode;
47835                 }
47836                 if (!target) {
47837                         target = this.el.dom.lastChild || this.el.dom;
47838                 }
47839                 return target;
47840     },
47841
47842 /**
47843  *      Create the drag data which consists of an object which has the property "ddel" as
47844  *      the drag proxy element. 
47845  */
47846     getDragData : function(e) {
47847         var target = this.findItemFromChild(e.getTarget());
47848                 if(target) {
47849                         this.handleSelection(e);
47850                         var selNodes = this.getSelectedNodes();
47851             var dragData = {
47852                 source: this,
47853                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47854                 nodes: selNodes,
47855                 records: []
47856                         };
47857                         var selectedIndices = this.getSelectedIndexes();
47858                         for (var i = 0; i < selectedIndices.length; i++) {
47859                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47860                         }
47861                         if (selNodes.length == 1) {
47862                                 dragData.ddel = target.cloneNode(true); // the div element
47863                         } else {
47864                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47865                                 div.className = 'multi-proxy';
47866                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47867                                         div.appendChild(selNodes[i].cloneNode(true));
47868                                 }
47869                                 dragData.ddel = div;
47870                         }
47871             //console.log(dragData)
47872             //console.log(dragData.ddel.innerHTML)
47873                         return dragData;
47874                 }
47875         //console.log('nodragData')
47876                 return false;
47877     },
47878     
47879 /**     Specify to which ddGroup items in this DDView may be dragged. */
47880     setDraggable: function(ddGroup) {
47881         if (ddGroup instanceof Array) {
47882                 Roo.each(ddGroup, this.setDraggable, this);
47883                 return;
47884         }
47885         if (this.dragZone) {
47886                 this.dragZone.addToGroup(ddGroup);
47887         } else {
47888                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47889                                 containerScroll: true,
47890                                 ddGroup: ddGroup 
47891
47892                         });
47893 //                      Draggability implies selection. DragZone's mousedown selects the element.
47894                         if (!this.multiSelect) { this.singleSelect = true; }
47895
47896 //                      Wire the DragZone's handlers up to methods in *this*
47897                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47898                 }
47899     },
47900
47901 /**     Specify from which ddGroup this DDView accepts drops. */
47902     setDroppable: function(ddGroup) {
47903         if (ddGroup instanceof Array) {
47904                 Roo.each(ddGroup, this.setDroppable, this);
47905                 return;
47906         }
47907         if (this.dropZone) {
47908                 this.dropZone.addToGroup(ddGroup);
47909         } else {
47910                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47911                                 containerScroll: true,
47912                                 ddGroup: ddGroup
47913                         });
47914
47915 //                      Wire the DropZone's handlers up to methods in *this*
47916                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47917                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47918                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47919                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47920                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47921                 }
47922     },
47923
47924 /**     Decide whether to drop above or below a View node. */
47925     getDropPoint : function(e, n, dd){
47926         if (n == this.el.dom) { return "above"; }
47927                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47928                 var c = t + (b - t) / 2;
47929                 var y = Roo.lib.Event.getPageY(e);
47930                 if(y <= c) {
47931                         return "above";
47932                 }else{
47933                         return "below";
47934                 }
47935     },
47936
47937     onNodeEnter : function(n, dd, e, data){
47938                 return false;
47939     },
47940     
47941     onNodeOver : function(n, dd, e, data){
47942                 var pt = this.getDropPoint(e, n, dd);
47943                 // set the insert point style on the target node
47944                 var dragElClass = this.dropNotAllowed;
47945                 if (pt) {
47946                         var targetElClass;
47947                         if (pt == "above"){
47948                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47949                                 targetElClass = "x-view-drag-insert-above";
47950                         } else {
47951                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47952                                 targetElClass = "x-view-drag-insert-below";
47953                         }
47954                         if (this.lastInsertClass != targetElClass){
47955                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47956                                 this.lastInsertClass = targetElClass;
47957                         }
47958                 }
47959                 return dragElClass;
47960         },
47961
47962     onNodeOut : function(n, dd, e, data){
47963                 this.removeDropIndicators(n);
47964     },
47965
47966     onNodeDrop : function(n, dd, e, data){
47967         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47968                 return false;
47969         }
47970         var pt = this.getDropPoint(e, n, dd);
47971                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47972                 if (pt == "below") { insertAt++; }
47973                 for (var i = 0; i < data.records.length; i++) {
47974                         var r = data.records[i];
47975                         var dup = this.store.getById(r.id);
47976                         if (dup && (dd != this.dragZone)) {
47977                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
47978                         } else {
47979                                 if (data.copy) {
47980                                         this.store.insert(insertAt++, r.copy());
47981                                 } else {
47982                                         data.source.isDirtyFlag = true;
47983                                         r.store.remove(r);
47984                                         this.store.insert(insertAt++, r);
47985                                 }
47986                                 this.isDirtyFlag = true;
47987                         }
47988                 }
47989                 this.dragZone.cachedTarget = null;
47990                 return true;
47991     },
47992
47993     removeDropIndicators : function(n){
47994                 if(n){
47995                         Roo.fly(n).removeClass([
47996                                 "x-view-drag-insert-above",
47997                                 "x-view-drag-insert-below"]);
47998                         this.lastInsertClass = "_noclass";
47999                 }
48000     },
48001
48002 /**
48003  *      Utility method. Add a delete option to the DDView's context menu.
48004  *      @param {String} imageUrl The URL of the "delete" icon image.
48005  */
48006         setDeletable: function(imageUrl) {
48007                 if (!this.singleSelect && !this.multiSelect) {
48008                         this.singleSelect = true;
48009                 }
48010                 var c = this.getContextMenu();
48011                 this.contextMenu.on("itemclick", function(item) {
48012                         switch (item.id) {
48013                                 case "delete":
48014                                         this.remove(this.getSelectedIndexes());
48015                                         break;
48016                         }
48017                 }, this);
48018                 this.contextMenu.add({
48019                         icon: imageUrl,
48020                         id: "delete",
48021                         text: 'Delete'
48022                 });
48023         },
48024         
48025 /**     Return the context menu for this DDView. */
48026         getContextMenu: function() {
48027                 if (!this.contextMenu) {
48028 //                      Create the View's context menu
48029                         this.contextMenu = new Roo.menu.Menu({
48030                                 id: this.id + "-contextmenu"
48031                         });
48032                         this.el.on("contextmenu", this.showContextMenu, this);
48033                 }
48034                 return this.contextMenu;
48035         },
48036         
48037         disableContextMenu: function() {
48038                 if (this.contextMenu) {
48039                         this.el.un("contextmenu", this.showContextMenu, this);
48040                 }
48041         },
48042
48043         showContextMenu: function(e, item) {
48044         item = this.findItemFromChild(e.getTarget());
48045                 if (item) {
48046                         e.stopEvent();
48047                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48048                         this.contextMenu.showAt(e.getXY());
48049             }
48050     },
48051
48052 /**
48053  *      Remove {@link Roo.data.Record}s at the specified indices.
48054  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48055  */
48056     remove: function(selectedIndices) {
48057                 selectedIndices = [].concat(selectedIndices);
48058                 for (var i = 0; i < selectedIndices.length; i++) {
48059                         var rec = this.store.getAt(selectedIndices[i]);
48060                         this.store.remove(rec);
48061                 }
48062     },
48063
48064 /**
48065  *      Double click fires the event, but also, if this is draggable, and there is only one other
48066  *      related DropZone, it transfers the selected node.
48067  */
48068     onDblClick : function(e){
48069         var item = this.findItemFromChild(e.getTarget());
48070         if(item){
48071             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48072                 return false;
48073             }
48074             if (this.dragGroup) {
48075                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48076                     while (targets.indexOf(this.dropZone) > -1) {
48077                             targets.remove(this.dropZone);
48078                                 }
48079                     if (targets.length == 1) {
48080                                         this.dragZone.cachedTarget = null;
48081                         var el = Roo.get(targets[0].getEl());
48082                         var box = el.getBox(true);
48083                         targets[0].onNodeDrop(el.dom, {
48084                                 target: el.dom,
48085                                 xy: [box.x, box.y + box.height - 1]
48086                         }, null, this.getDragData(e));
48087                     }
48088                 }
48089         }
48090     },
48091     
48092     handleSelection: function(e) {
48093                 this.dragZone.cachedTarget = null;
48094         var item = this.findItemFromChild(e.getTarget());
48095         if (!item) {
48096                 this.clearSelections(true);
48097                 return;
48098         }
48099                 if (item && (this.multiSelect || this.singleSelect)){
48100                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48101                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48102                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48103                                 this.unselect(item);
48104                         } else {
48105                                 this.select(item, this.multiSelect && e.ctrlKey);
48106                                 this.lastSelection = item;
48107                         }
48108                 }
48109     },
48110
48111     onItemClick : function(item, index, e){
48112                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48113                         return false;
48114                 }
48115                 return true;
48116     },
48117
48118     unselect : function(nodeInfo, suppressEvent){
48119                 var node = this.getNode(nodeInfo);
48120                 if(node && this.isSelected(node)){
48121                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48122                                 Roo.fly(node).removeClass(this.selectedClass);
48123                                 this.selections.remove(node);
48124                                 if(!suppressEvent){
48125                                         this.fireEvent("selectionchange", this, this.selections);
48126                                 }
48127                         }
48128                 }
48129     }
48130 });
48131 /*
48132  * Based on:
48133  * Ext JS Library 1.1.1
48134  * Copyright(c) 2006-2007, Ext JS, LLC.
48135  *
48136  * Originally Released Under LGPL - original licence link has changed is not relivant.
48137  *
48138  * Fork - LGPL
48139  * <script type="text/javascript">
48140  */
48141  
48142 /**
48143  * @class Roo.LayoutManager
48144  * @extends Roo.util.Observable
48145  * Base class for layout managers.
48146  */
48147 Roo.LayoutManager = function(container, config){
48148     Roo.LayoutManager.superclass.constructor.call(this);
48149     this.el = Roo.get(container);
48150     // ie scrollbar fix
48151     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48152         document.body.scroll = "no";
48153     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48154         this.el.position('relative');
48155     }
48156     this.id = this.el.id;
48157     this.el.addClass("x-layout-container");
48158     /** false to disable window resize monitoring @type Boolean */
48159     this.monitorWindowResize = true;
48160     this.regions = {};
48161     this.addEvents({
48162         /**
48163          * @event layout
48164          * Fires when a layout is performed. 
48165          * @param {Roo.LayoutManager} this
48166          */
48167         "layout" : true,
48168         /**
48169          * @event regionresized
48170          * Fires when the user resizes a region. 
48171          * @param {Roo.LayoutRegion} region The resized region
48172          * @param {Number} newSize The new size (width for east/west, height for north/south)
48173          */
48174         "regionresized" : true,
48175         /**
48176          * @event regioncollapsed
48177          * Fires when a region is collapsed. 
48178          * @param {Roo.LayoutRegion} region The collapsed region
48179          */
48180         "regioncollapsed" : true,
48181         /**
48182          * @event regionexpanded
48183          * Fires when a region is expanded.  
48184          * @param {Roo.LayoutRegion} region The expanded region
48185          */
48186         "regionexpanded" : true
48187     });
48188     this.updating = false;
48189     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48190 };
48191
48192 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48193     /**
48194      * Returns true if this layout is currently being updated
48195      * @return {Boolean}
48196      */
48197     isUpdating : function(){
48198         return this.updating; 
48199     },
48200     
48201     /**
48202      * Suspend the LayoutManager from doing auto-layouts while
48203      * making multiple add or remove calls
48204      */
48205     beginUpdate : function(){
48206         this.updating = true;    
48207     },
48208     
48209     /**
48210      * Restore auto-layouts and optionally disable the manager from performing a layout
48211      * @param {Boolean} noLayout true to disable a layout update 
48212      */
48213     endUpdate : function(noLayout){
48214         this.updating = false;
48215         if(!noLayout){
48216             this.layout();
48217         }    
48218     },
48219     
48220     layout: function(){
48221         
48222     },
48223     
48224     onRegionResized : function(region, newSize){
48225         this.fireEvent("regionresized", region, newSize);
48226         this.layout();
48227     },
48228     
48229     onRegionCollapsed : function(region){
48230         this.fireEvent("regioncollapsed", region);
48231     },
48232     
48233     onRegionExpanded : function(region){
48234         this.fireEvent("regionexpanded", region);
48235     },
48236         
48237     /**
48238      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48239      * performs box-model adjustments.
48240      * @return {Object} The size as an object {width: (the width), height: (the height)}
48241      */
48242     getViewSize : function(){
48243         var size;
48244         if(this.el.dom != document.body){
48245             size = this.el.getSize();
48246         }else{
48247             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48248         }
48249         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48250         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48251         return size;
48252     },
48253     
48254     /**
48255      * Returns the Element this layout is bound to.
48256      * @return {Roo.Element}
48257      */
48258     getEl : function(){
48259         return this.el;
48260     },
48261     
48262     /**
48263      * Returns the specified region.
48264      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48265      * @return {Roo.LayoutRegion}
48266      */
48267     getRegion : function(target){
48268         return this.regions[target.toLowerCase()];
48269     },
48270     
48271     onWindowResize : function(){
48272         if(this.monitorWindowResize){
48273             this.layout();
48274         }
48275     }
48276 });/*
48277  * Based on:
48278  * Ext JS Library 1.1.1
48279  * Copyright(c) 2006-2007, Ext JS, LLC.
48280  *
48281  * Originally Released Under LGPL - original licence link has changed is not relivant.
48282  *
48283  * Fork - LGPL
48284  * <script type="text/javascript">
48285  */
48286 /**
48287  * @class Roo.BorderLayout
48288  * @extends Roo.LayoutManager
48289  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48290  * please see: <br><br>
48291  * <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>
48292  * <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>
48293  * Example:
48294  <pre><code>
48295  var layout = new Roo.BorderLayout(document.body, {
48296     north: {
48297         initialSize: 25,
48298         titlebar: false
48299     },
48300     west: {
48301         split:true,
48302         initialSize: 200,
48303         minSize: 175,
48304         maxSize: 400,
48305         titlebar: true,
48306         collapsible: true
48307     },
48308     east: {
48309         split:true,
48310         initialSize: 202,
48311         minSize: 175,
48312         maxSize: 400,
48313         titlebar: true,
48314         collapsible: true
48315     },
48316     south: {
48317         split:true,
48318         initialSize: 100,
48319         minSize: 100,
48320         maxSize: 200,
48321         titlebar: true,
48322         collapsible: true
48323     },
48324     center: {
48325         titlebar: true,
48326         autoScroll:true,
48327         resizeTabs: true,
48328         minTabWidth: 50,
48329         preferredTabWidth: 150
48330     }
48331 });
48332
48333 // shorthand
48334 var CP = Roo.ContentPanel;
48335
48336 layout.beginUpdate();
48337 layout.add("north", new CP("north", "North"));
48338 layout.add("south", new CP("south", {title: "South", closable: true}));
48339 layout.add("west", new CP("west", {title: "West"}));
48340 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48341 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48342 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48343 layout.getRegion("center").showPanel("center1");
48344 layout.endUpdate();
48345 </code></pre>
48346
48347 <b>The container the layout is rendered into can be either the body element or any other element.
48348 If it is not the body element, the container needs to either be an absolute positioned element,
48349 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48350 the container size if it is not the body element.</b>
48351
48352 * @constructor
48353 * Create a new BorderLayout
48354 * @param {String/HTMLElement/Element} container The container this layout is bound to
48355 * @param {Object} config Configuration options
48356  */
48357 Roo.BorderLayout = function(container, config){
48358     config = config || {};
48359     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48360     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48361     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48362         var target = this.factory.validRegions[i];
48363         if(config[target]){
48364             this.addRegion(target, config[target]);
48365         }
48366     }
48367 };
48368
48369 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48370     /**
48371      * Creates and adds a new region if it doesn't already exist.
48372      * @param {String} target The target region key (north, south, east, west or center).
48373      * @param {Object} config The regions config object
48374      * @return {BorderLayoutRegion} The new region
48375      */
48376     addRegion : function(target, config){
48377         if(!this.regions[target]){
48378             var r = this.factory.create(target, this, config);
48379             this.bindRegion(target, r);
48380         }
48381         return this.regions[target];
48382     },
48383
48384     // private (kinda)
48385     bindRegion : function(name, r){
48386         this.regions[name] = r;
48387         r.on("visibilitychange", this.layout, this);
48388         r.on("paneladded", this.layout, this);
48389         r.on("panelremoved", this.layout, this);
48390         r.on("invalidated", this.layout, this);
48391         r.on("resized", this.onRegionResized, this);
48392         r.on("collapsed", this.onRegionCollapsed, this);
48393         r.on("expanded", this.onRegionExpanded, this);
48394     },
48395
48396     /**
48397      * Performs a layout update.
48398      */
48399     layout : function(){
48400         if(this.updating) return;
48401         var size = this.getViewSize();
48402         var w = size.width;
48403         var h = size.height;
48404         var centerW = w;
48405         var centerH = h;
48406         var centerY = 0;
48407         var centerX = 0;
48408         //var x = 0, y = 0;
48409
48410         var rs = this.regions;
48411         var north = rs["north"];
48412         var south = rs["south"]; 
48413         var west = rs["west"];
48414         var east = rs["east"];
48415         var center = rs["center"];
48416         //if(this.hideOnLayout){ // not supported anymore
48417             //c.el.setStyle("display", "none");
48418         //}
48419         if(north && north.isVisible()){
48420             var b = north.getBox();
48421             var m = north.getMargins();
48422             b.width = w - (m.left+m.right);
48423             b.x = m.left;
48424             b.y = m.top;
48425             centerY = b.height + b.y + m.bottom;
48426             centerH -= centerY;
48427             north.updateBox(this.safeBox(b));
48428         }
48429         if(south && south.isVisible()){
48430             var b = south.getBox();
48431             var m = south.getMargins();
48432             b.width = w - (m.left+m.right);
48433             b.x = m.left;
48434             var totalHeight = (b.height + m.top + m.bottom);
48435             b.y = h - totalHeight + m.top;
48436             centerH -= totalHeight;
48437             south.updateBox(this.safeBox(b));
48438         }
48439         if(west && west.isVisible()){
48440             var b = west.getBox();
48441             var m = west.getMargins();
48442             b.height = centerH - (m.top+m.bottom);
48443             b.x = m.left;
48444             b.y = centerY + m.top;
48445             var totalWidth = (b.width + m.left + m.right);
48446             centerX += totalWidth;
48447             centerW -= totalWidth;
48448             west.updateBox(this.safeBox(b));
48449         }
48450         if(east && east.isVisible()){
48451             var b = east.getBox();
48452             var m = east.getMargins();
48453             b.height = centerH - (m.top+m.bottom);
48454             var totalWidth = (b.width + m.left + m.right);
48455             b.x = w - totalWidth + m.left;
48456             b.y = centerY + m.top;
48457             centerW -= totalWidth;
48458             east.updateBox(this.safeBox(b));
48459         }
48460         if(center){
48461             var m = center.getMargins();
48462             var centerBox = {
48463                 x: centerX + m.left,
48464                 y: centerY + m.top,
48465                 width: centerW - (m.left+m.right),
48466                 height: centerH - (m.top+m.bottom)
48467             };
48468             //if(this.hideOnLayout){
48469                 //center.el.setStyle("display", "block");
48470             //}
48471             center.updateBox(this.safeBox(centerBox));
48472         }
48473         this.el.repaint();
48474         this.fireEvent("layout", this);
48475     },
48476
48477     // private
48478     safeBox : function(box){
48479         box.width = Math.max(0, box.width);
48480         box.height = Math.max(0, box.height);
48481         return box;
48482     },
48483
48484     /**
48485      * Adds a ContentPanel (or subclass) to this layout.
48486      * @param {String} target The target region key (north, south, east, west or center).
48487      * @param {Roo.ContentPanel} panel The panel to add
48488      * @return {Roo.ContentPanel} The added panel
48489      */
48490     add : function(target, panel){
48491          
48492         target = target.toLowerCase();
48493         return this.regions[target].add(panel);
48494     },
48495
48496     /**
48497      * Remove a ContentPanel (or subclass) to this layout.
48498      * @param {String} target The target region key (north, south, east, west or center).
48499      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48500      * @return {Roo.ContentPanel} The removed panel
48501      */
48502     remove : function(target, panel){
48503         target = target.toLowerCase();
48504         return this.regions[target].remove(panel);
48505     },
48506
48507     /**
48508      * Searches all regions for a panel with the specified id
48509      * @param {String} panelId
48510      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48511      */
48512     findPanel : function(panelId){
48513         var rs = this.regions;
48514         for(var target in rs){
48515             if(typeof rs[target] != "function"){
48516                 var p = rs[target].getPanel(panelId);
48517                 if(p){
48518                     return p;
48519                 }
48520             }
48521         }
48522         return null;
48523     },
48524
48525     /**
48526      * Searches all regions for a panel with the specified id and activates (shows) it.
48527      * @param {String/ContentPanel} panelId The panels id or the panel itself
48528      * @return {Roo.ContentPanel} The shown panel or null
48529      */
48530     showPanel : function(panelId) {
48531       var rs = this.regions;
48532       for(var target in rs){
48533          var r = rs[target];
48534          if(typeof r != "function"){
48535             if(r.hasPanel(panelId)){
48536                return r.showPanel(panelId);
48537             }
48538          }
48539       }
48540       return null;
48541    },
48542
48543    /**
48544      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48545      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48546      */
48547     restoreState : function(provider){
48548         if(!provider){
48549             provider = Roo.state.Manager;
48550         }
48551         var sm = new Roo.LayoutStateManager();
48552         sm.init(this, provider);
48553     },
48554
48555     /**
48556      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48557      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48558      * a valid ContentPanel config object.  Example:
48559      * <pre><code>
48560 // Create the main layout
48561 var layout = new Roo.BorderLayout('main-ct', {
48562     west: {
48563         split:true,
48564         minSize: 175,
48565         titlebar: true
48566     },
48567     center: {
48568         title:'Components'
48569     }
48570 }, 'main-ct');
48571
48572 // Create and add multiple ContentPanels at once via configs
48573 layout.batchAdd({
48574    west: {
48575        id: 'source-files',
48576        autoCreate:true,
48577        title:'Ext Source Files',
48578        autoScroll:true,
48579        fitToFrame:true
48580    },
48581    center : {
48582        el: cview,
48583        autoScroll:true,
48584        fitToFrame:true,
48585        toolbar: tb,
48586        resizeEl:'cbody'
48587    }
48588 });
48589 </code></pre>
48590      * @param {Object} regions An object containing ContentPanel configs by region name
48591      */
48592     batchAdd : function(regions){
48593         this.beginUpdate();
48594         for(var rname in regions){
48595             var lr = this.regions[rname];
48596             if(lr){
48597                 this.addTypedPanels(lr, regions[rname]);
48598             }
48599         }
48600         this.endUpdate();
48601     },
48602
48603     // private
48604     addTypedPanels : function(lr, ps){
48605         if(typeof ps == 'string'){
48606             lr.add(new Roo.ContentPanel(ps));
48607         }
48608         else if(ps instanceof Array){
48609             for(var i =0, len = ps.length; i < len; i++){
48610                 this.addTypedPanels(lr, ps[i]);
48611             }
48612         }
48613         else if(!ps.events){ // raw config?
48614             var el = ps.el;
48615             delete ps.el; // prevent conflict
48616             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48617         }
48618         else {  // panel object assumed!
48619             lr.add(ps);
48620         }
48621     },
48622     /**
48623      * Adds a xtype elements to the layout.
48624      * <pre><code>
48625
48626 layout.addxtype({
48627        xtype : 'ContentPanel',
48628        region: 'west',
48629        items: [ .... ]
48630    }
48631 );
48632
48633 layout.addxtype({
48634         xtype : 'NestedLayoutPanel',
48635         region: 'west',
48636         layout: {
48637            center: { },
48638            west: { }   
48639         },
48640         items : [ ... list of content panels or nested layout panels.. ]
48641    }
48642 );
48643 </code></pre>
48644      * @param {Object} cfg Xtype definition of item to add.
48645      */
48646     addxtype : function(cfg)
48647     {
48648         // basically accepts a pannel...
48649         // can accept a layout region..!?!?
48650         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48651         
48652         if (!cfg.xtype.match(/Panel$/)) {
48653             return false;
48654         }
48655         var ret = false;
48656         
48657         if (typeof(cfg.region) == 'undefined') {
48658             Roo.log("Failed to add Panel, region was not set");
48659             Roo.log(cfg);
48660             return false;
48661         }
48662         var region = cfg.region;
48663         delete cfg.region;
48664         
48665           
48666         var xitems = [];
48667         if (cfg.items) {
48668             xitems = cfg.items;
48669             delete cfg.items;
48670         }
48671         var nb = false;
48672         
48673         switch(cfg.xtype) 
48674         {
48675             case 'ContentPanel':  // ContentPanel (el, cfg)
48676             case 'ScrollPanel':  // ContentPanel (el, cfg)
48677             case 'ViewPanel': 
48678                 if(cfg.autoCreate) {
48679                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48680                 } else {
48681                     var el = this.el.createChild();
48682                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48683                 }
48684                 
48685                 this.add(region, ret);
48686                 break;
48687             
48688             
48689             case 'TreePanel': // our new panel!
48690                 cfg.el = this.el.createChild();
48691                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48692                 this.add(region, ret);
48693                 break;
48694             
48695             case 'NestedLayoutPanel': 
48696                 // create a new Layout (which is  a Border Layout...
48697                 var el = this.el.createChild();
48698                 var clayout = cfg.layout;
48699                 delete cfg.layout;
48700                 clayout.items   = clayout.items  || [];
48701                 // replace this exitems with the clayout ones..
48702                 xitems = clayout.items;
48703                  
48704                 
48705                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48706                     cfg.background = false;
48707                 }
48708                 var layout = new Roo.BorderLayout(el, clayout);
48709                 
48710                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48711                 //console.log('adding nested layout panel '  + cfg.toSource());
48712                 this.add(region, ret);
48713                 nb = {}; /// find first...
48714                 break;
48715                 
48716             case 'GridPanel': 
48717             
48718                 // needs grid and region
48719                 
48720                 //var el = this.getRegion(region).el.createChild();
48721                 var el = this.el.createChild();
48722                 // create the grid first...
48723                 
48724                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48725                 delete cfg.grid;
48726                 if (region == 'center' && this.active ) {
48727                     cfg.background = false;
48728                 }
48729                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48730                 
48731                 this.add(region, ret);
48732                 if (cfg.background) {
48733                     ret.on('activate', function(gp) {
48734                         if (!gp.grid.rendered) {
48735                             gp.grid.render();
48736                         }
48737                     });
48738                 } else {
48739                     grid.render();
48740                 }
48741                 break;
48742            
48743            
48744            
48745                 
48746                 
48747                 
48748             default:
48749                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
48750                     
48751                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48752                     this.add(region, ret);
48753                 } else {
48754                 
48755                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48756                     return null;
48757                 }
48758                 
48759              // GridPanel (grid, cfg)
48760             
48761         }
48762         this.beginUpdate();
48763         // add children..
48764         var region = '';
48765         var abn = {};
48766         Roo.each(xitems, function(i)  {
48767             region = nb && i.region ? i.region : false;
48768             
48769             var add = ret.addxtype(i);
48770            
48771             if (region) {
48772                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48773                 if (!i.background) {
48774                     abn[region] = nb[region] ;
48775                 }
48776             }
48777             
48778         });
48779         this.endUpdate();
48780
48781         // make the last non-background panel active..
48782         //if (nb) { Roo.log(abn); }
48783         if (nb) {
48784             
48785             for(var r in abn) {
48786                 region = this.getRegion(r);
48787                 if (region) {
48788                     // tried using nb[r], but it does not work..
48789                      
48790                     region.showPanel(abn[r]);
48791                    
48792                 }
48793             }
48794         }
48795         return ret;
48796         
48797     }
48798 });
48799
48800 /**
48801  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48802  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48803  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48804  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48805  * <pre><code>
48806 // shorthand
48807 var CP = Roo.ContentPanel;
48808
48809 var layout = Roo.BorderLayout.create({
48810     north: {
48811         initialSize: 25,
48812         titlebar: false,
48813         panels: [new CP("north", "North")]
48814     },
48815     west: {
48816         split:true,
48817         initialSize: 200,
48818         minSize: 175,
48819         maxSize: 400,
48820         titlebar: true,
48821         collapsible: true,
48822         panels: [new CP("west", {title: "West"})]
48823     },
48824     east: {
48825         split:true,
48826         initialSize: 202,
48827         minSize: 175,
48828         maxSize: 400,
48829         titlebar: true,
48830         collapsible: true,
48831         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48832     },
48833     south: {
48834         split:true,
48835         initialSize: 100,
48836         minSize: 100,
48837         maxSize: 200,
48838         titlebar: true,
48839         collapsible: true,
48840         panels: [new CP("south", {title: "South", closable: true})]
48841     },
48842     center: {
48843         titlebar: true,
48844         autoScroll:true,
48845         resizeTabs: true,
48846         minTabWidth: 50,
48847         preferredTabWidth: 150,
48848         panels: [
48849             new CP("center1", {title: "Close Me", closable: true}),
48850             new CP("center2", {title: "Center Panel", closable: false})
48851         ]
48852     }
48853 }, document.body);
48854
48855 layout.getRegion("center").showPanel("center1");
48856 </code></pre>
48857  * @param config
48858  * @param targetEl
48859  */
48860 Roo.BorderLayout.create = function(config, targetEl){
48861     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48862     layout.beginUpdate();
48863     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48864     for(var j = 0, jlen = regions.length; j < jlen; j++){
48865         var lr = regions[j];
48866         if(layout.regions[lr] && config[lr].panels){
48867             var r = layout.regions[lr];
48868             var ps = config[lr].panels;
48869             layout.addTypedPanels(r, ps);
48870         }
48871     }
48872     layout.endUpdate();
48873     return layout;
48874 };
48875
48876 // private
48877 Roo.BorderLayout.RegionFactory = {
48878     // private
48879     validRegions : ["north","south","east","west","center"],
48880
48881     // private
48882     create : function(target, mgr, config){
48883         target = target.toLowerCase();
48884         if(config.lightweight || config.basic){
48885             return new Roo.BasicLayoutRegion(mgr, config, target);
48886         }
48887         switch(target){
48888             case "north":
48889                 return new Roo.NorthLayoutRegion(mgr, config);
48890             case "south":
48891                 return new Roo.SouthLayoutRegion(mgr, config);
48892             case "east":
48893                 return new Roo.EastLayoutRegion(mgr, config);
48894             case "west":
48895                 return new Roo.WestLayoutRegion(mgr, config);
48896             case "center":
48897                 return new Roo.CenterLayoutRegion(mgr, config);
48898         }
48899         throw 'Layout region "'+target+'" not supported.';
48900     }
48901 };/*
48902  * Based on:
48903  * Ext JS Library 1.1.1
48904  * Copyright(c) 2006-2007, Ext JS, LLC.
48905  *
48906  * Originally Released Under LGPL - original licence link has changed is not relivant.
48907  *
48908  * Fork - LGPL
48909  * <script type="text/javascript">
48910  */
48911  
48912 /**
48913  * @class Roo.BasicLayoutRegion
48914  * @extends Roo.util.Observable
48915  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48916  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48917  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48918  */
48919 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48920     this.mgr = mgr;
48921     this.position  = pos;
48922     this.events = {
48923         /**
48924          * @scope Roo.BasicLayoutRegion
48925          */
48926         
48927         /**
48928          * @event beforeremove
48929          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48930          * @param {Roo.LayoutRegion} this
48931          * @param {Roo.ContentPanel} panel The panel
48932          * @param {Object} e The cancel event object
48933          */
48934         "beforeremove" : true,
48935         /**
48936          * @event invalidated
48937          * Fires when the layout for this region is changed.
48938          * @param {Roo.LayoutRegion} this
48939          */
48940         "invalidated" : true,
48941         /**
48942          * @event visibilitychange
48943          * Fires when this region is shown or hidden 
48944          * @param {Roo.LayoutRegion} this
48945          * @param {Boolean} visibility true or false
48946          */
48947         "visibilitychange" : true,
48948         /**
48949          * @event paneladded
48950          * Fires when a panel is added. 
48951          * @param {Roo.LayoutRegion} this
48952          * @param {Roo.ContentPanel} panel The panel
48953          */
48954         "paneladded" : true,
48955         /**
48956          * @event panelremoved
48957          * Fires when a panel is removed. 
48958          * @param {Roo.LayoutRegion} this
48959          * @param {Roo.ContentPanel} panel The panel
48960          */
48961         "panelremoved" : true,
48962         /**
48963          * @event collapsed
48964          * Fires when this region is collapsed.
48965          * @param {Roo.LayoutRegion} this
48966          */
48967         "collapsed" : true,
48968         /**
48969          * @event expanded
48970          * Fires when this region is expanded.
48971          * @param {Roo.LayoutRegion} this
48972          */
48973         "expanded" : true,
48974         /**
48975          * @event slideshow
48976          * Fires when this region is slid into view.
48977          * @param {Roo.LayoutRegion} this
48978          */
48979         "slideshow" : true,
48980         /**
48981          * @event slidehide
48982          * Fires when this region slides out of view. 
48983          * @param {Roo.LayoutRegion} this
48984          */
48985         "slidehide" : true,
48986         /**
48987          * @event panelactivated
48988          * Fires when a panel is activated. 
48989          * @param {Roo.LayoutRegion} this
48990          * @param {Roo.ContentPanel} panel The activated panel
48991          */
48992         "panelactivated" : true,
48993         /**
48994          * @event resized
48995          * Fires when the user resizes this region. 
48996          * @param {Roo.LayoutRegion} this
48997          * @param {Number} newSize The new size (width for east/west, height for north/south)
48998          */
48999         "resized" : true
49000     };
49001     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49002     this.panels = new Roo.util.MixedCollection();
49003     this.panels.getKey = this.getPanelId.createDelegate(this);
49004     this.box = null;
49005     this.activePanel = null;
49006     // ensure listeners are added...
49007     
49008     if (config.listeners || config.events) {
49009         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49010             listeners : config.listeners || {},
49011             events : config.events || {}
49012         });
49013     }
49014     
49015     if(skipConfig !== true){
49016         this.applyConfig(config);
49017     }
49018 };
49019
49020 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49021     getPanelId : function(p){
49022         return p.getId();
49023     },
49024     
49025     applyConfig : function(config){
49026         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49027         this.config = config;
49028         
49029     },
49030     
49031     /**
49032      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49033      * the width, for horizontal (north, south) the height.
49034      * @param {Number} newSize The new width or height
49035      */
49036     resizeTo : function(newSize){
49037         var el = this.el ? this.el :
49038                  (this.activePanel ? this.activePanel.getEl() : null);
49039         if(el){
49040             switch(this.position){
49041                 case "east":
49042                 case "west":
49043                     el.setWidth(newSize);
49044                     this.fireEvent("resized", this, newSize);
49045                 break;
49046                 case "north":
49047                 case "south":
49048                     el.setHeight(newSize);
49049                     this.fireEvent("resized", this, newSize);
49050                 break;                
49051             }
49052         }
49053     },
49054     
49055     getBox : function(){
49056         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49057     },
49058     
49059     getMargins : function(){
49060         return this.margins;
49061     },
49062     
49063     updateBox : function(box){
49064         this.box = box;
49065         var el = this.activePanel.getEl();
49066         el.dom.style.left = box.x + "px";
49067         el.dom.style.top = box.y + "px";
49068         this.activePanel.setSize(box.width, box.height);
49069     },
49070     
49071     /**
49072      * Returns the container element for this region.
49073      * @return {Roo.Element}
49074      */
49075     getEl : function(){
49076         return this.activePanel;
49077     },
49078     
49079     /**
49080      * Returns true if this region is currently visible.
49081      * @return {Boolean}
49082      */
49083     isVisible : function(){
49084         return this.activePanel ? true : false;
49085     },
49086     
49087     setActivePanel : function(panel){
49088         panel = this.getPanel(panel);
49089         if(this.activePanel && this.activePanel != panel){
49090             this.activePanel.setActiveState(false);
49091             this.activePanel.getEl().setLeftTop(-10000,-10000);
49092         }
49093         this.activePanel = panel;
49094         panel.setActiveState(true);
49095         if(this.box){
49096             panel.setSize(this.box.width, this.box.height);
49097         }
49098         this.fireEvent("panelactivated", this, panel);
49099         this.fireEvent("invalidated");
49100     },
49101     
49102     /**
49103      * Show the specified panel.
49104      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49105      * @return {Roo.ContentPanel} The shown panel or null
49106      */
49107     showPanel : function(panel){
49108         if(panel = this.getPanel(panel)){
49109             this.setActivePanel(panel);
49110         }
49111         return panel;
49112     },
49113     
49114     /**
49115      * Get the active panel for this region.
49116      * @return {Roo.ContentPanel} The active panel or null
49117      */
49118     getActivePanel : function(){
49119         return this.activePanel;
49120     },
49121     
49122     /**
49123      * Add the passed ContentPanel(s)
49124      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49125      * @return {Roo.ContentPanel} The panel added (if only one was added)
49126      */
49127     add : function(panel){
49128         if(arguments.length > 1){
49129             for(var i = 0, len = arguments.length; i < len; i++) {
49130                 this.add(arguments[i]);
49131             }
49132             return null;
49133         }
49134         if(this.hasPanel(panel)){
49135             this.showPanel(panel);
49136             return panel;
49137         }
49138         var el = panel.getEl();
49139         if(el.dom.parentNode != this.mgr.el.dom){
49140             this.mgr.el.dom.appendChild(el.dom);
49141         }
49142         if(panel.setRegion){
49143             panel.setRegion(this);
49144         }
49145         this.panels.add(panel);
49146         el.setStyle("position", "absolute");
49147         if(!panel.background){
49148             this.setActivePanel(panel);
49149             if(this.config.initialSize && this.panels.getCount()==1){
49150                 this.resizeTo(this.config.initialSize);
49151             }
49152         }
49153         this.fireEvent("paneladded", this, panel);
49154         return panel;
49155     },
49156     
49157     /**
49158      * Returns true if the panel is in this region.
49159      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49160      * @return {Boolean}
49161      */
49162     hasPanel : function(panel){
49163         if(typeof panel == "object"){ // must be panel obj
49164             panel = panel.getId();
49165         }
49166         return this.getPanel(panel) ? true : false;
49167     },
49168     
49169     /**
49170      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49171      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49172      * @param {Boolean} preservePanel Overrides the config preservePanel option
49173      * @return {Roo.ContentPanel} The panel that was removed
49174      */
49175     remove : function(panel, preservePanel){
49176         panel = this.getPanel(panel);
49177         if(!panel){
49178             return null;
49179         }
49180         var e = {};
49181         this.fireEvent("beforeremove", this, panel, e);
49182         if(e.cancel === true){
49183             return null;
49184         }
49185         var panelId = panel.getId();
49186         this.panels.removeKey(panelId);
49187         return panel;
49188     },
49189     
49190     /**
49191      * Returns the panel specified or null if it's not in this region.
49192      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49193      * @return {Roo.ContentPanel}
49194      */
49195     getPanel : function(id){
49196         if(typeof id == "object"){ // must be panel obj
49197             return id;
49198         }
49199         return this.panels.get(id);
49200     },
49201     
49202     /**
49203      * Returns this regions position (north/south/east/west/center).
49204      * @return {String} 
49205      */
49206     getPosition: function(){
49207         return this.position;    
49208     }
49209 });/*
49210  * Based on:
49211  * Ext JS Library 1.1.1
49212  * Copyright(c) 2006-2007, Ext JS, LLC.
49213  *
49214  * Originally Released Under LGPL - original licence link has changed is not relivant.
49215  *
49216  * Fork - LGPL
49217  * <script type="text/javascript">
49218  */
49219  
49220 /**
49221  * @class Roo.LayoutRegion
49222  * @extends Roo.BasicLayoutRegion
49223  * This class represents a region in a layout manager.
49224  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49225  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49226  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49227  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49228  * @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})
49229  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49230  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49231  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49232  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49233  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49234  * @cfg {String}    title           The title for the region (overrides panel titles)
49235  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49236  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49237  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49238  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49239  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49240  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49241  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49242  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49243  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49244  * @cfg {Boolean}   showPin         True to show a pin button
49245  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49246  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49247  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49248  * @cfg {Number}    width           For East/West panels
49249  * @cfg {Number}    height          For North/South panels
49250  * @cfg {Boolean}   split           To show the splitter
49251  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49252  */
49253 Roo.LayoutRegion = function(mgr, config, pos){
49254     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49255     var dh = Roo.DomHelper;
49256     /** This region's container element 
49257     * @type Roo.Element */
49258     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49259     /** This region's title element 
49260     * @type Roo.Element */
49261
49262     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49263         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49264         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49265     ]}, true);
49266     this.titleEl.enableDisplayMode();
49267     /** This region's title text element 
49268     * @type HTMLElement */
49269     this.titleTextEl = this.titleEl.dom.firstChild;
49270     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49271     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49272     this.closeBtn.enableDisplayMode();
49273     this.closeBtn.on("click", this.closeClicked, this);
49274     this.closeBtn.hide();
49275
49276     this.createBody(config);
49277     this.visible = true;
49278     this.collapsed = false;
49279
49280     if(config.hideWhenEmpty){
49281         this.hide();
49282         this.on("paneladded", this.validateVisibility, this);
49283         this.on("panelremoved", this.validateVisibility, this);
49284     }
49285     this.applyConfig(config);
49286 };
49287
49288 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49289
49290     createBody : function(){
49291         /** This region's body element 
49292         * @type Roo.Element */
49293         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49294     },
49295
49296     applyConfig : function(c){
49297         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49298             var dh = Roo.DomHelper;
49299             if(c.titlebar !== false){
49300                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49301                 this.collapseBtn.on("click", this.collapse, this);
49302                 this.collapseBtn.enableDisplayMode();
49303
49304                 if(c.showPin === true || this.showPin){
49305                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49306                     this.stickBtn.enableDisplayMode();
49307                     this.stickBtn.on("click", this.expand, this);
49308                     this.stickBtn.hide();
49309                 }
49310             }
49311             /** This region's collapsed element
49312             * @type Roo.Element */
49313             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49314                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49315             ]}, true);
49316             if(c.floatable !== false){
49317                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49318                this.collapsedEl.on("click", this.collapseClick, this);
49319             }
49320
49321             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49322                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49323                    id: "message", unselectable: "on", style:{"float":"left"}});
49324                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49325              }
49326             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49327             this.expandBtn.on("click", this.expand, this);
49328         }
49329         if(this.collapseBtn){
49330             this.collapseBtn.setVisible(c.collapsible == true);
49331         }
49332         this.cmargins = c.cmargins || this.cmargins ||
49333                          (this.position == "west" || this.position == "east" ?
49334                              {top: 0, left: 2, right:2, bottom: 0} :
49335                              {top: 2, left: 0, right:0, bottom: 2});
49336         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49337         this.bottomTabs = c.tabPosition != "top";
49338         this.autoScroll = c.autoScroll || false;
49339         if(this.autoScroll){
49340             this.bodyEl.setStyle("overflow", "auto");
49341         }else{
49342             this.bodyEl.setStyle("overflow", "hidden");
49343         }
49344         //if(c.titlebar !== false){
49345             if((!c.titlebar && !c.title) || c.titlebar === false){
49346                 this.titleEl.hide();
49347             }else{
49348                 this.titleEl.show();
49349                 if(c.title){
49350                     this.titleTextEl.innerHTML = c.title;
49351                 }
49352             }
49353         //}
49354         this.duration = c.duration || .30;
49355         this.slideDuration = c.slideDuration || .45;
49356         this.config = c;
49357         if(c.collapsed){
49358             this.collapse(true);
49359         }
49360         if(c.hidden){
49361             this.hide();
49362         }
49363     },
49364     /**
49365      * Returns true if this region is currently visible.
49366      * @return {Boolean}
49367      */
49368     isVisible : function(){
49369         return this.visible;
49370     },
49371
49372     /**
49373      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49374      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49375      */
49376     setCollapsedTitle : function(title){
49377         title = title || "&#160;";
49378         if(this.collapsedTitleTextEl){
49379             this.collapsedTitleTextEl.innerHTML = title;
49380         }
49381     },
49382
49383     getBox : function(){
49384         var b;
49385         if(!this.collapsed){
49386             b = this.el.getBox(false, true);
49387         }else{
49388             b = this.collapsedEl.getBox(false, true);
49389         }
49390         return b;
49391     },
49392
49393     getMargins : function(){
49394         return this.collapsed ? this.cmargins : this.margins;
49395     },
49396
49397     highlight : function(){
49398         this.el.addClass("x-layout-panel-dragover");
49399     },
49400
49401     unhighlight : function(){
49402         this.el.removeClass("x-layout-panel-dragover");
49403     },
49404
49405     updateBox : function(box){
49406         this.box = box;
49407         if(!this.collapsed){
49408             this.el.dom.style.left = box.x + "px";
49409             this.el.dom.style.top = box.y + "px";
49410             this.updateBody(box.width, box.height);
49411         }else{
49412             this.collapsedEl.dom.style.left = box.x + "px";
49413             this.collapsedEl.dom.style.top = box.y + "px";
49414             this.collapsedEl.setSize(box.width, box.height);
49415         }
49416         if(this.tabs){
49417             this.tabs.autoSizeTabs();
49418         }
49419     },
49420
49421     updateBody : function(w, h){
49422         if(w !== null){
49423             this.el.setWidth(w);
49424             w -= this.el.getBorderWidth("rl");
49425             if(this.config.adjustments){
49426                 w += this.config.adjustments[0];
49427             }
49428         }
49429         if(h !== null){
49430             this.el.setHeight(h);
49431             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49432             h -= this.el.getBorderWidth("tb");
49433             if(this.config.adjustments){
49434                 h += this.config.adjustments[1];
49435             }
49436             this.bodyEl.setHeight(h);
49437             if(this.tabs){
49438                 h = this.tabs.syncHeight(h);
49439             }
49440         }
49441         if(this.panelSize){
49442             w = w !== null ? w : this.panelSize.width;
49443             h = h !== null ? h : this.panelSize.height;
49444         }
49445         if(this.activePanel){
49446             var el = this.activePanel.getEl();
49447             w = w !== null ? w : el.getWidth();
49448             h = h !== null ? h : el.getHeight();
49449             this.panelSize = {width: w, height: h};
49450             this.activePanel.setSize(w, h);
49451         }
49452         if(Roo.isIE && this.tabs){
49453             this.tabs.el.repaint();
49454         }
49455     },
49456
49457     /**
49458      * Returns the container element for this region.
49459      * @return {Roo.Element}
49460      */
49461     getEl : function(){
49462         return this.el;
49463     },
49464
49465     /**
49466      * Hides this region.
49467      */
49468     hide : function(){
49469         if(!this.collapsed){
49470             this.el.dom.style.left = "-2000px";
49471             this.el.hide();
49472         }else{
49473             this.collapsedEl.dom.style.left = "-2000px";
49474             this.collapsedEl.hide();
49475         }
49476         this.visible = false;
49477         this.fireEvent("visibilitychange", this, false);
49478     },
49479
49480     /**
49481      * Shows this region if it was previously hidden.
49482      */
49483     show : function(){
49484         if(!this.collapsed){
49485             this.el.show();
49486         }else{
49487             this.collapsedEl.show();
49488         }
49489         this.visible = true;
49490         this.fireEvent("visibilitychange", this, true);
49491     },
49492
49493     closeClicked : function(){
49494         if(this.activePanel){
49495             this.remove(this.activePanel);
49496         }
49497     },
49498
49499     collapseClick : function(e){
49500         if(this.isSlid){
49501            e.stopPropagation();
49502            this.slideIn();
49503         }else{
49504            e.stopPropagation();
49505            this.slideOut();
49506         }
49507     },
49508
49509     /**
49510      * Collapses this region.
49511      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49512      */
49513     collapse : function(skipAnim){
49514         if(this.collapsed) return;
49515         this.collapsed = true;
49516         if(this.split){
49517             this.split.el.hide();
49518         }
49519         if(this.config.animate && skipAnim !== true){
49520             this.fireEvent("invalidated", this);
49521             this.animateCollapse();
49522         }else{
49523             this.el.setLocation(-20000,-20000);
49524             this.el.hide();
49525             this.collapsedEl.show();
49526             this.fireEvent("collapsed", this);
49527             this.fireEvent("invalidated", this);
49528         }
49529     },
49530
49531     animateCollapse : function(){
49532         // overridden
49533     },
49534
49535     /**
49536      * Expands this region if it was previously collapsed.
49537      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49538      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49539      */
49540     expand : function(e, skipAnim){
49541         if(e) e.stopPropagation();
49542         if(!this.collapsed || this.el.hasActiveFx()) return;
49543         if(this.isSlid){
49544             this.afterSlideIn();
49545             skipAnim = true;
49546         }
49547         this.collapsed = false;
49548         if(this.config.animate && skipAnim !== true){
49549             this.animateExpand();
49550         }else{
49551             this.el.show();
49552             if(this.split){
49553                 this.split.el.show();
49554             }
49555             this.collapsedEl.setLocation(-2000,-2000);
49556             this.collapsedEl.hide();
49557             this.fireEvent("invalidated", this);
49558             this.fireEvent("expanded", this);
49559         }
49560     },
49561
49562     animateExpand : function(){
49563         // overridden
49564     },
49565
49566     initTabs : function()
49567     {
49568         this.bodyEl.setStyle("overflow", "hidden");
49569         var ts = new Roo.TabPanel(
49570                 this.bodyEl.dom,
49571                 {
49572                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49573                     disableTooltips: this.config.disableTabTips,
49574                     toolbar : this.config.toolbar
49575                 }
49576         );
49577         if(this.config.hideTabs){
49578             ts.stripWrap.setDisplayed(false);
49579         }
49580         this.tabs = ts;
49581         ts.resizeTabs = this.config.resizeTabs === true;
49582         ts.minTabWidth = this.config.minTabWidth || 40;
49583         ts.maxTabWidth = this.config.maxTabWidth || 250;
49584         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49585         ts.monitorResize = false;
49586         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49587         ts.bodyEl.addClass('x-layout-tabs-body');
49588         this.panels.each(this.initPanelAsTab, this);
49589     },
49590
49591     initPanelAsTab : function(panel){
49592         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49593                     this.config.closeOnTab && panel.isClosable());
49594         if(panel.tabTip !== undefined){
49595             ti.setTooltip(panel.tabTip);
49596         }
49597         ti.on("activate", function(){
49598               this.setActivePanel(panel);
49599         }, this);
49600         if(this.config.closeOnTab){
49601             ti.on("beforeclose", function(t, e){
49602                 e.cancel = true;
49603                 this.remove(panel);
49604             }, this);
49605         }
49606         return ti;
49607     },
49608
49609     updatePanelTitle : function(panel, title){
49610         if(this.activePanel == panel){
49611             this.updateTitle(title);
49612         }
49613         if(this.tabs){
49614             var ti = this.tabs.getTab(panel.getEl().id);
49615             ti.setText(title);
49616             if(panel.tabTip !== undefined){
49617                 ti.setTooltip(panel.tabTip);
49618             }
49619         }
49620     },
49621
49622     updateTitle : function(title){
49623         if(this.titleTextEl && !this.config.title){
49624             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49625         }
49626     },
49627
49628     setActivePanel : function(panel){
49629         panel = this.getPanel(panel);
49630         if(this.activePanel && this.activePanel != panel){
49631             this.activePanel.setActiveState(false);
49632         }
49633         this.activePanel = panel;
49634         panel.setActiveState(true);
49635         if(this.panelSize){
49636             panel.setSize(this.panelSize.width, this.panelSize.height);
49637         }
49638         if(this.closeBtn){
49639             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49640         }
49641         this.updateTitle(panel.getTitle());
49642         if(this.tabs){
49643             this.fireEvent("invalidated", this);
49644         }
49645         this.fireEvent("panelactivated", this, panel);
49646     },
49647
49648     /**
49649      * Shows the specified panel.
49650      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49651      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49652      */
49653     showPanel : function(panel){
49654         if(panel = this.getPanel(panel)){
49655             if(this.tabs){
49656                 var tab = this.tabs.getTab(panel.getEl().id);
49657                 if(tab.isHidden()){
49658                     this.tabs.unhideTab(tab.id);
49659                 }
49660                 tab.activate();
49661             }else{
49662                 this.setActivePanel(panel);
49663             }
49664         }
49665         return panel;
49666     },
49667
49668     /**
49669      * Get the active panel for this region.
49670      * @return {Roo.ContentPanel} The active panel or null
49671      */
49672     getActivePanel : function(){
49673         return this.activePanel;
49674     },
49675
49676     validateVisibility : function(){
49677         if(this.panels.getCount() < 1){
49678             this.updateTitle("&#160;");
49679             this.closeBtn.hide();
49680             this.hide();
49681         }else{
49682             if(!this.isVisible()){
49683                 this.show();
49684             }
49685         }
49686     },
49687
49688     /**
49689      * Adds the passed ContentPanel(s) to this region.
49690      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49691      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49692      */
49693     add : function(panel){
49694         if(arguments.length > 1){
49695             for(var i = 0, len = arguments.length; i < len; i++) {
49696                 this.add(arguments[i]);
49697             }
49698             return null;
49699         }
49700         if(this.hasPanel(panel)){
49701             this.showPanel(panel);
49702             return panel;
49703         }
49704         panel.setRegion(this);
49705         this.panels.add(panel);
49706         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49707             this.bodyEl.dom.appendChild(panel.getEl().dom);
49708             if(panel.background !== true){
49709                 this.setActivePanel(panel);
49710             }
49711             this.fireEvent("paneladded", this, panel);
49712             return panel;
49713         }
49714         if(!this.tabs){
49715             this.initTabs();
49716         }else{
49717             this.initPanelAsTab(panel);
49718         }
49719         if(panel.background !== true){
49720             this.tabs.activate(panel.getEl().id);
49721         }
49722         this.fireEvent("paneladded", this, panel);
49723         return panel;
49724     },
49725
49726     /**
49727      * Hides the tab for the specified panel.
49728      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49729      */
49730     hidePanel : function(panel){
49731         if(this.tabs && (panel = this.getPanel(panel))){
49732             this.tabs.hideTab(panel.getEl().id);
49733         }
49734     },
49735
49736     /**
49737      * Unhides the tab for a previously hidden panel.
49738      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49739      */
49740     unhidePanel : function(panel){
49741         if(this.tabs && (panel = this.getPanel(panel))){
49742             this.tabs.unhideTab(panel.getEl().id);
49743         }
49744     },
49745
49746     clearPanels : function(){
49747         while(this.panels.getCount() > 0){
49748              this.remove(this.panels.first());
49749         }
49750     },
49751
49752     /**
49753      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49754      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49755      * @param {Boolean} preservePanel Overrides the config preservePanel option
49756      * @return {Roo.ContentPanel} The panel that was removed
49757      */
49758     remove : function(panel, preservePanel){
49759         panel = this.getPanel(panel);
49760         if(!panel){
49761             return null;
49762         }
49763         var e = {};
49764         this.fireEvent("beforeremove", this, panel, e);
49765         if(e.cancel === true){
49766             return null;
49767         }
49768         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49769         var panelId = panel.getId();
49770         this.panels.removeKey(panelId);
49771         if(preservePanel){
49772             document.body.appendChild(panel.getEl().dom);
49773         }
49774         if(this.tabs){
49775             this.tabs.removeTab(panel.getEl().id);
49776         }else if (!preservePanel){
49777             this.bodyEl.dom.removeChild(panel.getEl().dom);
49778         }
49779         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49780             var p = this.panels.first();
49781             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49782             tempEl.appendChild(p.getEl().dom);
49783             this.bodyEl.update("");
49784             this.bodyEl.dom.appendChild(p.getEl().dom);
49785             tempEl = null;
49786             this.updateTitle(p.getTitle());
49787             this.tabs = null;
49788             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49789             this.setActivePanel(p);
49790         }
49791         panel.setRegion(null);
49792         if(this.activePanel == panel){
49793             this.activePanel = null;
49794         }
49795         if(this.config.autoDestroy !== false && preservePanel !== true){
49796             try{panel.destroy();}catch(e){}
49797         }
49798         this.fireEvent("panelremoved", this, panel);
49799         return panel;
49800     },
49801
49802     /**
49803      * Returns the TabPanel component used by this region
49804      * @return {Roo.TabPanel}
49805      */
49806     getTabs : function(){
49807         return this.tabs;
49808     },
49809
49810     createTool : function(parentEl, className){
49811         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49812             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49813         btn.addClassOnOver("x-layout-tools-button-over");
49814         return btn;
49815     }
49816 });/*
49817  * Based on:
49818  * Ext JS Library 1.1.1
49819  * Copyright(c) 2006-2007, Ext JS, LLC.
49820  *
49821  * Originally Released Under LGPL - original licence link has changed is not relivant.
49822  *
49823  * Fork - LGPL
49824  * <script type="text/javascript">
49825  */
49826  
49827
49828
49829 /**
49830  * @class Roo.SplitLayoutRegion
49831  * @extends Roo.LayoutRegion
49832  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49833  */
49834 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49835     this.cursor = cursor;
49836     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49837 };
49838
49839 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49840     splitTip : "Drag to resize.",
49841     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49842     useSplitTips : false,
49843
49844     applyConfig : function(config){
49845         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49846         if(config.split){
49847             if(!this.split){
49848                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49849                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49850                 /** The SplitBar for this region 
49851                 * @type Roo.SplitBar */
49852                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49853                 this.split.on("moved", this.onSplitMove, this);
49854                 this.split.useShim = config.useShim === true;
49855                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49856                 if(this.useSplitTips){
49857                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49858                 }
49859                 if(config.collapsible){
49860                     this.split.el.on("dblclick", this.collapse,  this);
49861                 }
49862             }
49863             if(typeof config.minSize != "undefined"){
49864                 this.split.minSize = config.minSize;
49865             }
49866             if(typeof config.maxSize != "undefined"){
49867                 this.split.maxSize = config.maxSize;
49868             }
49869             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49870                 this.hideSplitter();
49871             }
49872         }
49873     },
49874
49875     getHMaxSize : function(){
49876          var cmax = this.config.maxSize || 10000;
49877          var center = this.mgr.getRegion("center");
49878          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49879     },
49880
49881     getVMaxSize : function(){
49882          var cmax = this.config.maxSize || 10000;
49883          var center = this.mgr.getRegion("center");
49884          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49885     },
49886
49887     onSplitMove : function(split, newSize){
49888         this.fireEvent("resized", this, newSize);
49889     },
49890     
49891     /** 
49892      * Returns the {@link Roo.SplitBar} for this region.
49893      * @return {Roo.SplitBar}
49894      */
49895     getSplitBar : function(){
49896         return this.split;
49897     },
49898     
49899     hide : function(){
49900         this.hideSplitter();
49901         Roo.SplitLayoutRegion.superclass.hide.call(this);
49902     },
49903
49904     hideSplitter : function(){
49905         if(this.split){
49906             this.split.el.setLocation(-2000,-2000);
49907             this.split.el.hide();
49908         }
49909     },
49910
49911     show : function(){
49912         if(this.split){
49913             this.split.el.show();
49914         }
49915         Roo.SplitLayoutRegion.superclass.show.call(this);
49916     },
49917     
49918     beforeSlide: function(){
49919         if(Roo.isGecko){// firefox overflow auto bug workaround
49920             this.bodyEl.clip();
49921             if(this.tabs) this.tabs.bodyEl.clip();
49922             if(this.activePanel){
49923                 this.activePanel.getEl().clip();
49924                 
49925                 if(this.activePanel.beforeSlide){
49926                     this.activePanel.beforeSlide();
49927                 }
49928             }
49929         }
49930     },
49931     
49932     afterSlide : function(){
49933         if(Roo.isGecko){// firefox overflow auto bug workaround
49934             this.bodyEl.unclip();
49935             if(this.tabs) this.tabs.bodyEl.unclip();
49936             if(this.activePanel){
49937                 this.activePanel.getEl().unclip();
49938                 if(this.activePanel.afterSlide){
49939                     this.activePanel.afterSlide();
49940                 }
49941             }
49942         }
49943     },
49944
49945     initAutoHide : function(){
49946         if(this.autoHide !== false){
49947             if(!this.autoHideHd){
49948                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49949                 this.autoHideHd = {
49950                     "mouseout": function(e){
49951                         if(!e.within(this.el, true)){
49952                             st.delay(500);
49953                         }
49954                     },
49955                     "mouseover" : function(e){
49956                         st.cancel();
49957                     },
49958                     scope : this
49959                 };
49960             }
49961             this.el.on(this.autoHideHd);
49962         }
49963     },
49964
49965     clearAutoHide : function(){
49966         if(this.autoHide !== false){
49967             this.el.un("mouseout", this.autoHideHd.mouseout);
49968             this.el.un("mouseover", this.autoHideHd.mouseover);
49969         }
49970     },
49971
49972     clearMonitor : function(){
49973         Roo.get(document).un("click", this.slideInIf, this);
49974     },
49975
49976     // these names are backwards but not changed for compat
49977     slideOut : function(){
49978         if(this.isSlid || this.el.hasActiveFx()){
49979             return;
49980         }
49981         this.isSlid = true;
49982         if(this.collapseBtn){
49983             this.collapseBtn.hide();
49984         }
49985         this.closeBtnState = this.closeBtn.getStyle('display');
49986         this.closeBtn.hide();
49987         if(this.stickBtn){
49988             this.stickBtn.show();
49989         }
49990         this.el.show();
49991         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49992         this.beforeSlide();
49993         this.el.setStyle("z-index", 10001);
49994         this.el.slideIn(this.getSlideAnchor(), {
49995             callback: function(){
49996                 this.afterSlide();
49997                 this.initAutoHide();
49998                 Roo.get(document).on("click", this.slideInIf, this);
49999                 this.fireEvent("slideshow", this);
50000             },
50001             scope: this,
50002             block: true
50003         });
50004     },
50005
50006     afterSlideIn : function(){
50007         this.clearAutoHide();
50008         this.isSlid = false;
50009         this.clearMonitor();
50010         this.el.setStyle("z-index", "");
50011         if(this.collapseBtn){
50012             this.collapseBtn.show();
50013         }
50014         this.closeBtn.setStyle('display', this.closeBtnState);
50015         if(this.stickBtn){
50016             this.stickBtn.hide();
50017         }
50018         this.fireEvent("slidehide", this);
50019     },
50020
50021     slideIn : function(cb){
50022         if(!this.isSlid || this.el.hasActiveFx()){
50023             Roo.callback(cb);
50024             return;
50025         }
50026         this.isSlid = false;
50027         this.beforeSlide();
50028         this.el.slideOut(this.getSlideAnchor(), {
50029             callback: function(){
50030                 this.el.setLeftTop(-10000, -10000);
50031                 this.afterSlide();
50032                 this.afterSlideIn();
50033                 Roo.callback(cb);
50034             },
50035             scope: this,
50036             block: true
50037         });
50038     },
50039     
50040     slideInIf : function(e){
50041         if(!e.within(this.el)){
50042             this.slideIn();
50043         }
50044     },
50045
50046     animateCollapse : function(){
50047         this.beforeSlide();
50048         this.el.setStyle("z-index", 20000);
50049         var anchor = this.getSlideAnchor();
50050         this.el.slideOut(anchor, {
50051             callback : function(){
50052                 this.el.setStyle("z-index", "");
50053                 this.collapsedEl.slideIn(anchor, {duration:.3});
50054                 this.afterSlide();
50055                 this.el.setLocation(-10000,-10000);
50056                 this.el.hide();
50057                 this.fireEvent("collapsed", this);
50058             },
50059             scope: this,
50060             block: true
50061         });
50062     },
50063
50064     animateExpand : function(){
50065         this.beforeSlide();
50066         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50067         this.el.setStyle("z-index", 20000);
50068         this.collapsedEl.hide({
50069             duration:.1
50070         });
50071         this.el.slideIn(this.getSlideAnchor(), {
50072             callback : function(){
50073                 this.el.setStyle("z-index", "");
50074                 this.afterSlide();
50075                 if(this.split){
50076                     this.split.el.show();
50077                 }
50078                 this.fireEvent("invalidated", this);
50079                 this.fireEvent("expanded", this);
50080             },
50081             scope: this,
50082             block: true
50083         });
50084     },
50085
50086     anchors : {
50087         "west" : "left",
50088         "east" : "right",
50089         "north" : "top",
50090         "south" : "bottom"
50091     },
50092
50093     sanchors : {
50094         "west" : "l",
50095         "east" : "r",
50096         "north" : "t",
50097         "south" : "b"
50098     },
50099
50100     canchors : {
50101         "west" : "tl-tr",
50102         "east" : "tr-tl",
50103         "north" : "tl-bl",
50104         "south" : "bl-tl"
50105     },
50106
50107     getAnchor : function(){
50108         return this.anchors[this.position];
50109     },
50110
50111     getCollapseAnchor : function(){
50112         return this.canchors[this.position];
50113     },
50114
50115     getSlideAnchor : function(){
50116         return this.sanchors[this.position];
50117     },
50118
50119     getAlignAdj : function(){
50120         var cm = this.cmargins;
50121         switch(this.position){
50122             case "west":
50123                 return [0, 0];
50124             break;
50125             case "east":
50126                 return [0, 0];
50127             break;
50128             case "north":
50129                 return [0, 0];
50130             break;
50131             case "south":
50132                 return [0, 0];
50133             break;
50134         }
50135     },
50136
50137     getExpandAdj : function(){
50138         var c = this.collapsedEl, cm = this.cmargins;
50139         switch(this.position){
50140             case "west":
50141                 return [-(cm.right+c.getWidth()+cm.left), 0];
50142             break;
50143             case "east":
50144                 return [cm.right+c.getWidth()+cm.left, 0];
50145             break;
50146             case "north":
50147                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50148             break;
50149             case "south":
50150                 return [0, cm.top+cm.bottom+c.getHeight()];
50151             break;
50152         }
50153     }
50154 });/*
50155  * Based on:
50156  * Ext JS Library 1.1.1
50157  * Copyright(c) 2006-2007, Ext JS, LLC.
50158  *
50159  * Originally Released Under LGPL - original licence link has changed is not relivant.
50160  *
50161  * Fork - LGPL
50162  * <script type="text/javascript">
50163  */
50164 /*
50165  * These classes are private internal classes
50166  */
50167 Roo.CenterLayoutRegion = function(mgr, config){
50168     Roo.LayoutRegion.call(this, mgr, config, "center");
50169     this.visible = true;
50170     this.minWidth = config.minWidth || 20;
50171     this.minHeight = config.minHeight || 20;
50172 };
50173
50174 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50175     hide : function(){
50176         // center panel can't be hidden
50177     },
50178     
50179     show : function(){
50180         // center panel can't be hidden
50181     },
50182     
50183     getMinWidth: function(){
50184         return this.minWidth;
50185     },
50186     
50187     getMinHeight: function(){
50188         return this.minHeight;
50189     }
50190 });
50191
50192
50193 Roo.NorthLayoutRegion = function(mgr, config){
50194     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50195     if(this.split){
50196         this.split.placement = Roo.SplitBar.TOP;
50197         this.split.orientation = Roo.SplitBar.VERTICAL;
50198         this.split.el.addClass("x-layout-split-v");
50199     }
50200     var size = config.initialSize || config.height;
50201     if(typeof size != "undefined"){
50202         this.el.setHeight(size);
50203     }
50204 };
50205 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50206     orientation: Roo.SplitBar.VERTICAL,
50207     getBox : function(){
50208         if(this.collapsed){
50209             return this.collapsedEl.getBox();
50210         }
50211         var box = this.el.getBox();
50212         if(this.split){
50213             box.height += this.split.el.getHeight();
50214         }
50215         return box;
50216     },
50217     
50218     updateBox : function(box){
50219         if(this.split && !this.collapsed){
50220             box.height -= this.split.el.getHeight();
50221             this.split.el.setLeft(box.x);
50222             this.split.el.setTop(box.y+box.height);
50223             this.split.el.setWidth(box.width);
50224         }
50225         if(this.collapsed){
50226             this.updateBody(box.width, null);
50227         }
50228         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50229     }
50230 });
50231
50232 Roo.SouthLayoutRegion = function(mgr, config){
50233     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50234     if(this.split){
50235         this.split.placement = Roo.SplitBar.BOTTOM;
50236         this.split.orientation = Roo.SplitBar.VERTICAL;
50237         this.split.el.addClass("x-layout-split-v");
50238     }
50239     var size = config.initialSize || config.height;
50240     if(typeof size != "undefined"){
50241         this.el.setHeight(size);
50242     }
50243 };
50244 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50245     orientation: Roo.SplitBar.VERTICAL,
50246     getBox : function(){
50247         if(this.collapsed){
50248             return this.collapsedEl.getBox();
50249         }
50250         var box = this.el.getBox();
50251         if(this.split){
50252             var sh = this.split.el.getHeight();
50253             box.height += sh;
50254             box.y -= sh;
50255         }
50256         return box;
50257     },
50258     
50259     updateBox : function(box){
50260         if(this.split && !this.collapsed){
50261             var sh = this.split.el.getHeight();
50262             box.height -= sh;
50263             box.y += sh;
50264             this.split.el.setLeft(box.x);
50265             this.split.el.setTop(box.y-sh);
50266             this.split.el.setWidth(box.width);
50267         }
50268         if(this.collapsed){
50269             this.updateBody(box.width, null);
50270         }
50271         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50272     }
50273 });
50274
50275 Roo.EastLayoutRegion = function(mgr, config){
50276     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50277     if(this.split){
50278         this.split.placement = Roo.SplitBar.RIGHT;
50279         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50280         this.split.el.addClass("x-layout-split-h");
50281     }
50282     var size = config.initialSize || config.width;
50283     if(typeof size != "undefined"){
50284         this.el.setWidth(size);
50285     }
50286 };
50287 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50288     orientation: Roo.SplitBar.HORIZONTAL,
50289     getBox : function(){
50290         if(this.collapsed){
50291             return this.collapsedEl.getBox();
50292         }
50293         var box = this.el.getBox();
50294         if(this.split){
50295             var sw = this.split.el.getWidth();
50296             box.width += sw;
50297             box.x -= sw;
50298         }
50299         return box;
50300     },
50301
50302     updateBox : function(box){
50303         if(this.split && !this.collapsed){
50304             var sw = this.split.el.getWidth();
50305             box.width -= sw;
50306             this.split.el.setLeft(box.x);
50307             this.split.el.setTop(box.y);
50308             this.split.el.setHeight(box.height);
50309             box.x += sw;
50310         }
50311         if(this.collapsed){
50312             this.updateBody(null, box.height);
50313         }
50314         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50315     }
50316 });
50317
50318 Roo.WestLayoutRegion = function(mgr, config){
50319     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50320     if(this.split){
50321         this.split.placement = Roo.SplitBar.LEFT;
50322         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50323         this.split.el.addClass("x-layout-split-h");
50324     }
50325     var size = config.initialSize || config.width;
50326     if(typeof size != "undefined"){
50327         this.el.setWidth(size);
50328     }
50329 };
50330 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50331     orientation: Roo.SplitBar.HORIZONTAL,
50332     getBox : function(){
50333         if(this.collapsed){
50334             return this.collapsedEl.getBox();
50335         }
50336         var box = this.el.getBox();
50337         if(this.split){
50338             box.width += this.split.el.getWidth();
50339         }
50340         return box;
50341     },
50342     
50343     updateBox : function(box){
50344         if(this.split && !this.collapsed){
50345             var sw = this.split.el.getWidth();
50346             box.width -= sw;
50347             this.split.el.setLeft(box.x+box.width);
50348             this.split.el.setTop(box.y);
50349             this.split.el.setHeight(box.height);
50350         }
50351         if(this.collapsed){
50352             this.updateBody(null, box.height);
50353         }
50354         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50355     }
50356 });
50357 /*
50358  * Based on:
50359  * Ext JS Library 1.1.1
50360  * Copyright(c) 2006-2007, Ext JS, LLC.
50361  *
50362  * Originally Released Under LGPL - original licence link has changed is not relivant.
50363  *
50364  * Fork - LGPL
50365  * <script type="text/javascript">
50366  */
50367  
50368  
50369 /*
50370  * Private internal class for reading and applying state
50371  */
50372 Roo.LayoutStateManager = function(layout){
50373      // default empty state
50374      this.state = {
50375         north: {},
50376         south: {},
50377         east: {},
50378         west: {}       
50379     };
50380 };
50381
50382 Roo.LayoutStateManager.prototype = {
50383     init : function(layout, provider){
50384         this.provider = provider;
50385         var state = provider.get(layout.id+"-layout-state");
50386         if(state){
50387             var wasUpdating = layout.isUpdating();
50388             if(!wasUpdating){
50389                 layout.beginUpdate();
50390             }
50391             for(var key in state){
50392                 if(typeof state[key] != "function"){
50393                     var rstate = state[key];
50394                     var r = layout.getRegion(key);
50395                     if(r && rstate){
50396                         if(rstate.size){
50397                             r.resizeTo(rstate.size);
50398                         }
50399                         if(rstate.collapsed == true){
50400                             r.collapse(true);
50401                         }else{
50402                             r.expand(null, true);
50403                         }
50404                     }
50405                 }
50406             }
50407             if(!wasUpdating){
50408                 layout.endUpdate();
50409             }
50410             this.state = state; 
50411         }
50412         this.layout = layout;
50413         layout.on("regionresized", this.onRegionResized, this);
50414         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50415         layout.on("regionexpanded", this.onRegionExpanded, this);
50416     },
50417     
50418     storeState : function(){
50419         this.provider.set(this.layout.id+"-layout-state", this.state);
50420     },
50421     
50422     onRegionResized : function(region, newSize){
50423         this.state[region.getPosition()].size = newSize;
50424         this.storeState();
50425     },
50426     
50427     onRegionCollapsed : function(region){
50428         this.state[region.getPosition()].collapsed = true;
50429         this.storeState();
50430     },
50431     
50432     onRegionExpanded : function(region){
50433         this.state[region.getPosition()].collapsed = false;
50434         this.storeState();
50435     }
50436 };/*
50437  * Based on:
50438  * Ext JS Library 1.1.1
50439  * Copyright(c) 2006-2007, Ext JS, LLC.
50440  *
50441  * Originally Released Under LGPL - original licence link has changed is not relivant.
50442  *
50443  * Fork - LGPL
50444  * <script type="text/javascript">
50445  */
50446 /**
50447  * @class Roo.ContentPanel
50448  * @extends Roo.util.Observable
50449  * A basic ContentPanel element.
50450  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50451  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50452  * @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
50453  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50454  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50455  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50456  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50457  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50458  * @cfg {String} title          The title for this panel
50459  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50460  * @cfg {String} url            Calls {@link #setUrl} with this value
50461  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50462  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50463  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50464  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50465
50466  * @constructor
50467  * Create a new ContentPanel.
50468  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50469  * @param {String/Object} config A string to set only the title or a config object
50470  * @param {String} content (optional) Set the HTML content for this panel
50471  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50472  */
50473 Roo.ContentPanel = function(el, config, content){
50474     
50475      
50476     /*
50477     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50478         config = el;
50479         el = Roo.id();
50480     }
50481     if (config && config.parentLayout) { 
50482         el = config.parentLayout.el.createChild(); 
50483     }
50484     */
50485     if(el.autoCreate){ // xtype is available if this is called from factory
50486         config = el;
50487         el = Roo.id();
50488     }
50489     this.el = Roo.get(el);
50490     if(!this.el && config && config.autoCreate){
50491         if(typeof config.autoCreate == "object"){
50492             if(!config.autoCreate.id){
50493                 config.autoCreate.id = config.id||el;
50494             }
50495             this.el = Roo.DomHelper.append(document.body,
50496                         config.autoCreate, true);
50497         }else{
50498             this.el = Roo.DomHelper.append(document.body,
50499                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50500         }
50501     }
50502     this.closable = false;
50503     this.loaded = false;
50504     this.active = false;
50505     if(typeof config == "string"){
50506         this.title = config;
50507     }else{
50508         Roo.apply(this, config);
50509     }
50510     
50511     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50512         this.wrapEl = this.el.wrap();
50513         this.toolbar.container = this.el.insertSibling(false, 'before');
50514         this.toolbar = new Roo.Toolbar(this.toolbar);
50515     }
50516     
50517     // xtype created footer. - not sure if will work as we normally have to render first..
50518     if (this.footer && !this.footer.el && this.footer.xtype) {
50519         if (!this.wrapEl) {
50520             this.wrapEl = this.el.wrap();
50521         }
50522     
50523         this.footer.container = this.wrapEl.createChild();
50524          
50525         this.footer = Roo.factory(this.footer, Roo);
50526         
50527     }
50528     
50529     if(this.resizeEl){
50530         this.resizeEl = Roo.get(this.resizeEl, true);
50531     }else{
50532         this.resizeEl = this.el;
50533     }
50534     // handle view.xtype
50535     
50536  
50537     
50538     
50539     this.addEvents({
50540         /**
50541          * @event activate
50542          * Fires when this panel is activated. 
50543          * @param {Roo.ContentPanel} this
50544          */
50545         "activate" : true,
50546         /**
50547          * @event deactivate
50548          * Fires when this panel is activated. 
50549          * @param {Roo.ContentPanel} this
50550          */
50551         "deactivate" : true,
50552
50553         /**
50554          * @event resize
50555          * Fires when this panel is resized if fitToFrame is true.
50556          * @param {Roo.ContentPanel} this
50557          * @param {Number} width The width after any component adjustments
50558          * @param {Number} height The height after any component adjustments
50559          */
50560         "resize" : true,
50561         
50562          /**
50563          * @event render
50564          * Fires when this tab is created
50565          * @param {Roo.ContentPanel} this
50566          */
50567         "render" : true
50568         
50569         
50570         
50571     });
50572     
50573
50574     
50575     
50576     if(this.autoScroll){
50577         this.resizeEl.setStyle("overflow", "auto");
50578     } else {
50579         // fix randome scrolling
50580         this.el.on('scroll', function() {
50581             Roo.log('fix random scolling');
50582             this.scrollTo('top',0); 
50583         });
50584     }
50585     content = content || this.content;
50586     if(content){
50587         this.setContent(content);
50588     }
50589     if(config && config.url){
50590         this.setUrl(this.url, this.params, this.loadOnce);
50591     }
50592     
50593     
50594     
50595     Roo.ContentPanel.superclass.constructor.call(this);
50596     
50597     if (this.view && typeof(this.view.xtype) != 'undefined') {
50598         this.view.el = this.el.appendChild(document.createElement("div"));
50599         this.view = Roo.factory(this.view); 
50600         this.view.render  &&  this.view.render(false, '');  
50601     }
50602     
50603     
50604     this.fireEvent('render', this);
50605 };
50606
50607 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50608     tabTip:'',
50609     setRegion : function(region){
50610         this.region = region;
50611         if(region){
50612            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50613         }else{
50614            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50615         } 
50616     },
50617     
50618     /**
50619      * Returns the toolbar for this Panel if one was configured. 
50620      * @return {Roo.Toolbar} 
50621      */
50622     getToolbar : function(){
50623         return this.toolbar;
50624     },
50625     
50626     setActiveState : function(active){
50627         this.active = active;
50628         if(!active){
50629             this.fireEvent("deactivate", this);
50630         }else{
50631             this.fireEvent("activate", this);
50632         }
50633     },
50634     /**
50635      * Updates this panel's element
50636      * @param {String} content The new content
50637      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50638     */
50639     setContent : function(content, loadScripts){
50640         this.el.update(content, loadScripts);
50641     },
50642
50643     ignoreResize : function(w, h){
50644         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50645             return true;
50646         }else{
50647             this.lastSize = {width: w, height: h};
50648             return false;
50649         }
50650     },
50651     /**
50652      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50653      * @return {Roo.UpdateManager} The UpdateManager
50654      */
50655     getUpdateManager : function(){
50656         return this.el.getUpdateManager();
50657     },
50658      /**
50659      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50660      * @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:
50661 <pre><code>
50662 panel.load({
50663     url: "your-url.php",
50664     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50665     callback: yourFunction,
50666     scope: yourObject, //(optional scope)
50667     discardUrl: false,
50668     nocache: false,
50669     text: "Loading...",
50670     timeout: 30,
50671     scripts: false
50672 });
50673 </code></pre>
50674      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50675      * 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.
50676      * @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}
50677      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50678      * @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.
50679      * @return {Roo.ContentPanel} this
50680      */
50681     load : function(){
50682         var um = this.el.getUpdateManager();
50683         um.update.apply(um, arguments);
50684         return this;
50685     },
50686
50687
50688     /**
50689      * 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.
50690      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50691      * @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)
50692      * @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)
50693      * @return {Roo.UpdateManager} The UpdateManager
50694      */
50695     setUrl : function(url, params, loadOnce){
50696         if(this.refreshDelegate){
50697             this.removeListener("activate", this.refreshDelegate);
50698         }
50699         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50700         this.on("activate", this.refreshDelegate);
50701         return this.el.getUpdateManager();
50702     },
50703     
50704     _handleRefresh : function(url, params, loadOnce){
50705         if(!loadOnce || !this.loaded){
50706             var updater = this.el.getUpdateManager();
50707             updater.update(url, params, this._setLoaded.createDelegate(this));
50708         }
50709     },
50710     
50711     _setLoaded : function(){
50712         this.loaded = true;
50713     }, 
50714     
50715     /**
50716      * Returns this panel's id
50717      * @return {String} 
50718      */
50719     getId : function(){
50720         return this.el.id;
50721     },
50722     
50723     /** 
50724      * Returns this panel's element - used by regiosn to add.
50725      * @return {Roo.Element} 
50726      */
50727     getEl : function(){
50728         return this.wrapEl || this.el;
50729     },
50730     
50731     adjustForComponents : function(width, height)
50732     {
50733         //Roo.log('adjustForComponents ');
50734         if(this.resizeEl != this.el){
50735             width -= this.el.getFrameWidth('lr');
50736             height -= this.el.getFrameWidth('tb');
50737         }
50738         if(this.toolbar){
50739             var te = this.toolbar.getEl();
50740             height -= te.getHeight();
50741             te.setWidth(width);
50742         }
50743         if(this.footer){
50744             var te = this.footer.getEl();
50745             Roo.log("footer:" + te.getHeight());
50746             
50747             height -= te.getHeight();
50748             te.setWidth(width);
50749         }
50750         
50751         
50752         if(this.adjustments){
50753             width += this.adjustments[0];
50754             height += this.adjustments[1];
50755         }
50756         return {"width": width, "height": height};
50757     },
50758     
50759     setSize : function(width, height){
50760         if(this.fitToFrame && !this.ignoreResize(width, height)){
50761             if(this.fitContainer && this.resizeEl != this.el){
50762                 this.el.setSize(width, height);
50763             }
50764             var size = this.adjustForComponents(width, height);
50765             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50766             this.fireEvent('resize', this, size.width, size.height);
50767         }
50768     },
50769     
50770     /**
50771      * Returns this panel's title
50772      * @return {String} 
50773      */
50774     getTitle : function(){
50775         return this.title;
50776     },
50777     
50778     /**
50779      * Set this panel's title
50780      * @param {String} title
50781      */
50782     setTitle : function(title){
50783         this.title = title;
50784         if(this.region){
50785             this.region.updatePanelTitle(this, title);
50786         }
50787     },
50788     
50789     /**
50790      * Returns true is this panel was configured to be closable
50791      * @return {Boolean} 
50792      */
50793     isClosable : function(){
50794         return this.closable;
50795     },
50796     
50797     beforeSlide : function(){
50798         this.el.clip();
50799         this.resizeEl.clip();
50800     },
50801     
50802     afterSlide : function(){
50803         this.el.unclip();
50804         this.resizeEl.unclip();
50805     },
50806     
50807     /**
50808      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50809      *   Will fail silently if the {@link #setUrl} method has not been called.
50810      *   This does not activate the panel, just updates its content.
50811      */
50812     refresh : function(){
50813         if(this.refreshDelegate){
50814            this.loaded = false;
50815            this.refreshDelegate();
50816         }
50817     },
50818     
50819     /**
50820      * Destroys this panel
50821      */
50822     destroy : function(){
50823         this.el.removeAllListeners();
50824         var tempEl = document.createElement("span");
50825         tempEl.appendChild(this.el.dom);
50826         tempEl.innerHTML = "";
50827         this.el.remove();
50828         this.el = null;
50829     },
50830     
50831     /**
50832      * form - if the content panel contains a form - this is a reference to it.
50833      * @type {Roo.form.Form}
50834      */
50835     form : false,
50836     /**
50837      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50838      *    This contains a reference to it.
50839      * @type {Roo.View}
50840      */
50841     view : false,
50842     
50843       /**
50844      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50845      * <pre><code>
50846
50847 layout.addxtype({
50848        xtype : 'Form',
50849        items: [ .... ]
50850    }
50851 );
50852
50853 </code></pre>
50854      * @param {Object} cfg Xtype definition of item to add.
50855      */
50856     
50857     addxtype : function(cfg) {
50858         // add form..
50859         if (cfg.xtype.match(/^Form$/)) {
50860             
50861             var el;
50862             //if (this.footer) {
50863             //    el = this.footer.container.insertSibling(false, 'before');
50864             //} else {
50865                 el = this.el.createChild();
50866             //}
50867
50868             this.form = new  Roo.form.Form(cfg);
50869             
50870             
50871             if ( this.form.allItems.length) this.form.render(el.dom);
50872             return this.form;
50873         }
50874         // should only have one of theses..
50875         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50876             // views.. should not be just added - used named prop 'view''
50877             
50878             cfg.el = this.el.appendChild(document.createElement("div"));
50879             // factory?
50880             
50881             var ret = new Roo.factory(cfg);
50882              
50883              ret.render && ret.render(false, ''); // render blank..
50884             this.view = ret;
50885             return ret;
50886         }
50887         return false;
50888     }
50889 });
50890
50891 /**
50892  * @class Roo.GridPanel
50893  * @extends Roo.ContentPanel
50894  * @constructor
50895  * Create a new GridPanel.
50896  * @param {Roo.grid.Grid} grid The grid for this panel
50897  * @param {String/Object} config A string to set only the panel's title, or a config object
50898  */
50899 Roo.GridPanel = function(grid, config){
50900     
50901   
50902     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50903         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50904         
50905     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50906     
50907     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50908     
50909     if(this.toolbar){
50910         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50911     }
50912     // xtype created footer. - not sure if will work as we normally have to render first..
50913     if (this.footer && !this.footer.el && this.footer.xtype) {
50914         
50915         this.footer.container = this.grid.getView().getFooterPanel(true);
50916         this.footer.dataSource = this.grid.dataSource;
50917         this.footer = Roo.factory(this.footer, Roo);
50918         
50919     }
50920     
50921     grid.monitorWindowResize = false; // turn off autosizing
50922     grid.autoHeight = false;
50923     grid.autoWidth = false;
50924     this.grid = grid;
50925     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50926 };
50927
50928 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50929     getId : function(){
50930         return this.grid.id;
50931     },
50932     
50933     /**
50934      * Returns the grid for this panel
50935      * @return {Roo.grid.Grid} 
50936      */
50937     getGrid : function(){
50938         return this.grid;    
50939     },
50940     
50941     setSize : function(width, height){
50942         if(!this.ignoreResize(width, height)){
50943             var grid = this.grid;
50944             var size = this.adjustForComponents(width, height);
50945             grid.getGridEl().setSize(size.width, size.height);
50946             grid.autoSize();
50947         }
50948     },
50949     
50950     beforeSlide : function(){
50951         this.grid.getView().scroller.clip();
50952     },
50953     
50954     afterSlide : function(){
50955         this.grid.getView().scroller.unclip();
50956     },
50957     
50958     destroy : function(){
50959         this.grid.destroy();
50960         delete this.grid;
50961         Roo.GridPanel.superclass.destroy.call(this); 
50962     }
50963 });
50964
50965
50966 /**
50967  * @class Roo.NestedLayoutPanel
50968  * @extends Roo.ContentPanel
50969  * @constructor
50970  * Create a new NestedLayoutPanel.
50971  * 
50972  * 
50973  * @param {Roo.BorderLayout} layout The layout for this panel
50974  * @param {String/Object} config A string to set only the title or a config object
50975  */
50976 Roo.NestedLayoutPanel = function(layout, config)
50977 {
50978     // construct with only one argument..
50979     /* FIXME - implement nicer consturctors
50980     if (layout.layout) {
50981         config = layout;
50982         layout = config.layout;
50983         delete config.layout;
50984     }
50985     if (layout.xtype && !layout.getEl) {
50986         // then layout needs constructing..
50987         layout = Roo.factory(layout, Roo);
50988     }
50989     */
50990     
50991     
50992     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50993     
50994     layout.monitorWindowResize = false; // turn off autosizing
50995     this.layout = layout;
50996     this.layout.getEl().addClass("x-layout-nested-layout");
50997     
50998     
50999     
51000     
51001 };
51002
51003 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51004
51005     setSize : function(width, height){
51006         if(!this.ignoreResize(width, height)){
51007             var size = this.adjustForComponents(width, height);
51008             var el = this.layout.getEl();
51009             el.setSize(size.width, size.height);
51010             var touch = el.dom.offsetWidth;
51011             this.layout.layout();
51012             // ie requires a double layout on the first pass
51013             if(Roo.isIE && !this.initialized){
51014                 this.initialized = true;
51015                 this.layout.layout();
51016             }
51017         }
51018     },
51019     
51020     // activate all subpanels if not currently active..
51021     
51022     setActiveState : function(active){
51023         this.active = active;
51024         if(!active){
51025             this.fireEvent("deactivate", this);
51026             return;
51027         }
51028         
51029         this.fireEvent("activate", this);
51030         // not sure if this should happen before or after..
51031         if (!this.layout) {
51032             return; // should not happen..
51033         }
51034         var reg = false;
51035         for (var r in this.layout.regions) {
51036             reg = this.layout.getRegion(r);
51037             if (reg.getActivePanel()) {
51038                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51039                 reg.setActivePanel(reg.getActivePanel());
51040                 continue;
51041             }
51042             if (!reg.panels.length) {
51043                 continue;
51044             }
51045             reg.showPanel(reg.getPanel(0));
51046         }
51047         
51048         
51049         
51050         
51051     },
51052     
51053     /**
51054      * Returns the nested BorderLayout for this panel
51055      * @return {Roo.BorderLayout} 
51056      */
51057     getLayout : function(){
51058         return this.layout;
51059     },
51060     
51061      /**
51062      * Adds a xtype elements to the layout of the nested panel
51063      * <pre><code>
51064
51065 panel.addxtype({
51066        xtype : 'ContentPanel',
51067        region: 'west',
51068        items: [ .... ]
51069    }
51070 );
51071
51072 panel.addxtype({
51073         xtype : 'NestedLayoutPanel',
51074         region: 'west',
51075         layout: {
51076            center: { },
51077            west: { }   
51078         },
51079         items : [ ... list of content panels or nested layout panels.. ]
51080    }
51081 );
51082 </code></pre>
51083      * @param {Object} cfg Xtype definition of item to add.
51084      */
51085     addxtype : function(cfg) {
51086         return this.layout.addxtype(cfg);
51087     
51088     }
51089 });
51090
51091 Roo.ScrollPanel = function(el, config, content){
51092     config = config || {};
51093     config.fitToFrame = true;
51094     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51095     
51096     this.el.dom.style.overflow = "hidden";
51097     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51098     this.el.removeClass("x-layout-inactive-content");
51099     this.el.on("mousewheel", this.onWheel, this);
51100
51101     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51102     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51103     up.unselectable(); down.unselectable();
51104     up.on("click", this.scrollUp, this);
51105     down.on("click", this.scrollDown, this);
51106     up.addClassOnOver("x-scroller-btn-over");
51107     down.addClassOnOver("x-scroller-btn-over");
51108     up.addClassOnClick("x-scroller-btn-click");
51109     down.addClassOnClick("x-scroller-btn-click");
51110     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51111
51112     this.resizeEl = this.el;
51113     this.el = wrap; this.up = up; this.down = down;
51114 };
51115
51116 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51117     increment : 100,
51118     wheelIncrement : 5,
51119     scrollUp : function(){
51120         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51121     },
51122
51123     scrollDown : function(){
51124         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51125     },
51126
51127     afterScroll : function(){
51128         var el = this.resizeEl;
51129         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51130         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51131         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51132     },
51133
51134     setSize : function(){
51135         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51136         this.afterScroll();
51137     },
51138
51139     onWheel : function(e){
51140         var d = e.getWheelDelta();
51141         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51142         this.afterScroll();
51143         e.stopEvent();
51144     },
51145
51146     setContent : function(content, loadScripts){
51147         this.resizeEl.update(content, loadScripts);
51148     }
51149
51150 });
51151
51152
51153
51154
51155
51156
51157
51158
51159
51160 /**
51161  * @class Roo.TreePanel
51162  * @extends Roo.ContentPanel
51163  * @constructor
51164  * Create a new TreePanel. - defaults to fit/scoll contents.
51165  * @param {String/Object} config A string to set only the panel's title, or a config object
51166  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51167  */
51168 Roo.TreePanel = function(config){
51169     var el = config.el;
51170     var tree = config.tree;
51171     delete config.tree; 
51172     delete config.el; // hopefull!
51173     
51174     // wrapper for IE7 strict & safari scroll issue
51175     
51176     var treeEl = el.createChild();
51177     config.resizeEl = treeEl;
51178     
51179     
51180     
51181     Roo.TreePanel.superclass.constructor.call(this, el, config);
51182  
51183  
51184     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51185     //console.log(tree);
51186     this.on('activate', function()
51187     {
51188         if (this.tree.rendered) {
51189             return;
51190         }
51191         //console.log('render tree');
51192         this.tree.render();
51193     });
51194     // this should not be needed.. - it's actually the 'el' that resizes?
51195     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51196     
51197     //this.on('resize',  function (cp, w, h) {
51198     //        this.tree.innerCt.setWidth(w);
51199     //        this.tree.innerCt.setHeight(h);
51200     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51201     //});
51202
51203         
51204     
51205 };
51206
51207 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51208     fitToFrame : true,
51209     autoScroll : true
51210 });
51211
51212
51213
51214
51215
51216
51217
51218
51219
51220
51221
51222 /*
51223  * Based on:
51224  * Ext JS Library 1.1.1
51225  * Copyright(c) 2006-2007, Ext JS, LLC.
51226  *
51227  * Originally Released Under LGPL - original licence link has changed is not relivant.
51228  *
51229  * Fork - LGPL
51230  * <script type="text/javascript">
51231  */
51232  
51233
51234 /**
51235  * @class Roo.ReaderLayout
51236  * @extends Roo.BorderLayout
51237  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51238  * center region containing two nested regions (a top one for a list view and one for item preview below),
51239  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51240  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51241  * expedites the setup of the overall layout and regions for this common application style.
51242  * Example:
51243  <pre><code>
51244 var reader = new Roo.ReaderLayout();
51245 var CP = Roo.ContentPanel;  // shortcut for adding
51246
51247 reader.beginUpdate();
51248 reader.add("north", new CP("north", "North"));
51249 reader.add("west", new CP("west", {title: "West"}));
51250 reader.add("east", new CP("east", {title: "East"}));
51251
51252 reader.regions.listView.add(new CP("listView", "List"));
51253 reader.regions.preview.add(new CP("preview", "Preview"));
51254 reader.endUpdate();
51255 </code></pre>
51256 * @constructor
51257 * Create a new ReaderLayout
51258 * @param {Object} config Configuration options
51259 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51260 * document.body if omitted)
51261 */
51262 Roo.ReaderLayout = function(config, renderTo){
51263     var c = config || {size:{}};
51264     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51265         north: c.north !== false ? Roo.apply({
51266             split:false,
51267             initialSize: 32,
51268             titlebar: false
51269         }, c.north) : false,
51270         west: c.west !== false ? Roo.apply({
51271             split:true,
51272             initialSize: 200,
51273             minSize: 175,
51274             maxSize: 400,
51275             titlebar: true,
51276             collapsible: true,
51277             animate: true,
51278             margins:{left:5,right:0,bottom:5,top:5},
51279             cmargins:{left:5,right:5,bottom:5,top:5}
51280         }, c.west) : false,
51281         east: c.east !== false ? Roo.apply({
51282             split:true,
51283             initialSize: 200,
51284             minSize: 175,
51285             maxSize: 400,
51286             titlebar: true,
51287             collapsible: true,
51288             animate: true,
51289             margins:{left:0,right:5,bottom:5,top:5},
51290             cmargins:{left:5,right:5,bottom:5,top:5}
51291         }, c.east) : false,
51292         center: Roo.apply({
51293             tabPosition: 'top',
51294             autoScroll:false,
51295             closeOnTab: true,
51296             titlebar:false,
51297             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51298         }, c.center)
51299     });
51300
51301     this.el.addClass('x-reader');
51302
51303     this.beginUpdate();
51304
51305     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51306         south: c.preview !== false ? Roo.apply({
51307             split:true,
51308             initialSize: 200,
51309             minSize: 100,
51310             autoScroll:true,
51311             collapsible:true,
51312             titlebar: true,
51313             cmargins:{top:5,left:0, right:0, bottom:0}
51314         }, c.preview) : false,
51315         center: Roo.apply({
51316             autoScroll:false,
51317             titlebar:false,
51318             minHeight:200
51319         }, c.listView)
51320     });
51321     this.add('center', new Roo.NestedLayoutPanel(inner,
51322             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51323
51324     this.endUpdate();
51325
51326     this.regions.preview = inner.getRegion('south');
51327     this.regions.listView = inner.getRegion('center');
51328 };
51329
51330 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51331  * Based on:
51332  * Ext JS Library 1.1.1
51333  * Copyright(c) 2006-2007, Ext JS, LLC.
51334  *
51335  * Originally Released Under LGPL - original licence link has changed is not relivant.
51336  *
51337  * Fork - LGPL
51338  * <script type="text/javascript">
51339  */
51340  
51341 /**
51342  * @class Roo.grid.Grid
51343  * @extends Roo.util.Observable
51344  * This class represents the primary interface of a component based grid control.
51345  * <br><br>Usage:<pre><code>
51346  var grid = new Roo.grid.Grid("my-container-id", {
51347      ds: myDataStore,
51348      cm: myColModel,
51349      selModel: mySelectionModel,
51350      autoSizeColumns: true,
51351      monitorWindowResize: false,
51352      trackMouseOver: true
51353  });
51354  // set any options
51355  grid.render();
51356  * </code></pre>
51357  * <b>Common Problems:</b><br/>
51358  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51359  * element will correct this<br/>
51360  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51361  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51362  * are unpredictable.<br/>
51363  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51364  * grid to calculate dimensions/offsets.<br/>
51365   * @constructor
51366  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51367  * The container MUST have some type of size defined for the grid to fill. The container will be
51368  * automatically set to position relative if it isn't already.
51369  * @param {Object} config A config object that sets properties on this grid.
51370  */
51371 Roo.grid.Grid = function(container, config){
51372         // initialize the container
51373         this.container = Roo.get(container);
51374         this.container.update("");
51375         this.container.setStyle("overflow", "hidden");
51376     this.container.addClass('x-grid-container');
51377
51378     this.id = this.container.id;
51379
51380     Roo.apply(this, config);
51381     // check and correct shorthanded configs
51382     if(this.ds){
51383         this.dataSource = this.ds;
51384         delete this.ds;
51385     }
51386     if(this.cm){
51387         this.colModel = this.cm;
51388         delete this.cm;
51389     }
51390     if(this.sm){
51391         this.selModel = this.sm;
51392         delete this.sm;
51393     }
51394
51395     if (this.selModel) {
51396         this.selModel = Roo.factory(this.selModel, Roo.grid);
51397         this.sm = this.selModel;
51398         this.sm.xmodule = this.xmodule || false;
51399     }
51400     if (typeof(this.colModel.config) == 'undefined') {
51401         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51402         this.cm = this.colModel;
51403         this.cm.xmodule = this.xmodule || false;
51404     }
51405     if (this.dataSource) {
51406         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51407         this.ds = this.dataSource;
51408         this.ds.xmodule = this.xmodule || false;
51409          
51410     }
51411     
51412     
51413     
51414     if(this.width){
51415         this.container.setWidth(this.width);
51416     }
51417
51418     if(this.height){
51419         this.container.setHeight(this.height);
51420     }
51421     /** @private */
51422         this.addEvents({
51423         // raw events
51424         /**
51425          * @event click
51426          * The raw click event for the entire grid.
51427          * @param {Roo.EventObject} e
51428          */
51429         "click" : true,
51430         /**
51431          * @event dblclick
51432          * The raw dblclick event for the entire grid.
51433          * @param {Roo.EventObject} e
51434          */
51435         "dblclick" : true,
51436         /**
51437          * @event contextmenu
51438          * The raw contextmenu event for the entire grid.
51439          * @param {Roo.EventObject} e
51440          */
51441         "contextmenu" : true,
51442         /**
51443          * @event mousedown
51444          * The raw mousedown event for the entire grid.
51445          * @param {Roo.EventObject} e
51446          */
51447         "mousedown" : true,
51448         /**
51449          * @event mouseup
51450          * The raw mouseup event for the entire grid.
51451          * @param {Roo.EventObject} e
51452          */
51453         "mouseup" : true,
51454         /**
51455          * @event mouseover
51456          * The raw mouseover event for the entire grid.
51457          * @param {Roo.EventObject} e
51458          */
51459         "mouseover" : true,
51460         /**
51461          * @event mouseout
51462          * The raw mouseout event for the entire grid.
51463          * @param {Roo.EventObject} e
51464          */
51465         "mouseout" : true,
51466         /**
51467          * @event keypress
51468          * The raw keypress event for the entire grid.
51469          * @param {Roo.EventObject} e
51470          */
51471         "keypress" : true,
51472         /**
51473          * @event keydown
51474          * The raw keydown event for the entire grid.
51475          * @param {Roo.EventObject} e
51476          */
51477         "keydown" : true,
51478
51479         // custom events
51480
51481         /**
51482          * @event cellclick
51483          * Fires when a cell is clicked
51484          * @param {Grid} this
51485          * @param {Number} rowIndex
51486          * @param {Number} columnIndex
51487          * @param {Roo.EventObject} e
51488          */
51489         "cellclick" : true,
51490         /**
51491          * @event celldblclick
51492          * Fires when a cell is double clicked
51493          * @param {Grid} this
51494          * @param {Number} rowIndex
51495          * @param {Number} columnIndex
51496          * @param {Roo.EventObject} e
51497          */
51498         "celldblclick" : true,
51499         /**
51500          * @event rowclick
51501          * Fires when a row is clicked
51502          * @param {Grid} this
51503          * @param {Number} rowIndex
51504          * @param {Roo.EventObject} e
51505          */
51506         "rowclick" : true,
51507         /**
51508          * @event rowdblclick
51509          * Fires when a row is double clicked
51510          * @param {Grid} this
51511          * @param {Number} rowIndex
51512          * @param {Roo.EventObject} e
51513          */
51514         "rowdblclick" : true,
51515         /**
51516          * @event headerclick
51517          * Fires when a header is clicked
51518          * @param {Grid} this
51519          * @param {Number} columnIndex
51520          * @param {Roo.EventObject} e
51521          */
51522         "headerclick" : true,
51523         /**
51524          * @event headerdblclick
51525          * Fires when a header cell is double clicked
51526          * @param {Grid} this
51527          * @param {Number} columnIndex
51528          * @param {Roo.EventObject} e
51529          */
51530         "headerdblclick" : true,
51531         /**
51532          * @event rowcontextmenu
51533          * Fires when a row is right clicked
51534          * @param {Grid} this
51535          * @param {Number} rowIndex
51536          * @param {Roo.EventObject} e
51537          */
51538         "rowcontextmenu" : true,
51539         /**
51540          * @event cellcontextmenu
51541          * Fires when a cell is right clicked
51542          * @param {Grid} this
51543          * @param {Number} rowIndex
51544          * @param {Number} cellIndex
51545          * @param {Roo.EventObject} e
51546          */
51547          "cellcontextmenu" : true,
51548         /**
51549          * @event headercontextmenu
51550          * Fires when a header is right clicked
51551          * @param {Grid} this
51552          * @param {Number} columnIndex
51553          * @param {Roo.EventObject} e
51554          */
51555         "headercontextmenu" : true,
51556         /**
51557          * @event bodyscroll
51558          * Fires when the body element is scrolled
51559          * @param {Number} scrollLeft
51560          * @param {Number} scrollTop
51561          */
51562         "bodyscroll" : true,
51563         /**
51564          * @event columnresize
51565          * Fires when the user resizes a column
51566          * @param {Number} columnIndex
51567          * @param {Number} newSize
51568          */
51569         "columnresize" : true,
51570         /**
51571          * @event columnmove
51572          * Fires when the user moves a column
51573          * @param {Number} oldIndex
51574          * @param {Number} newIndex
51575          */
51576         "columnmove" : true,
51577         /**
51578          * @event startdrag
51579          * Fires when row(s) start being dragged
51580          * @param {Grid} this
51581          * @param {Roo.GridDD} dd The drag drop object
51582          * @param {event} e The raw browser event
51583          */
51584         "startdrag" : true,
51585         /**
51586          * @event enddrag
51587          * Fires when a drag operation is complete
51588          * @param {Grid} this
51589          * @param {Roo.GridDD} dd The drag drop object
51590          * @param {event} e The raw browser event
51591          */
51592         "enddrag" : true,
51593         /**
51594          * @event dragdrop
51595          * Fires when dragged row(s) are dropped on a valid DD target
51596          * @param {Grid} this
51597          * @param {Roo.GridDD} dd The drag drop object
51598          * @param {String} targetId The target drag drop object
51599          * @param {event} e The raw browser event
51600          */
51601         "dragdrop" : true,
51602         /**
51603          * @event dragover
51604          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51605          * @param {Grid} this
51606          * @param {Roo.GridDD} dd The drag drop object
51607          * @param {String} targetId The target drag drop object
51608          * @param {event} e The raw browser event
51609          */
51610         "dragover" : true,
51611         /**
51612          * @event dragenter
51613          *  Fires when the dragged row(s) first cross another DD target while being dragged
51614          * @param {Grid} this
51615          * @param {Roo.GridDD} dd The drag drop object
51616          * @param {String} targetId The target drag drop object
51617          * @param {event} e The raw browser event
51618          */
51619         "dragenter" : true,
51620         /**
51621          * @event dragout
51622          * Fires when the dragged row(s) leave another DD target while being dragged
51623          * @param {Grid} this
51624          * @param {Roo.GridDD} dd The drag drop object
51625          * @param {String} targetId The target drag drop object
51626          * @param {event} e The raw browser event
51627          */
51628         "dragout" : true,
51629         /**
51630          * @event rowclass
51631          * Fires when a row is rendered, so you can change add a style to it.
51632          * @param {GridView} gridview   The grid view
51633          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51634          */
51635         'rowclass' : true,
51636
51637         /**
51638          * @event render
51639          * Fires when the grid is rendered
51640          * @param {Grid} grid
51641          */
51642         'render' : true
51643     });
51644
51645     Roo.grid.Grid.superclass.constructor.call(this);
51646 };
51647 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51648     
51649     /**
51650      * @cfg {String} ddGroup - drag drop group.
51651      */
51652
51653     /**
51654      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51655      */
51656     minColumnWidth : 25,
51657
51658     /**
51659      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51660      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51661      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51662      */
51663     autoSizeColumns : false,
51664
51665     /**
51666      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51667      */
51668     autoSizeHeaders : true,
51669
51670     /**
51671      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51672      */
51673     monitorWindowResize : true,
51674
51675     /**
51676      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51677      * rows measured to get a columns size. Default is 0 (all rows).
51678      */
51679     maxRowsToMeasure : 0,
51680
51681     /**
51682      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51683      */
51684     trackMouseOver : true,
51685
51686     /**
51687     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51688     */
51689     
51690     /**
51691     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51692     */
51693     enableDragDrop : false,
51694     
51695     /**
51696     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51697     */
51698     enableColumnMove : true,
51699     
51700     /**
51701     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51702     */
51703     enableColumnHide : true,
51704     
51705     /**
51706     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51707     */
51708     enableRowHeightSync : false,
51709     
51710     /**
51711     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51712     */
51713     stripeRows : true,
51714     
51715     /**
51716     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51717     */
51718     autoHeight : false,
51719
51720     /**
51721      * @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.
51722      */
51723     autoExpandColumn : false,
51724
51725     /**
51726     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51727     * Default is 50.
51728     */
51729     autoExpandMin : 50,
51730
51731     /**
51732     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51733     */
51734     autoExpandMax : 1000,
51735
51736     /**
51737     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51738     */
51739     view : null,
51740
51741     /**
51742     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51743     */
51744     loadMask : false,
51745     /**
51746     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51747     */
51748     dropTarget: false,
51749     
51750    
51751     
51752     // private
51753     rendered : false,
51754
51755     /**
51756     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51757     * of a fixed width. Default is false.
51758     */
51759     /**
51760     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51761     */
51762     /**
51763      * Called once after all setup has been completed and the grid is ready to be rendered.
51764      * @return {Roo.grid.Grid} this
51765      */
51766     render : function()
51767     {
51768         var c = this.container;
51769         // try to detect autoHeight/width mode
51770         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51771             this.autoHeight = true;
51772         }
51773         var view = this.getView();
51774         view.init(this);
51775
51776         c.on("click", this.onClick, this);
51777         c.on("dblclick", this.onDblClick, this);
51778         c.on("contextmenu", this.onContextMenu, this);
51779         c.on("keydown", this.onKeyDown, this);
51780         if (Roo.isTouch) {
51781             c.on("touchstart", this.onTouchStart, this);
51782         }
51783
51784         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51785
51786         this.getSelectionModel().init(this);
51787
51788         view.render();
51789
51790         if(this.loadMask){
51791             this.loadMask = new Roo.LoadMask(this.container,
51792                     Roo.apply({store:this.dataSource}, this.loadMask));
51793         }
51794         
51795         
51796         if (this.toolbar && this.toolbar.xtype) {
51797             this.toolbar.container = this.getView().getHeaderPanel(true);
51798             this.toolbar = new Roo.Toolbar(this.toolbar);
51799         }
51800         if (this.footer && this.footer.xtype) {
51801             this.footer.dataSource = this.getDataSource();
51802             this.footer.container = this.getView().getFooterPanel(true);
51803             this.footer = Roo.factory(this.footer, Roo);
51804         }
51805         if (this.dropTarget && this.dropTarget.xtype) {
51806             delete this.dropTarget.xtype;
51807             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51808         }
51809         
51810         
51811         this.rendered = true;
51812         this.fireEvent('render', this);
51813         return this;
51814     },
51815
51816         /**
51817          * Reconfigures the grid to use a different Store and Column Model.
51818          * The View will be bound to the new objects and refreshed.
51819          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51820          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51821          */
51822     reconfigure : function(dataSource, colModel){
51823         if(this.loadMask){
51824             this.loadMask.destroy();
51825             this.loadMask = new Roo.LoadMask(this.container,
51826                     Roo.apply({store:dataSource}, this.loadMask));
51827         }
51828         this.view.bind(dataSource, colModel);
51829         this.dataSource = dataSource;
51830         this.colModel = colModel;
51831         this.view.refresh(true);
51832     },
51833
51834     // private
51835     onKeyDown : function(e){
51836         this.fireEvent("keydown", e);
51837     },
51838
51839     /**
51840      * Destroy this grid.
51841      * @param {Boolean} removeEl True to remove the element
51842      */
51843     destroy : function(removeEl, keepListeners){
51844         if(this.loadMask){
51845             this.loadMask.destroy();
51846         }
51847         var c = this.container;
51848         c.removeAllListeners();
51849         this.view.destroy();
51850         this.colModel.purgeListeners();
51851         if(!keepListeners){
51852             this.purgeListeners();
51853         }
51854         c.update("");
51855         if(removeEl === true){
51856             c.remove();
51857         }
51858     },
51859
51860     // private
51861     processEvent : function(name, e){
51862         // does this fire select???
51863         Roo.log('grid:processEvent '  + name);
51864         
51865         if (name != 'touchstart' ) {
51866             this.fireEvent(name, e);    
51867         }
51868         
51869         var t = e.getTarget();
51870         var v = this.view;
51871         var header = v.findHeaderIndex(t);
51872         if(header !== false){
51873             var ename = name == 'touchstart' ? 'click' : name;
51874              
51875             this.fireEvent("header" + ename, this, header, e);
51876         }else{
51877             var row = v.findRowIndex(t);
51878             var cell = v.findCellIndex(t);
51879             if (name == 'touchstart') {
51880                 // first touch is always a click.
51881                 // hopefull this happens after selection is updated.?
51882                 name = false;
51883                 
51884                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51885                     var cs = this.selModel.getSelectedCell();
51886                     if (row == cs[0] && cell == cs[1]){
51887                         name = 'dblclick';
51888                     }
51889                 }
51890                 if (typeof(this.selModel.getSelections) != 'undefined') {
51891                     var cs = this.selModel.getSelections();
51892                     var ds = this.dataSource;
51893                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51894                         name = 'dblclick';
51895                     }
51896                 }
51897                 if (!name) {
51898                     return;
51899                 }
51900             }
51901             
51902             
51903             if(row !== false){
51904                 this.fireEvent("row" + name, this, row, e);
51905                 if(cell !== false){
51906                     this.fireEvent("cell" + name, this, row, cell, e);
51907                 }
51908             }
51909         }
51910     },
51911
51912     // private
51913     onClick : function(e){
51914         this.processEvent("click", e);
51915     },
51916    // private
51917     onTouchStart : function(e){
51918         this.processEvent("touchstart", e);
51919     },
51920
51921     // private
51922     onContextMenu : function(e, t){
51923         this.processEvent("contextmenu", e);
51924     },
51925
51926     // private
51927     onDblClick : function(e){
51928         this.processEvent("dblclick", e);
51929     },
51930
51931     // private
51932     walkCells : function(row, col, step, fn, scope){
51933         var cm = this.colModel, clen = cm.getColumnCount();
51934         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51935         if(step < 0){
51936             if(col < 0){
51937                 row--;
51938                 first = false;
51939             }
51940             while(row >= 0){
51941                 if(!first){
51942                     col = clen-1;
51943                 }
51944                 first = false;
51945                 while(col >= 0){
51946                     if(fn.call(scope || this, row, col, cm) === true){
51947                         return [row, col];
51948                     }
51949                     col--;
51950                 }
51951                 row--;
51952             }
51953         } else {
51954             if(col >= clen){
51955                 row++;
51956                 first = false;
51957             }
51958             while(row < rlen){
51959                 if(!first){
51960                     col = 0;
51961                 }
51962                 first = false;
51963                 while(col < clen){
51964                     if(fn.call(scope || this, row, col, cm) === true){
51965                         return [row, col];
51966                     }
51967                     col++;
51968                 }
51969                 row++;
51970             }
51971         }
51972         return null;
51973     },
51974
51975     // private
51976     getSelections : function(){
51977         return this.selModel.getSelections();
51978     },
51979
51980     /**
51981      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
51982      * but if manual update is required this method will initiate it.
51983      */
51984     autoSize : function(){
51985         if(this.rendered){
51986             this.view.layout();
51987             if(this.view.adjustForScroll){
51988                 this.view.adjustForScroll();
51989             }
51990         }
51991     },
51992
51993     /**
51994      * Returns the grid's underlying element.
51995      * @return {Element} The element
51996      */
51997     getGridEl : function(){
51998         return this.container;
51999     },
52000
52001     // private for compatibility, overridden by editor grid
52002     stopEditing : function(){},
52003
52004     /**
52005      * Returns the grid's SelectionModel.
52006      * @return {SelectionModel}
52007      */
52008     getSelectionModel : function(){
52009         if(!this.selModel){
52010             this.selModel = new Roo.grid.RowSelectionModel();
52011         }
52012         return this.selModel;
52013     },
52014
52015     /**
52016      * Returns the grid's DataSource.
52017      * @return {DataSource}
52018      */
52019     getDataSource : function(){
52020         return this.dataSource;
52021     },
52022
52023     /**
52024      * Returns the grid's ColumnModel.
52025      * @return {ColumnModel}
52026      */
52027     getColumnModel : function(){
52028         return this.colModel;
52029     },
52030
52031     /**
52032      * Returns the grid's GridView object.
52033      * @return {GridView}
52034      */
52035     getView : function(){
52036         if(!this.view){
52037             this.view = new Roo.grid.GridView(this.viewConfig);
52038         }
52039         return this.view;
52040     },
52041     /**
52042      * Called to get grid's drag proxy text, by default returns this.ddText.
52043      * @return {String}
52044      */
52045     getDragDropText : function(){
52046         var count = this.selModel.getCount();
52047         return String.format(this.ddText, count, count == 1 ? '' : 's');
52048     }
52049 });
52050 /**
52051  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52052  * %0 is replaced with the number of selected rows.
52053  * @type String
52054  */
52055 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52056  * Based on:
52057  * Ext JS Library 1.1.1
52058  * Copyright(c) 2006-2007, Ext JS, LLC.
52059  *
52060  * Originally Released Under LGPL - original licence link has changed is not relivant.
52061  *
52062  * Fork - LGPL
52063  * <script type="text/javascript">
52064  */
52065  
52066 Roo.grid.AbstractGridView = function(){
52067         this.grid = null;
52068         
52069         this.events = {
52070             "beforerowremoved" : true,
52071             "beforerowsinserted" : true,
52072             "beforerefresh" : true,
52073             "rowremoved" : true,
52074             "rowsinserted" : true,
52075             "rowupdated" : true,
52076             "refresh" : true
52077         };
52078     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52079 };
52080
52081 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52082     rowClass : "x-grid-row",
52083     cellClass : "x-grid-cell",
52084     tdClass : "x-grid-td",
52085     hdClass : "x-grid-hd",
52086     splitClass : "x-grid-hd-split",
52087     
52088         init: function(grid){
52089         this.grid = grid;
52090                 var cid = this.grid.getGridEl().id;
52091         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52092         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52093         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52094         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52095         },
52096         
52097         getColumnRenderers : function(){
52098         var renderers = [];
52099         var cm = this.grid.colModel;
52100         var colCount = cm.getColumnCount();
52101         for(var i = 0; i < colCount; i++){
52102             renderers[i] = cm.getRenderer(i);
52103         }
52104         return renderers;
52105     },
52106     
52107     getColumnIds : function(){
52108         var ids = [];
52109         var cm = this.grid.colModel;
52110         var colCount = cm.getColumnCount();
52111         for(var i = 0; i < colCount; i++){
52112             ids[i] = cm.getColumnId(i);
52113         }
52114         return ids;
52115     },
52116     
52117     getDataIndexes : function(){
52118         if(!this.indexMap){
52119             this.indexMap = this.buildIndexMap();
52120         }
52121         return this.indexMap.colToData;
52122     },
52123     
52124     getColumnIndexByDataIndex : function(dataIndex){
52125         if(!this.indexMap){
52126             this.indexMap = this.buildIndexMap();
52127         }
52128         return this.indexMap.dataToCol[dataIndex];
52129     },
52130     
52131     /**
52132      * Set a css style for a column dynamically. 
52133      * @param {Number} colIndex The index of the column
52134      * @param {String} name The css property name
52135      * @param {String} value The css value
52136      */
52137     setCSSStyle : function(colIndex, name, value){
52138         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52139         Roo.util.CSS.updateRule(selector, name, value);
52140     },
52141     
52142     generateRules : function(cm){
52143         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52144         Roo.util.CSS.removeStyleSheet(rulesId);
52145         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52146             var cid = cm.getColumnId(i);
52147             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52148                          this.tdSelector, cid, " {\n}\n",
52149                          this.hdSelector, cid, " {\n}\n",
52150                          this.splitSelector, cid, " {\n}\n");
52151         }
52152         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52153     }
52154 });/*
52155  * Based on:
52156  * Ext JS Library 1.1.1
52157  * Copyright(c) 2006-2007, Ext JS, LLC.
52158  *
52159  * Originally Released Under LGPL - original licence link has changed is not relivant.
52160  *
52161  * Fork - LGPL
52162  * <script type="text/javascript">
52163  */
52164
52165 // private
52166 // This is a support class used internally by the Grid components
52167 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52168     this.grid = grid;
52169     this.view = grid.getView();
52170     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52171     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52172     if(hd2){
52173         this.setHandleElId(Roo.id(hd));
52174         this.setOuterHandleElId(Roo.id(hd2));
52175     }
52176     this.scroll = false;
52177 };
52178 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52179     maxDragWidth: 120,
52180     getDragData : function(e){
52181         var t = Roo.lib.Event.getTarget(e);
52182         var h = this.view.findHeaderCell(t);
52183         if(h){
52184             return {ddel: h.firstChild, header:h};
52185         }
52186         return false;
52187     },
52188
52189     onInitDrag : function(e){
52190         this.view.headersDisabled = true;
52191         var clone = this.dragData.ddel.cloneNode(true);
52192         clone.id = Roo.id();
52193         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52194         this.proxy.update(clone);
52195         return true;
52196     },
52197
52198     afterValidDrop : function(){
52199         var v = this.view;
52200         setTimeout(function(){
52201             v.headersDisabled = false;
52202         }, 50);
52203     },
52204
52205     afterInvalidDrop : function(){
52206         var v = this.view;
52207         setTimeout(function(){
52208             v.headersDisabled = false;
52209         }, 50);
52210     }
52211 });
52212 /*
52213  * Based on:
52214  * Ext JS Library 1.1.1
52215  * Copyright(c) 2006-2007, Ext JS, LLC.
52216  *
52217  * Originally Released Under LGPL - original licence link has changed is not relivant.
52218  *
52219  * Fork - LGPL
52220  * <script type="text/javascript">
52221  */
52222 // private
52223 // This is a support class used internally by the Grid components
52224 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52225     this.grid = grid;
52226     this.view = grid.getView();
52227     // split the proxies so they don't interfere with mouse events
52228     this.proxyTop = Roo.DomHelper.append(document.body, {
52229         cls:"col-move-top", html:"&#160;"
52230     }, true);
52231     this.proxyBottom = Roo.DomHelper.append(document.body, {
52232         cls:"col-move-bottom", html:"&#160;"
52233     }, true);
52234     this.proxyTop.hide = this.proxyBottom.hide = function(){
52235         this.setLeftTop(-100,-100);
52236         this.setStyle("visibility", "hidden");
52237     };
52238     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52239     // temporarily disabled
52240     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52241     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52242 };
52243 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52244     proxyOffsets : [-4, -9],
52245     fly: Roo.Element.fly,
52246
52247     getTargetFromEvent : function(e){
52248         var t = Roo.lib.Event.getTarget(e);
52249         var cindex = this.view.findCellIndex(t);
52250         if(cindex !== false){
52251             return this.view.getHeaderCell(cindex);
52252         }
52253         return null;
52254     },
52255
52256     nextVisible : function(h){
52257         var v = this.view, cm = this.grid.colModel;
52258         h = h.nextSibling;
52259         while(h){
52260             if(!cm.isHidden(v.getCellIndex(h))){
52261                 return h;
52262             }
52263             h = h.nextSibling;
52264         }
52265         return null;
52266     },
52267
52268     prevVisible : function(h){
52269         var v = this.view, cm = this.grid.colModel;
52270         h = h.prevSibling;
52271         while(h){
52272             if(!cm.isHidden(v.getCellIndex(h))){
52273                 return h;
52274             }
52275             h = h.prevSibling;
52276         }
52277         return null;
52278     },
52279
52280     positionIndicator : function(h, n, e){
52281         var x = Roo.lib.Event.getPageX(e);
52282         var r = Roo.lib.Dom.getRegion(n.firstChild);
52283         var px, pt, py = r.top + this.proxyOffsets[1];
52284         if((r.right - x) <= (r.right-r.left)/2){
52285             px = r.right+this.view.borderWidth;
52286             pt = "after";
52287         }else{
52288             px = r.left;
52289             pt = "before";
52290         }
52291         var oldIndex = this.view.getCellIndex(h);
52292         var newIndex = this.view.getCellIndex(n);
52293
52294         if(this.grid.colModel.isFixed(newIndex)){
52295             return false;
52296         }
52297
52298         var locked = this.grid.colModel.isLocked(newIndex);
52299
52300         if(pt == "after"){
52301             newIndex++;
52302         }
52303         if(oldIndex < newIndex){
52304             newIndex--;
52305         }
52306         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52307             return false;
52308         }
52309         px +=  this.proxyOffsets[0];
52310         this.proxyTop.setLeftTop(px, py);
52311         this.proxyTop.show();
52312         if(!this.bottomOffset){
52313             this.bottomOffset = this.view.mainHd.getHeight();
52314         }
52315         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52316         this.proxyBottom.show();
52317         return pt;
52318     },
52319
52320     onNodeEnter : function(n, dd, e, data){
52321         if(data.header != n){
52322             this.positionIndicator(data.header, n, e);
52323         }
52324     },
52325
52326     onNodeOver : function(n, dd, e, data){
52327         var result = false;
52328         if(data.header != n){
52329             result = this.positionIndicator(data.header, n, e);
52330         }
52331         if(!result){
52332             this.proxyTop.hide();
52333             this.proxyBottom.hide();
52334         }
52335         return result ? this.dropAllowed : this.dropNotAllowed;
52336     },
52337
52338     onNodeOut : function(n, dd, e, data){
52339         this.proxyTop.hide();
52340         this.proxyBottom.hide();
52341     },
52342
52343     onNodeDrop : function(n, dd, e, data){
52344         var h = data.header;
52345         if(h != n){
52346             var cm = this.grid.colModel;
52347             var x = Roo.lib.Event.getPageX(e);
52348             var r = Roo.lib.Dom.getRegion(n.firstChild);
52349             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52350             var oldIndex = this.view.getCellIndex(h);
52351             var newIndex = this.view.getCellIndex(n);
52352             var locked = cm.isLocked(newIndex);
52353             if(pt == "after"){
52354                 newIndex++;
52355             }
52356             if(oldIndex < newIndex){
52357                 newIndex--;
52358             }
52359             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52360                 return false;
52361             }
52362             cm.setLocked(oldIndex, locked, true);
52363             cm.moveColumn(oldIndex, newIndex);
52364             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52365             return true;
52366         }
52367         return false;
52368     }
52369 });
52370 /*
52371  * Based on:
52372  * Ext JS Library 1.1.1
52373  * Copyright(c) 2006-2007, Ext JS, LLC.
52374  *
52375  * Originally Released Under LGPL - original licence link has changed is not relivant.
52376  *
52377  * Fork - LGPL
52378  * <script type="text/javascript">
52379  */
52380   
52381 /**
52382  * @class Roo.grid.GridView
52383  * @extends Roo.util.Observable
52384  *
52385  * @constructor
52386  * @param {Object} config
52387  */
52388 Roo.grid.GridView = function(config){
52389     Roo.grid.GridView.superclass.constructor.call(this);
52390     this.el = null;
52391
52392     Roo.apply(this, config);
52393 };
52394
52395 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52396
52397     unselectable :  'unselectable="on"',
52398     unselectableCls :  'x-unselectable',
52399     
52400     
52401     rowClass : "x-grid-row",
52402
52403     cellClass : "x-grid-col",
52404
52405     tdClass : "x-grid-td",
52406
52407     hdClass : "x-grid-hd",
52408
52409     splitClass : "x-grid-split",
52410
52411     sortClasses : ["sort-asc", "sort-desc"],
52412
52413     enableMoveAnim : false,
52414
52415     hlColor: "C3DAF9",
52416
52417     dh : Roo.DomHelper,
52418
52419     fly : Roo.Element.fly,
52420
52421     css : Roo.util.CSS,
52422
52423     borderWidth: 1,
52424
52425     splitOffset: 3,
52426
52427     scrollIncrement : 22,
52428
52429     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52430
52431     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52432
52433     bind : function(ds, cm){
52434         if(this.ds){
52435             this.ds.un("load", this.onLoad, this);
52436             this.ds.un("datachanged", this.onDataChange, this);
52437             this.ds.un("add", this.onAdd, this);
52438             this.ds.un("remove", this.onRemove, this);
52439             this.ds.un("update", this.onUpdate, this);
52440             this.ds.un("clear", this.onClear, this);
52441         }
52442         if(ds){
52443             ds.on("load", this.onLoad, this);
52444             ds.on("datachanged", this.onDataChange, this);
52445             ds.on("add", this.onAdd, this);
52446             ds.on("remove", this.onRemove, this);
52447             ds.on("update", this.onUpdate, this);
52448             ds.on("clear", this.onClear, this);
52449         }
52450         this.ds = ds;
52451
52452         if(this.cm){
52453             this.cm.un("widthchange", this.onColWidthChange, this);
52454             this.cm.un("headerchange", this.onHeaderChange, this);
52455             this.cm.un("hiddenchange", this.onHiddenChange, this);
52456             this.cm.un("columnmoved", this.onColumnMove, this);
52457             this.cm.un("columnlockchange", this.onColumnLock, this);
52458         }
52459         if(cm){
52460             this.generateRules(cm);
52461             cm.on("widthchange", this.onColWidthChange, this);
52462             cm.on("headerchange", this.onHeaderChange, this);
52463             cm.on("hiddenchange", this.onHiddenChange, this);
52464             cm.on("columnmoved", this.onColumnMove, this);
52465             cm.on("columnlockchange", this.onColumnLock, this);
52466         }
52467         this.cm = cm;
52468     },
52469
52470     init: function(grid){
52471         Roo.grid.GridView.superclass.init.call(this, grid);
52472
52473         this.bind(grid.dataSource, grid.colModel);
52474
52475         grid.on("headerclick", this.handleHeaderClick, this);
52476
52477         if(grid.trackMouseOver){
52478             grid.on("mouseover", this.onRowOver, this);
52479             grid.on("mouseout", this.onRowOut, this);
52480         }
52481         grid.cancelTextSelection = function(){};
52482         this.gridId = grid.id;
52483
52484         var tpls = this.templates || {};
52485
52486         if(!tpls.master){
52487             tpls.master = new Roo.Template(
52488                '<div class="x-grid" hidefocus="true">',
52489                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52490                   '<div class="x-grid-topbar"></div>',
52491                   '<div class="x-grid-scroller"><div></div></div>',
52492                   '<div class="x-grid-locked">',
52493                       '<div class="x-grid-header">{lockedHeader}</div>',
52494                       '<div class="x-grid-body">{lockedBody}</div>',
52495                   "</div>",
52496                   '<div class="x-grid-viewport">',
52497                       '<div class="x-grid-header">{header}</div>',
52498                       '<div class="x-grid-body">{body}</div>',
52499                   "</div>",
52500                   '<div class="x-grid-bottombar"></div>',
52501                  
52502                   '<div class="x-grid-resize-proxy">&#160;</div>',
52503                "</div>"
52504             );
52505             tpls.master.disableformats = true;
52506         }
52507
52508         if(!tpls.header){
52509             tpls.header = new Roo.Template(
52510                '<table border="0" cellspacing="0" cellpadding="0">',
52511                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52512                "</table>{splits}"
52513             );
52514             tpls.header.disableformats = true;
52515         }
52516         tpls.header.compile();
52517
52518         if(!tpls.hcell){
52519             tpls.hcell = new Roo.Template(
52520                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52521                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52522                 "</div></td>"
52523              );
52524              tpls.hcell.disableFormats = true;
52525         }
52526         tpls.hcell.compile();
52527
52528         if(!tpls.hsplit){
52529             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52530                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52531             tpls.hsplit.disableFormats = true;
52532         }
52533         tpls.hsplit.compile();
52534
52535         if(!tpls.body){
52536             tpls.body = new Roo.Template(
52537                '<table border="0" cellspacing="0" cellpadding="0">',
52538                "<tbody>{rows}</tbody>",
52539                "</table>"
52540             );
52541             tpls.body.disableFormats = true;
52542         }
52543         tpls.body.compile();
52544
52545         if(!tpls.row){
52546             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52547             tpls.row.disableFormats = true;
52548         }
52549         tpls.row.compile();
52550
52551         if(!tpls.cell){
52552             tpls.cell = new Roo.Template(
52553                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52554                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52555                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52556                 "</td>"
52557             );
52558             tpls.cell.disableFormats = true;
52559         }
52560         tpls.cell.compile();
52561
52562         this.templates = tpls;
52563     },
52564
52565     // remap these for backwards compat
52566     onColWidthChange : function(){
52567         this.updateColumns.apply(this, arguments);
52568     },
52569     onHeaderChange : function(){
52570         this.updateHeaders.apply(this, arguments);
52571     }, 
52572     onHiddenChange : function(){
52573         this.handleHiddenChange.apply(this, arguments);
52574     },
52575     onColumnMove : function(){
52576         this.handleColumnMove.apply(this, arguments);
52577     },
52578     onColumnLock : function(){
52579         this.handleLockChange.apply(this, arguments);
52580     },
52581
52582     onDataChange : function(){
52583         this.refresh();
52584         this.updateHeaderSortState();
52585     },
52586
52587     onClear : function(){
52588         this.refresh();
52589     },
52590
52591     onUpdate : function(ds, record){
52592         this.refreshRow(record);
52593     },
52594
52595     refreshRow : function(record){
52596         var ds = this.ds, index;
52597         if(typeof record == 'number'){
52598             index = record;
52599             record = ds.getAt(index);
52600         }else{
52601             index = ds.indexOf(record);
52602         }
52603         this.insertRows(ds, index, index, true);
52604         this.onRemove(ds, record, index+1, true);
52605         this.syncRowHeights(index, index);
52606         this.layout();
52607         this.fireEvent("rowupdated", this, index, record);
52608     },
52609
52610     onAdd : function(ds, records, index){
52611         this.insertRows(ds, index, index + (records.length-1));
52612     },
52613
52614     onRemove : function(ds, record, index, isUpdate){
52615         if(isUpdate !== true){
52616             this.fireEvent("beforerowremoved", this, index, record);
52617         }
52618         var bt = this.getBodyTable(), lt = this.getLockedTable();
52619         if(bt.rows[index]){
52620             bt.firstChild.removeChild(bt.rows[index]);
52621         }
52622         if(lt.rows[index]){
52623             lt.firstChild.removeChild(lt.rows[index]);
52624         }
52625         if(isUpdate !== true){
52626             this.stripeRows(index);
52627             this.syncRowHeights(index, index);
52628             this.layout();
52629             this.fireEvent("rowremoved", this, index, record);
52630         }
52631     },
52632
52633     onLoad : function(){
52634         this.scrollToTop();
52635     },
52636
52637     /**
52638      * Scrolls the grid to the top
52639      */
52640     scrollToTop : function(){
52641         if(this.scroller){
52642             this.scroller.dom.scrollTop = 0;
52643             this.syncScroll();
52644         }
52645     },
52646
52647     /**
52648      * Gets a panel in the header of the grid that can be used for toolbars etc.
52649      * After modifying the contents of this panel a call to grid.autoSize() may be
52650      * required to register any changes in size.
52651      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52652      * @return Roo.Element
52653      */
52654     getHeaderPanel : function(doShow){
52655         if(doShow){
52656             this.headerPanel.show();
52657         }
52658         return this.headerPanel;
52659     },
52660
52661     /**
52662      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52663      * After modifying the contents of this panel a call to grid.autoSize() may be
52664      * required to register any changes in size.
52665      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52666      * @return Roo.Element
52667      */
52668     getFooterPanel : function(doShow){
52669         if(doShow){
52670             this.footerPanel.show();
52671         }
52672         return this.footerPanel;
52673     },
52674
52675     initElements : function(){
52676         var E = Roo.Element;
52677         var el = this.grid.getGridEl().dom.firstChild;
52678         var cs = el.childNodes;
52679
52680         this.el = new E(el);
52681         
52682          this.focusEl = new E(el.firstChild);
52683         this.focusEl.swallowEvent("click", true);
52684         
52685         this.headerPanel = new E(cs[1]);
52686         this.headerPanel.enableDisplayMode("block");
52687
52688         this.scroller = new E(cs[2]);
52689         this.scrollSizer = new E(this.scroller.dom.firstChild);
52690
52691         this.lockedWrap = new E(cs[3]);
52692         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52693         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52694
52695         this.mainWrap = new E(cs[4]);
52696         this.mainHd = new E(this.mainWrap.dom.firstChild);
52697         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52698
52699         this.footerPanel = new E(cs[5]);
52700         this.footerPanel.enableDisplayMode("block");
52701
52702         this.resizeProxy = new E(cs[6]);
52703
52704         this.headerSelector = String.format(
52705            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52706            this.lockedHd.id, this.mainHd.id
52707         );
52708
52709         this.splitterSelector = String.format(
52710            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52711            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52712         );
52713     },
52714     idToCssName : function(s)
52715     {
52716         return s.replace(/[^a-z0-9]+/ig, '-');
52717     },
52718
52719     getHeaderCell : function(index){
52720         return Roo.DomQuery.select(this.headerSelector)[index];
52721     },
52722
52723     getHeaderCellMeasure : function(index){
52724         return this.getHeaderCell(index).firstChild;
52725     },
52726
52727     getHeaderCellText : function(index){
52728         return this.getHeaderCell(index).firstChild.firstChild;
52729     },
52730
52731     getLockedTable : function(){
52732         return this.lockedBody.dom.firstChild;
52733     },
52734
52735     getBodyTable : function(){
52736         return this.mainBody.dom.firstChild;
52737     },
52738
52739     getLockedRow : function(index){
52740         return this.getLockedTable().rows[index];
52741     },
52742
52743     getRow : function(index){
52744         return this.getBodyTable().rows[index];
52745     },
52746
52747     getRowComposite : function(index){
52748         if(!this.rowEl){
52749             this.rowEl = new Roo.CompositeElementLite();
52750         }
52751         var els = [], lrow, mrow;
52752         if(lrow = this.getLockedRow(index)){
52753             els.push(lrow);
52754         }
52755         if(mrow = this.getRow(index)){
52756             els.push(mrow);
52757         }
52758         this.rowEl.elements = els;
52759         return this.rowEl;
52760     },
52761     /**
52762      * Gets the 'td' of the cell
52763      * 
52764      * @param {Integer} rowIndex row to select
52765      * @param {Integer} colIndex column to select
52766      * 
52767      * @return {Object} 
52768      */
52769     getCell : function(rowIndex, colIndex){
52770         var locked = this.cm.getLockedCount();
52771         var source;
52772         if(colIndex < locked){
52773             source = this.lockedBody.dom.firstChild;
52774         }else{
52775             source = this.mainBody.dom.firstChild;
52776             colIndex -= locked;
52777         }
52778         return source.rows[rowIndex].childNodes[colIndex];
52779     },
52780
52781     getCellText : function(rowIndex, colIndex){
52782         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52783     },
52784
52785     getCellBox : function(cell){
52786         var b = this.fly(cell).getBox();
52787         if(Roo.isOpera){ // opera fails to report the Y
52788             b.y = cell.offsetTop + this.mainBody.getY();
52789         }
52790         return b;
52791     },
52792
52793     getCellIndex : function(cell){
52794         var id = String(cell.className).match(this.cellRE);
52795         if(id){
52796             return parseInt(id[1], 10);
52797         }
52798         return 0;
52799     },
52800
52801     findHeaderIndex : function(n){
52802         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52803         return r ? this.getCellIndex(r) : false;
52804     },
52805
52806     findHeaderCell : function(n){
52807         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52808         return r ? r : false;
52809     },
52810
52811     findRowIndex : function(n){
52812         if(!n){
52813             return false;
52814         }
52815         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52816         return r ? r.rowIndex : false;
52817     },
52818
52819     findCellIndex : function(node){
52820         var stop = this.el.dom;
52821         while(node && node != stop){
52822             if(this.findRE.test(node.className)){
52823                 return this.getCellIndex(node);
52824             }
52825             node = node.parentNode;
52826         }
52827         return false;
52828     },
52829
52830     getColumnId : function(index){
52831         return this.cm.getColumnId(index);
52832     },
52833
52834     getSplitters : function()
52835     {
52836         if(this.splitterSelector){
52837            return Roo.DomQuery.select(this.splitterSelector);
52838         }else{
52839             return null;
52840       }
52841     },
52842
52843     getSplitter : function(index){
52844         return this.getSplitters()[index];
52845     },
52846
52847     onRowOver : function(e, t){
52848         var row;
52849         if((row = this.findRowIndex(t)) !== false){
52850             this.getRowComposite(row).addClass("x-grid-row-over");
52851         }
52852     },
52853
52854     onRowOut : function(e, t){
52855         var row;
52856         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52857             this.getRowComposite(row).removeClass("x-grid-row-over");
52858         }
52859     },
52860
52861     renderHeaders : function(){
52862         var cm = this.cm;
52863         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52864         var cb = [], lb = [], sb = [], lsb = [], p = {};
52865         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52866             p.cellId = "x-grid-hd-0-" + i;
52867             p.splitId = "x-grid-csplit-0-" + i;
52868             p.id = cm.getColumnId(i);
52869             p.title = cm.getColumnTooltip(i) || "";
52870             p.value = cm.getColumnHeader(i) || "";
52871             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52872             if(!cm.isLocked(i)){
52873                 cb[cb.length] = ct.apply(p);
52874                 sb[sb.length] = st.apply(p);
52875             }else{
52876                 lb[lb.length] = ct.apply(p);
52877                 lsb[lsb.length] = st.apply(p);
52878             }
52879         }
52880         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52881                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52882     },
52883
52884     updateHeaders : function(){
52885         var html = this.renderHeaders();
52886         this.lockedHd.update(html[0]);
52887         this.mainHd.update(html[1]);
52888     },
52889
52890     /**
52891      * Focuses the specified row.
52892      * @param {Number} row The row index
52893      */
52894     focusRow : function(row)
52895     {
52896         //Roo.log('GridView.focusRow');
52897         var x = this.scroller.dom.scrollLeft;
52898         this.focusCell(row, 0, false);
52899         this.scroller.dom.scrollLeft = x;
52900     },
52901
52902     /**
52903      * Focuses the specified cell.
52904      * @param {Number} row The row index
52905      * @param {Number} col The column index
52906      * @param {Boolean} hscroll false to disable horizontal scrolling
52907      */
52908     focusCell : function(row, col, hscroll)
52909     {
52910         //Roo.log('GridView.focusCell');
52911         var el = this.ensureVisible(row, col, hscroll);
52912         this.focusEl.alignTo(el, "tl-tl");
52913         if(Roo.isGecko){
52914             this.focusEl.focus();
52915         }else{
52916             this.focusEl.focus.defer(1, this.focusEl);
52917         }
52918     },
52919
52920     /**
52921      * Scrolls the specified cell into view
52922      * @param {Number} row The row index
52923      * @param {Number} col The column index
52924      * @param {Boolean} hscroll false to disable horizontal scrolling
52925      */
52926     ensureVisible : function(row, col, hscroll)
52927     {
52928         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52929         //return null; //disable for testing.
52930         if(typeof row != "number"){
52931             row = row.rowIndex;
52932         }
52933         if(row < 0 && row >= this.ds.getCount()){
52934             return  null;
52935         }
52936         col = (col !== undefined ? col : 0);
52937         var cm = this.grid.colModel;
52938         while(cm.isHidden(col)){
52939             col++;
52940         }
52941
52942         var el = this.getCell(row, col);
52943         if(!el){
52944             return null;
52945         }
52946         var c = this.scroller.dom;
52947
52948         var ctop = parseInt(el.offsetTop, 10);
52949         var cleft = parseInt(el.offsetLeft, 10);
52950         var cbot = ctop + el.offsetHeight;
52951         var cright = cleft + el.offsetWidth;
52952         
52953         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52954         var stop = parseInt(c.scrollTop, 10);
52955         var sleft = parseInt(c.scrollLeft, 10);
52956         var sbot = stop + ch;
52957         var sright = sleft + c.clientWidth;
52958         /*
52959         Roo.log('GridView.ensureVisible:' +
52960                 ' ctop:' + ctop +
52961                 ' c.clientHeight:' + c.clientHeight +
52962                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52963                 ' stop:' + stop +
52964                 ' cbot:' + cbot +
52965                 ' sbot:' + sbot +
52966                 ' ch:' + ch  
52967                 );
52968         */
52969         if(ctop < stop){
52970              c.scrollTop = ctop;
52971             //Roo.log("set scrolltop to ctop DISABLE?");
52972         }else if(cbot > sbot){
52973             //Roo.log("set scrolltop to cbot-ch");
52974             c.scrollTop = cbot-ch;
52975         }
52976         
52977         if(hscroll !== false){
52978             if(cleft < sleft){
52979                 c.scrollLeft = cleft;
52980             }else if(cright > sright){
52981                 c.scrollLeft = cright-c.clientWidth;
52982             }
52983         }
52984          
52985         return el;
52986     },
52987
52988     updateColumns : function(){
52989         this.grid.stopEditing();
52990         var cm = this.grid.colModel, colIds = this.getColumnIds();
52991         //var totalWidth = cm.getTotalWidth();
52992         var pos = 0;
52993         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52994             //if(cm.isHidden(i)) continue;
52995             var w = cm.getColumnWidth(i);
52996             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52997             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52998         }
52999         this.updateSplitters();
53000     },
53001
53002     generateRules : function(cm){
53003         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53004         Roo.util.CSS.removeStyleSheet(rulesId);
53005         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53006             var cid = cm.getColumnId(i);
53007             var align = '';
53008             if(cm.config[i].align){
53009                 align = 'text-align:'+cm.config[i].align+';';
53010             }
53011             var hidden = '';
53012             if(cm.isHidden(i)){
53013                 hidden = 'display:none;';
53014             }
53015             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53016             ruleBuf.push(
53017                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53018                     this.hdSelector, cid, " {\n", align, width, "}\n",
53019                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53020                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53021         }
53022         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53023     },
53024
53025     updateSplitters : function(){
53026         var cm = this.cm, s = this.getSplitters();
53027         if(s){ // splitters not created yet
53028             var pos = 0, locked = true;
53029             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53030                 if(cm.isHidden(i)) continue;
53031                 var w = cm.getColumnWidth(i); // make sure it's a number
53032                 if(!cm.isLocked(i) && locked){
53033                     pos = 0;
53034                     locked = false;
53035                 }
53036                 pos += w;
53037                 s[i].style.left = (pos-this.splitOffset) + "px";
53038             }
53039         }
53040     },
53041
53042     handleHiddenChange : function(colModel, colIndex, hidden){
53043         if(hidden){
53044             this.hideColumn(colIndex);
53045         }else{
53046             this.unhideColumn(colIndex);
53047         }
53048     },
53049
53050     hideColumn : function(colIndex){
53051         var cid = this.getColumnId(colIndex);
53052         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53053         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53054         if(Roo.isSafari){
53055             this.updateHeaders();
53056         }
53057         this.updateSplitters();
53058         this.layout();
53059     },
53060
53061     unhideColumn : function(colIndex){
53062         var cid = this.getColumnId(colIndex);
53063         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53064         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53065
53066         if(Roo.isSafari){
53067             this.updateHeaders();
53068         }
53069         this.updateSplitters();
53070         this.layout();
53071     },
53072
53073     insertRows : function(dm, firstRow, lastRow, isUpdate){
53074         if(firstRow == 0 && lastRow == dm.getCount()-1){
53075             this.refresh();
53076         }else{
53077             if(!isUpdate){
53078                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53079             }
53080             var s = this.getScrollState();
53081             var markup = this.renderRows(firstRow, lastRow);
53082             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53083             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53084             this.restoreScroll(s);
53085             if(!isUpdate){
53086                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53087                 this.syncRowHeights(firstRow, lastRow);
53088                 this.stripeRows(firstRow);
53089                 this.layout();
53090             }
53091         }
53092     },
53093
53094     bufferRows : function(markup, target, index){
53095         var before = null, trows = target.rows, tbody = target.tBodies[0];
53096         if(index < trows.length){
53097             before = trows[index];
53098         }
53099         var b = document.createElement("div");
53100         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53101         var rows = b.firstChild.rows;
53102         for(var i = 0, len = rows.length; i < len; i++){
53103             if(before){
53104                 tbody.insertBefore(rows[0], before);
53105             }else{
53106                 tbody.appendChild(rows[0]);
53107             }
53108         }
53109         b.innerHTML = "";
53110         b = null;
53111     },
53112
53113     deleteRows : function(dm, firstRow, lastRow){
53114         if(dm.getRowCount()<1){
53115             this.fireEvent("beforerefresh", this);
53116             this.mainBody.update("");
53117             this.lockedBody.update("");
53118             this.fireEvent("refresh", this);
53119         }else{
53120             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53121             var bt = this.getBodyTable();
53122             var tbody = bt.firstChild;
53123             var rows = bt.rows;
53124             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53125                 tbody.removeChild(rows[firstRow]);
53126             }
53127             this.stripeRows(firstRow);
53128             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53129         }
53130     },
53131
53132     updateRows : function(dataSource, firstRow, lastRow){
53133         var s = this.getScrollState();
53134         this.refresh();
53135         this.restoreScroll(s);
53136     },
53137
53138     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53139         if(!noRefresh){
53140            this.refresh();
53141         }
53142         this.updateHeaderSortState();
53143     },
53144
53145     getScrollState : function(){
53146         
53147         var sb = this.scroller.dom;
53148         return {left: sb.scrollLeft, top: sb.scrollTop};
53149     },
53150
53151     stripeRows : function(startRow){
53152         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53153             return;
53154         }
53155         startRow = startRow || 0;
53156         var rows = this.getBodyTable().rows;
53157         var lrows = this.getLockedTable().rows;
53158         var cls = ' x-grid-row-alt ';
53159         for(var i = startRow, len = rows.length; i < len; i++){
53160             var row = rows[i], lrow = lrows[i];
53161             var isAlt = ((i+1) % 2 == 0);
53162             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53163             if(isAlt == hasAlt){
53164                 continue;
53165             }
53166             if(isAlt){
53167                 row.className += " x-grid-row-alt";
53168             }else{
53169                 row.className = row.className.replace("x-grid-row-alt", "");
53170             }
53171             if(lrow){
53172                 lrow.className = row.className;
53173             }
53174         }
53175     },
53176
53177     restoreScroll : function(state){
53178         //Roo.log('GridView.restoreScroll');
53179         var sb = this.scroller.dom;
53180         sb.scrollLeft = state.left;
53181         sb.scrollTop = state.top;
53182         this.syncScroll();
53183     },
53184
53185     syncScroll : function(){
53186         //Roo.log('GridView.syncScroll');
53187         var sb = this.scroller.dom;
53188         var sh = this.mainHd.dom;
53189         var bs = this.mainBody.dom;
53190         var lv = this.lockedBody.dom;
53191         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53192         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53193     },
53194
53195     handleScroll : function(e){
53196         this.syncScroll();
53197         var sb = this.scroller.dom;
53198         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53199         e.stopEvent();
53200     },
53201
53202     handleWheel : function(e){
53203         var d = e.getWheelDelta();
53204         this.scroller.dom.scrollTop -= d*22;
53205         // set this here to prevent jumpy scrolling on large tables
53206         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53207         e.stopEvent();
53208     },
53209
53210     renderRows : function(startRow, endRow){
53211         // pull in all the crap needed to render rows
53212         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53213         var colCount = cm.getColumnCount();
53214
53215         if(ds.getCount() < 1){
53216             return ["", ""];
53217         }
53218
53219         // build a map for all the columns
53220         var cs = [];
53221         for(var i = 0; i < colCount; i++){
53222             var name = cm.getDataIndex(i);
53223             cs[i] = {
53224                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53225                 renderer : cm.getRenderer(i),
53226                 id : cm.getColumnId(i),
53227                 locked : cm.isLocked(i)
53228             };
53229         }
53230
53231         startRow = startRow || 0;
53232         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53233
53234         // records to render
53235         var rs = ds.getRange(startRow, endRow);
53236
53237         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53238     },
53239
53240     // As much as I hate to duplicate code, this was branched because FireFox really hates
53241     // [].join("") on strings. The performance difference was substantial enough to
53242     // branch this function
53243     doRender : Roo.isGecko ?
53244             function(cs, rs, ds, startRow, colCount, stripe){
53245                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53246                 // buffers
53247                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53248                 
53249                 var hasListener = this.grid.hasListener('rowclass');
53250                 var rowcfg = {};
53251                 for(var j = 0, len = rs.length; j < len; j++){
53252                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53253                     for(var i = 0; i < colCount; i++){
53254                         c = cs[i];
53255                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53256                         p.id = c.id;
53257                         p.css = p.attr = "";
53258                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53259                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53260                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53261                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53262                         }
53263                         var markup = ct.apply(p);
53264                         if(!c.locked){
53265                             cb+= markup;
53266                         }else{
53267                             lcb+= markup;
53268                         }
53269                     }
53270                     var alt = [];
53271                     if(stripe && ((rowIndex+1) % 2 == 0)){
53272                         alt.push("x-grid-row-alt")
53273                     }
53274                     if(r.dirty){
53275                         alt.push(  " x-grid-dirty-row");
53276                     }
53277                     rp.cells = lcb;
53278                     if(this.getRowClass){
53279                         alt.push(this.getRowClass(r, rowIndex));
53280                     }
53281                     if (hasListener) {
53282                         rowcfg = {
53283                              
53284                             record: r,
53285                             rowIndex : rowIndex,
53286                             rowClass : ''
53287                         }
53288                         this.grid.fireEvent('rowclass', this, rowcfg);
53289                         alt.push(rowcfg.rowClass);
53290                     }
53291                     rp.alt = alt.join(" ");
53292                     lbuf+= rt.apply(rp);
53293                     rp.cells = cb;
53294                     buf+=  rt.apply(rp);
53295                 }
53296                 return [lbuf, buf];
53297             } :
53298             function(cs, rs, ds, startRow, colCount, stripe){
53299                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53300                 // buffers
53301                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53302                 var hasListener = this.grid.hasListener('rowclass');
53303  
53304                 var rowcfg = {};
53305                 for(var j = 0, len = rs.length; j < len; j++){
53306                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53307                     for(var i = 0; i < colCount; i++){
53308                         c = cs[i];
53309                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53310                         p.id = c.id;
53311                         p.css = p.attr = "";
53312                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53313                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53314                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53315                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53316                         }
53317                         
53318                         var markup = ct.apply(p);
53319                         if(!c.locked){
53320                             cb[cb.length] = markup;
53321                         }else{
53322                             lcb[lcb.length] = markup;
53323                         }
53324                     }
53325                     var alt = [];
53326                     if(stripe && ((rowIndex+1) % 2 == 0)){
53327                         alt.push( "x-grid-row-alt");
53328                     }
53329                     if(r.dirty){
53330                         alt.push(" x-grid-dirty-row");
53331                     }
53332                     rp.cells = lcb;
53333                     if(this.getRowClass){
53334                         alt.push( this.getRowClass(r, rowIndex));
53335                     }
53336                     if (hasListener) {
53337                         rowcfg = {
53338                              
53339                             record: r,
53340                             rowIndex : rowIndex,
53341                             rowClass : ''
53342                         }
53343                         this.grid.fireEvent('rowclass', this, rowcfg);
53344                         alt.push(rowcfg.rowClass);
53345                     }
53346                     rp.alt = alt.join(" ");
53347                     rp.cells = lcb.join("");
53348                     lbuf[lbuf.length] = rt.apply(rp);
53349                     rp.cells = cb.join("");
53350                     buf[buf.length] =  rt.apply(rp);
53351                 }
53352                 return [lbuf.join(""), buf.join("")];
53353             },
53354
53355     renderBody : function(){
53356         var markup = this.renderRows();
53357         var bt = this.templates.body;
53358         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53359     },
53360
53361     /**
53362      * Refreshes the grid
53363      * @param {Boolean} headersToo
53364      */
53365     refresh : function(headersToo){
53366         this.fireEvent("beforerefresh", this);
53367         this.grid.stopEditing();
53368         var result = this.renderBody();
53369         this.lockedBody.update(result[0]);
53370         this.mainBody.update(result[1]);
53371         if(headersToo === true){
53372             this.updateHeaders();
53373             this.updateColumns();
53374             this.updateSplitters();
53375             this.updateHeaderSortState();
53376         }
53377         this.syncRowHeights();
53378         this.layout();
53379         this.fireEvent("refresh", this);
53380     },
53381
53382     handleColumnMove : function(cm, oldIndex, newIndex){
53383         this.indexMap = null;
53384         var s = this.getScrollState();
53385         this.refresh(true);
53386         this.restoreScroll(s);
53387         this.afterMove(newIndex);
53388     },
53389
53390     afterMove : function(colIndex){
53391         if(this.enableMoveAnim && Roo.enableFx){
53392             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53393         }
53394         // if multisort - fix sortOrder, and reload..
53395         if (this.grid.dataSource.multiSort) {
53396             // the we can call sort again..
53397             var dm = this.grid.dataSource;
53398             var cm = this.grid.colModel;
53399             var so = [];
53400             for(var i = 0; i < cm.config.length; i++ ) {
53401                 
53402                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53403                     continue; // dont' bother, it's not in sort list or being set.
53404                 }
53405                 
53406                 so.push(cm.config[i].dataIndex);
53407             };
53408             dm.sortOrder = so;
53409             dm.load(dm.lastOptions);
53410             
53411             
53412         }
53413         
53414     },
53415
53416     updateCell : function(dm, rowIndex, dataIndex){
53417         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53418         if(typeof colIndex == "undefined"){ // not present in grid
53419             return;
53420         }
53421         var cm = this.grid.colModel;
53422         var cell = this.getCell(rowIndex, colIndex);
53423         var cellText = this.getCellText(rowIndex, colIndex);
53424
53425         var p = {
53426             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53427             id : cm.getColumnId(colIndex),
53428             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53429         };
53430         var renderer = cm.getRenderer(colIndex);
53431         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53432         if(typeof val == "undefined" || val === "") val = "&#160;";
53433         cellText.innerHTML = val;
53434         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53435         this.syncRowHeights(rowIndex, rowIndex);
53436     },
53437
53438     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53439         var maxWidth = 0;
53440         if(this.grid.autoSizeHeaders){
53441             var h = this.getHeaderCellMeasure(colIndex);
53442             maxWidth = Math.max(maxWidth, h.scrollWidth);
53443         }
53444         var tb, index;
53445         if(this.cm.isLocked(colIndex)){
53446             tb = this.getLockedTable();
53447             index = colIndex;
53448         }else{
53449             tb = this.getBodyTable();
53450             index = colIndex - this.cm.getLockedCount();
53451         }
53452         if(tb && tb.rows){
53453             var rows = tb.rows;
53454             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53455             for(var i = 0; i < stopIndex; i++){
53456                 var cell = rows[i].childNodes[index].firstChild;
53457                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53458             }
53459         }
53460         return maxWidth + /*margin for error in IE*/ 5;
53461     },
53462     /**
53463      * Autofit a column to its content.
53464      * @param {Number} colIndex
53465      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53466      */
53467      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53468          if(this.cm.isHidden(colIndex)){
53469              return; // can't calc a hidden column
53470          }
53471         if(forceMinSize){
53472             var cid = this.cm.getColumnId(colIndex);
53473             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53474            if(this.grid.autoSizeHeaders){
53475                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53476            }
53477         }
53478         var newWidth = this.calcColumnWidth(colIndex);
53479         this.cm.setColumnWidth(colIndex,
53480             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53481         if(!suppressEvent){
53482             this.grid.fireEvent("columnresize", colIndex, newWidth);
53483         }
53484     },
53485
53486     /**
53487      * Autofits all columns to their content and then expands to fit any extra space in the grid
53488      */
53489      autoSizeColumns : function(){
53490         var cm = this.grid.colModel;
53491         var colCount = cm.getColumnCount();
53492         for(var i = 0; i < colCount; i++){
53493             this.autoSizeColumn(i, true, true);
53494         }
53495         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53496             this.fitColumns();
53497         }else{
53498             this.updateColumns();
53499             this.layout();
53500         }
53501     },
53502
53503     /**
53504      * Autofits all columns to the grid's width proportionate with their current size
53505      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53506      */
53507     fitColumns : function(reserveScrollSpace){
53508         var cm = this.grid.colModel;
53509         var colCount = cm.getColumnCount();
53510         var cols = [];
53511         var width = 0;
53512         var i, w;
53513         for (i = 0; i < colCount; i++){
53514             if(!cm.isHidden(i) && !cm.isFixed(i)){
53515                 w = cm.getColumnWidth(i);
53516                 cols.push(i);
53517                 cols.push(w);
53518                 width += w;
53519             }
53520         }
53521         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53522         if(reserveScrollSpace){
53523             avail -= 17;
53524         }
53525         var frac = (avail - cm.getTotalWidth())/width;
53526         while (cols.length){
53527             w = cols.pop();
53528             i = cols.pop();
53529             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53530         }
53531         this.updateColumns();
53532         this.layout();
53533     },
53534
53535     onRowSelect : function(rowIndex){
53536         var row = this.getRowComposite(rowIndex);
53537         row.addClass("x-grid-row-selected");
53538     },
53539
53540     onRowDeselect : function(rowIndex){
53541         var row = this.getRowComposite(rowIndex);
53542         row.removeClass("x-grid-row-selected");
53543     },
53544
53545     onCellSelect : function(row, col){
53546         var cell = this.getCell(row, col);
53547         if(cell){
53548             Roo.fly(cell).addClass("x-grid-cell-selected");
53549         }
53550     },
53551
53552     onCellDeselect : function(row, col){
53553         var cell = this.getCell(row, col);
53554         if(cell){
53555             Roo.fly(cell).removeClass("x-grid-cell-selected");
53556         }
53557     },
53558
53559     updateHeaderSortState : function(){
53560         
53561         // sort state can be single { field: xxx, direction : yyy}
53562         // or   { xxx=>ASC , yyy : DESC ..... }
53563         
53564         var mstate = {};
53565         if (!this.ds.multiSort) { 
53566             var state = this.ds.getSortState();
53567             if(!state){
53568                 return;
53569             }
53570             mstate[state.field] = state.direction;
53571             // FIXME... - this is not used here.. but might be elsewhere..
53572             this.sortState = state;
53573             
53574         } else {
53575             mstate = this.ds.sortToggle;
53576         }
53577         //remove existing sort classes..
53578         
53579         var sc = this.sortClasses;
53580         var hds = this.el.select(this.headerSelector).removeClass(sc);
53581         
53582         for(var f in mstate) {
53583         
53584             var sortColumn = this.cm.findColumnIndex(f);
53585             
53586             if(sortColumn != -1){
53587                 var sortDir = mstate[f];        
53588                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53589             }
53590         }
53591         
53592          
53593         
53594     },
53595
53596
53597     handleHeaderClick : function(g, index,e){
53598         
53599         Roo.log("header click");
53600         
53601         if (Roo.isTouch) {
53602             // touch events on header are handled by context
53603             this.handleHdCtx(g,index,e);
53604             return;
53605         }
53606         
53607         
53608         if(this.headersDisabled){
53609             return;
53610         }
53611         var dm = g.dataSource, cm = g.colModel;
53612         if(!cm.isSortable(index)){
53613             return;
53614         }
53615         g.stopEditing();
53616         
53617         if (dm.multiSort) {
53618             // update the sortOrder
53619             var so = [];
53620             for(var i = 0; i < cm.config.length; i++ ) {
53621                 
53622                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53623                     continue; // dont' bother, it's not in sort list or being set.
53624                 }
53625                 
53626                 so.push(cm.config[i].dataIndex);
53627             };
53628             dm.sortOrder = so;
53629         }
53630         
53631         
53632         dm.sort(cm.getDataIndex(index));
53633     },
53634
53635
53636     destroy : function(){
53637         if(this.colMenu){
53638             this.colMenu.removeAll();
53639             Roo.menu.MenuMgr.unregister(this.colMenu);
53640             this.colMenu.getEl().remove();
53641             delete this.colMenu;
53642         }
53643         if(this.hmenu){
53644             this.hmenu.removeAll();
53645             Roo.menu.MenuMgr.unregister(this.hmenu);
53646             this.hmenu.getEl().remove();
53647             delete this.hmenu;
53648         }
53649         if(this.grid.enableColumnMove){
53650             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53651             if(dds){
53652                 for(var dd in dds){
53653                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53654                         var elid = dds[dd].dragElId;
53655                         dds[dd].unreg();
53656                         Roo.get(elid).remove();
53657                     } else if(dds[dd].config.isTarget){
53658                         dds[dd].proxyTop.remove();
53659                         dds[dd].proxyBottom.remove();
53660                         dds[dd].unreg();
53661                     }
53662                     if(Roo.dd.DDM.locationCache[dd]){
53663                         delete Roo.dd.DDM.locationCache[dd];
53664                     }
53665                 }
53666                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53667             }
53668         }
53669         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53670         this.bind(null, null);
53671         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53672     },
53673
53674     handleLockChange : function(){
53675         this.refresh(true);
53676     },
53677
53678     onDenyColumnLock : function(){
53679
53680     },
53681
53682     onDenyColumnHide : function(){
53683
53684     },
53685
53686     handleHdMenuClick : function(item){
53687         var index = this.hdCtxIndex;
53688         var cm = this.cm, ds = this.ds;
53689         switch(item.id){
53690             case "asc":
53691                 ds.sort(cm.getDataIndex(index), "ASC");
53692                 break;
53693             case "desc":
53694                 ds.sort(cm.getDataIndex(index), "DESC");
53695                 break;
53696             case "lock":
53697                 var lc = cm.getLockedCount();
53698                 if(cm.getColumnCount(true) <= lc+1){
53699                     this.onDenyColumnLock();
53700                     return;
53701                 }
53702                 if(lc != index){
53703                     cm.setLocked(index, true, true);
53704                     cm.moveColumn(index, lc);
53705                     this.grid.fireEvent("columnmove", index, lc);
53706                 }else{
53707                     cm.setLocked(index, true);
53708                 }
53709             break;
53710             case "unlock":
53711                 var lc = cm.getLockedCount();
53712                 if((lc-1) != index){
53713                     cm.setLocked(index, false, true);
53714                     cm.moveColumn(index, lc-1);
53715                     this.grid.fireEvent("columnmove", index, lc-1);
53716                 }else{
53717                     cm.setLocked(index, false);
53718                 }
53719             break;
53720             case 'wider': // used to expand cols on touch..
53721             case 'narrow':
53722                 var cw = cm.getColumnWidth(index);
53723                 cw += (item.id == 'wider' ? 1 : -1) * 50;
53724                 cw = Math.max(0, cw);
53725                 cw = Math.min(cw,4000);
53726                 cm.setColumnWidth(index, cw);
53727                 break;
53728                 
53729             default:
53730                 index = cm.getIndexById(item.id.substr(4));
53731                 if(index != -1){
53732                     if(item.checked && cm.getColumnCount(true) <= 1){
53733                         this.onDenyColumnHide();
53734                         return false;
53735                     }
53736                     cm.setHidden(index, item.checked);
53737                 }
53738         }
53739         return true;
53740     },
53741
53742     beforeColMenuShow : function(){
53743         var cm = this.cm,  colCount = cm.getColumnCount();
53744         this.colMenu.removeAll();
53745         for(var i = 0; i < colCount; i++){
53746             this.colMenu.add(new Roo.menu.CheckItem({
53747                 id: "col-"+cm.getColumnId(i),
53748                 text: cm.getColumnHeader(i),
53749                 checked: !cm.isHidden(i),
53750                 hideOnClick:false
53751             }));
53752         }
53753     },
53754
53755     handleHdCtx : function(g, index, e){
53756         e.stopEvent();
53757         var hd = this.getHeaderCell(index);
53758         this.hdCtxIndex = index;
53759         var ms = this.hmenu.items, cm = this.cm;
53760         ms.get("asc").setDisabled(!cm.isSortable(index));
53761         ms.get("desc").setDisabled(!cm.isSortable(index));
53762         if(this.grid.enableColLock !== false){
53763             ms.get("lock").setDisabled(cm.isLocked(index));
53764             ms.get("unlock").setDisabled(!cm.isLocked(index));
53765         }
53766         this.hmenu.show(hd, "tl-bl");
53767     },
53768
53769     handleHdOver : function(e){
53770         var hd = this.findHeaderCell(e.getTarget());
53771         if(hd && !this.headersDisabled){
53772             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53773                this.fly(hd).addClass("x-grid-hd-over");
53774             }
53775         }
53776     },
53777
53778     handleHdOut : function(e){
53779         var hd = this.findHeaderCell(e.getTarget());
53780         if(hd){
53781             this.fly(hd).removeClass("x-grid-hd-over");
53782         }
53783     },
53784
53785     handleSplitDblClick : function(e, t){
53786         var i = this.getCellIndex(t);
53787         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53788             this.autoSizeColumn(i, true);
53789             this.layout();
53790         }
53791     },
53792
53793     render : function(){
53794
53795         var cm = this.cm;
53796         var colCount = cm.getColumnCount();
53797
53798         if(this.grid.monitorWindowResize === true){
53799             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53800         }
53801         var header = this.renderHeaders();
53802         var body = this.templates.body.apply({rows:""});
53803         var html = this.templates.master.apply({
53804             lockedBody: body,
53805             body: body,
53806             lockedHeader: header[0],
53807             header: header[1]
53808         });
53809
53810         //this.updateColumns();
53811
53812         this.grid.getGridEl().dom.innerHTML = html;
53813
53814         this.initElements();
53815         
53816         // a kludge to fix the random scolling effect in webkit
53817         this.el.on("scroll", function() {
53818             this.el.dom.scrollTop=0; // hopefully not recursive..
53819         },this);
53820
53821         this.scroller.on("scroll", this.handleScroll, this);
53822         this.lockedBody.on("mousewheel", this.handleWheel, this);
53823         this.mainBody.on("mousewheel", this.handleWheel, this);
53824
53825         this.mainHd.on("mouseover", this.handleHdOver, this);
53826         this.mainHd.on("mouseout", this.handleHdOut, this);
53827         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53828                 {delegate: "."+this.splitClass});
53829
53830         this.lockedHd.on("mouseover", this.handleHdOver, this);
53831         this.lockedHd.on("mouseout", this.handleHdOut, this);
53832         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53833                 {delegate: "."+this.splitClass});
53834
53835         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53836             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53837         }
53838
53839         this.updateSplitters();
53840
53841         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53842             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53843             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53844         }
53845
53846         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53847             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53848             this.hmenu.add(
53849                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53850                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53851             );
53852             if(this.grid.enableColLock !== false){
53853                 this.hmenu.add('-',
53854                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53855                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53856                 );
53857             }
53858             if (Roo.isTouch) {
53859                  this.hmenu.add('-',
53860                     {id:"wider", text: this.columnsWiderText},
53861                     {id:"narrow", text: this.columnsNarrowText }
53862                 );
53863                 
53864                  
53865             }
53866             
53867             if(this.grid.enableColumnHide !== false){
53868
53869                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53870                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53871                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53872
53873                 this.hmenu.add('-',
53874                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53875                 );
53876             }
53877             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53878
53879             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53880         }
53881
53882         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53883             this.dd = new Roo.grid.GridDragZone(this.grid, {
53884                 ddGroup : this.grid.ddGroup || 'GridDD'
53885             });
53886             
53887         }
53888
53889         /*
53890         for(var i = 0; i < colCount; i++){
53891             if(cm.isHidden(i)){
53892                 this.hideColumn(i);
53893             }
53894             if(cm.config[i].align){
53895                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53896                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53897             }
53898         }*/
53899         
53900         this.updateHeaderSortState();
53901
53902         this.beforeInitialResize();
53903         this.layout(true);
53904
53905         // two part rendering gives faster view to the user
53906         this.renderPhase2.defer(1, this);
53907     },
53908
53909     renderPhase2 : function(){
53910         // render the rows now
53911         this.refresh();
53912         if(this.grid.autoSizeColumns){
53913             this.autoSizeColumns();
53914         }
53915     },
53916
53917     beforeInitialResize : function(){
53918
53919     },
53920
53921     onColumnSplitterMoved : function(i, w){
53922         this.userResized = true;
53923         var cm = this.grid.colModel;
53924         cm.setColumnWidth(i, w, true);
53925         var cid = cm.getColumnId(i);
53926         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53927         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53928         this.updateSplitters();
53929         this.layout();
53930         this.grid.fireEvent("columnresize", i, w);
53931     },
53932
53933     syncRowHeights : function(startIndex, endIndex){
53934         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53935             startIndex = startIndex || 0;
53936             var mrows = this.getBodyTable().rows;
53937             var lrows = this.getLockedTable().rows;
53938             var len = mrows.length-1;
53939             endIndex = Math.min(endIndex || len, len);
53940             for(var i = startIndex; i <= endIndex; i++){
53941                 var m = mrows[i], l = lrows[i];
53942                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53943                 m.style.height = l.style.height = h + "px";
53944             }
53945         }
53946     },
53947
53948     layout : function(initialRender, is2ndPass){
53949         var g = this.grid;
53950         var auto = g.autoHeight;
53951         var scrollOffset = 16;
53952         var c = g.getGridEl(), cm = this.cm,
53953                 expandCol = g.autoExpandColumn,
53954                 gv = this;
53955         //c.beginMeasure();
53956
53957         if(!c.dom.offsetWidth){ // display:none?
53958             if(initialRender){
53959                 this.lockedWrap.show();
53960                 this.mainWrap.show();
53961             }
53962             return;
53963         }
53964
53965         var hasLock = this.cm.isLocked(0);
53966
53967         var tbh = this.headerPanel.getHeight();
53968         var bbh = this.footerPanel.getHeight();
53969
53970         if(auto){
53971             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53972             var newHeight = ch + c.getBorderWidth("tb");
53973             if(g.maxHeight){
53974                 newHeight = Math.min(g.maxHeight, newHeight);
53975             }
53976             c.setHeight(newHeight);
53977         }
53978
53979         if(g.autoWidth){
53980             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
53981         }
53982
53983         var s = this.scroller;
53984
53985         var csize = c.getSize(true);
53986
53987         this.el.setSize(csize.width, csize.height);
53988
53989         this.headerPanel.setWidth(csize.width);
53990         this.footerPanel.setWidth(csize.width);
53991
53992         var hdHeight = this.mainHd.getHeight();
53993         var vw = csize.width;
53994         var vh = csize.height - (tbh + bbh);
53995
53996         s.setSize(vw, vh);
53997
53998         var bt = this.getBodyTable();
53999         var ltWidth = hasLock ?
54000                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54001
54002         var scrollHeight = bt.offsetHeight;
54003         var scrollWidth = ltWidth + bt.offsetWidth;
54004         var vscroll = false, hscroll = false;
54005
54006         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54007
54008         var lw = this.lockedWrap, mw = this.mainWrap;
54009         var lb = this.lockedBody, mb = this.mainBody;
54010
54011         setTimeout(function(){
54012             var t = s.dom.offsetTop;
54013             var w = s.dom.clientWidth,
54014                 h = s.dom.clientHeight;
54015
54016             lw.setTop(t);
54017             lw.setSize(ltWidth, h);
54018
54019             mw.setLeftTop(ltWidth, t);
54020             mw.setSize(w-ltWidth, h);
54021
54022             lb.setHeight(h-hdHeight);
54023             mb.setHeight(h-hdHeight);
54024
54025             if(is2ndPass !== true && !gv.userResized && expandCol){
54026                 // high speed resize without full column calculation
54027                 
54028                 var ci = cm.getIndexById(expandCol);
54029                 if (ci < 0) {
54030                     ci = cm.findColumnIndex(expandCol);
54031                 }
54032                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54033                 var expandId = cm.getColumnId(ci);
54034                 var  tw = cm.getTotalWidth(false);
54035                 var currentWidth = cm.getColumnWidth(ci);
54036                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54037                 if(currentWidth != cw){
54038                     cm.setColumnWidth(ci, cw, true);
54039                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54040                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54041                     gv.updateSplitters();
54042                     gv.layout(false, true);
54043                 }
54044             }
54045
54046             if(initialRender){
54047                 lw.show();
54048                 mw.show();
54049             }
54050             //c.endMeasure();
54051         }, 10);
54052     },
54053
54054     onWindowResize : function(){
54055         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54056             return;
54057         }
54058         this.layout();
54059     },
54060
54061     appendFooter : function(parentEl){
54062         return null;
54063     },
54064
54065     sortAscText : "Sort Ascending",
54066     sortDescText : "Sort Descending",
54067     lockText : "Lock Column",
54068     unlockText : "Unlock Column",
54069     columnsText : "Columns",
54070  
54071     columnsWiderText : "Wider",
54072     columnsNarrowText : "Thinner"
54073 });
54074
54075
54076 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54077     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54078     this.proxy.el.addClass('x-grid3-col-dd');
54079 };
54080
54081 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54082     handleMouseDown : function(e){
54083
54084     },
54085
54086     callHandleMouseDown : function(e){
54087         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54088     }
54089 });
54090 /*
54091  * Based on:
54092  * Ext JS Library 1.1.1
54093  * Copyright(c) 2006-2007, Ext JS, LLC.
54094  *
54095  * Originally Released Under LGPL - original licence link has changed is not relivant.
54096  *
54097  * Fork - LGPL
54098  * <script type="text/javascript">
54099  */
54100  
54101 // private
54102 // This is a support class used internally by the Grid components
54103 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54104     this.grid = grid;
54105     this.view = grid.getView();
54106     this.proxy = this.view.resizeProxy;
54107     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54108         "gridSplitters" + this.grid.getGridEl().id, {
54109         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54110     });
54111     this.setHandleElId(Roo.id(hd));
54112     this.setOuterHandleElId(Roo.id(hd2));
54113     this.scroll = false;
54114 };
54115 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54116     fly: Roo.Element.fly,
54117
54118     b4StartDrag : function(x, y){
54119         this.view.headersDisabled = true;
54120         this.proxy.setHeight(this.view.mainWrap.getHeight());
54121         var w = this.cm.getColumnWidth(this.cellIndex);
54122         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54123         this.resetConstraints();
54124         this.setXConstraint(minw, 1000);
54125         this.setYConstraint(0, 0);
54126         this.minX = x - minw;
54127         this.maxX = x + 1000;
54128         this.startPos = x;
54129         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54130     },
54131
54132
54133     handleMouseDown : function(e){
54134         ev = Roo.EventObject.setEvent(e);
54135         var t = this.fly(ev.getTarget());
54136         if(t.hasClass("x-grid-split")){
54137             this.cellIndex = this.view.getCellIndex(t.dom);
54138             this.split = t.dom;
54139             this.cm = this.grid.colModel;
54140             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54141                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54142             }
54143         }
54144     },
54145
54146     endDrag : function(e){
54147         this.view.headersDisabled = false;
54148         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54149         var diff = endX - this.startPos;
54150         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54151     },
54152
54153     autoOffset : function(){
54154         this.setDelta(0,0);
54155     }
54156 });/*
54157  * Based on:
54158  * Ext JS Library 1.1.1
54159  * Copyright(c) 2006-2007, Ext JS, LLC.
54160  *
54161  * Originally Released Under LGPL - original licence link has changed is not relivant.
54162  *
54163  * Fork - LGPL
54164  * <script type="text/javascript">
54165  */
54166  
54167 // private
54168 // This is a support class used internally by the Grid components
54169 Roo.grid.GridDragZone = function(grid, config){
54170     this.view = grid.getView();
54171     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54172     if(this.view.lockedBody){
54173         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54174         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54175     }
54176     this.scroll = false;
54177     this.grid = grid;
54178     this.ddel = document.createElement('div');
54179     this.ddel.className = 'x-grid-dd-wrap';
54180 };
54181
54182 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54183     ddGroup : "GridDD",
54184
54185     getDragData : function(e){
54186         var t = Roo.lib.Event.getTarget(e);
54187         var rowIndex = this.view.findRowIndex(t);
54188         var sm = this.grid.selModel;
54189             
54190         //Roo.log(rowIndex);
54191         
54192         if (sm.getSelectedCell) {
54193             // cell selection..
54194             if (!sm.getSelectedCell()) {
54195                 return false;
54196             }
54197             if (rowIndex != sm.getSelectedCell()[0]) {
54198                 return false;
54199             }
54200         
54201         }
54202         
54203         if(rowIndex !== false){
54204             
54205             // if editorgrid.. 
54206             
54207             
54208             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54209                
54210             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54211               //  
54212             //}
54213             if (e.hasModifier()){
54214                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54215             }
54216             
54217             Roo.log("getDragData");
54218             
54219             return {
54220                 grid: this.grid,
54221                 ddel: this.ddel,
54222                 rowIndex: rowIndex,
54223                 selections:sm.getSelections ? sm.getSelections() : (
54224                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54225                 )
54226             };
54227         }
54228         return false;
54229     },
54230
54231     onInitDrag : function(e){
54232         var data = this.dragData;
54233         this.ddel.innerHTML = this.grid.getDragDropText();
54234         this.proxy.update(this.ddel);
54235         // fire start drag?
54236     },
54237
54238     afterRepair : function(){
54239         this.dragging = false;
54240     },
54241
54242     getRepairXY : function(e, data){
54243         return false;
54244     },
54245
54246     onEndDrag : function(data, e){
54247         // fire end drag?
54248     },
54249
54250     onValidDrop : function(dd, e, id){
54251         // fire drag drop?
54252         this.hideProxy();
54253     },
54254
54255     beforeInvalidDrop : function(e, id){
54256
54257     }
54258 });/*
54259  * Based on:
54260  * Ext JS Library 1.1.1
54261  * Copyright(c) 2006-2007, Ext JS, LLC.
54262  *
54263  * Originally Released Under LGPL - original licence link has changed is not relivant.
54264  *
54265  * Fork - LGPL
54266  * <script type="text/javascript">
54267  */
54268  
54269
54270 /**
54271  * @class Roo.grid.ColumnModel
54272  * @extends Roo.util.Observable
54273  * This is the default implementation of a ColumnModel used by the Grid. It defines
54274  * the columns in the grid.
54275  * <br>Usage:<br>
54276  <pre><code>
54277  var colModel = new Roo.grid.ColumnModel([
54278         {header: "Ticker", width: 60, sortable: true, locked: true},
54279         {header: "Company Name", width: 150, sortable: true},
54280         {header: "Market Cap.", width: 100, sortable: true},
54281         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54282         {header: "Employees", width: 100, sortable: true, resizable: false}
54283  ]);
54284  </code></pre>
54285  * <p>
54286  
54287  * The config options listed for this class are options which may appear in each
54288  * individual column definition.
54289  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54290  * @constructor
54291  * @param {Object} config An Array of column config objects. See this class's
54292  * config objects for details.
54293 */
54294 Roo.grid.ColumnModel = function(config){
54295         /**
54296      * The config passed into the constructor
54297      */
54298     this.config = config;
54299     this.lookup = {};
54300
54301     // if no id, create one
54302     // if the column does not have a dataIndex mapping,
54303     // map it to the order it is in the config
54304     for(var i = 0, len = config.length; i < len; i++){
54305         var c = config[i];
54306         if(typeof c.dataIndex == "undefined"){
54307             c.dataIndex = i;
54308         }
54309         if(typeof c.renderer == "string"){
54310             c.renderer = Roo.util.Format[c.renderer];
54311         }
54312         if(typeof c.id == "undefined"){
54313             c.id = Roo.id();
54314         }
54315         if(c.editor && c.editor.xtype){
54316             c.editor  = Roo.factory(c.editor, Roo.grid);
54317         }
54318         if(c.editor && c.editor.isFormField){
54319             c.editor = new Roo.grid.GridEditor(c.editor);
54320         }
54321         this.lookup[c.id] = c;
54322     }
54323
54324     /**
54325      * The width of columns which have no width specified (defaults to 100)
54326      * @type Number
54327      */
54328     this.defaultWidth = 100;
54329
54330     /**
54331      * Default sortable of columns which have no sortable specified (defaults to false)
54332      * @type Boolean
54333      */
54334     this.defaultSortable = false;
54335
54336     this.addEvents({
54337         /**
54338              * @event widthchange
54339              * Fires when the width of a column changes.
54340              * @param {ColumnModel} this
54341              * @param {Number} columnIndex The column index
54342              * @param {Number} newWidth The new width
54343              */
54344             "widthchange": true,
54345         /**
54346              * @event headerchange
54347              * Fires when the text of a header changes.
54348              * @param {ColumnModel} this
54349              * @param {Number} columnIndex The column index
54350              * @param {Number} newText The new header text
54351              */
54352             "headerchange": true,
54353         /**
54354              * @event hiddenchange
54355              * Fires when a column is hidden or "unhidden".
54356              * @param {ColumnModel} this
54357              * @param {Number} columnIndex The column index
54358              * @param {Boolean} hidden true if hidden, false otherwise
54359              */
54360             "hiddenchange": true,
54361             /**
54362          * @event columnmoved
54363          * Fires when a column is moved.
54364          * @param {ColumnModel} this
54365          * @param {Number} oldIndex
54366          * @param {Number} newIndex
54367          */
54368         "columnmoved" : true,
54369         /**
54370          * @event columlockchange
54371          * Fires when a column's locked state is changed
54372          * @param {ColumnModel} this
54373          * @param {Number} colIndex
54374          * @param {Boolean} locked true if locked
54375          */
54376         "columnlockchange" : true
54377     });
54378     Roo.grid.ColumnModel.superclass.constructor.call(this);
54379 };
54380 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54381     /**
54382      * @cfg {String} header The header text to display in the Grid view.
54383      */
54384     /**
54385      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54386      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54387      * specified, the column's index is used as an index into the Record's data Array.
54388      */
54389     /**
54390      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54391      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54392      */
54393     /**
54394      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54395      * Defaults to the value of the {@link #defaultSortable} property.
54396      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54397      */
54398     /**
54399      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54400      */
54401     /**
54402      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54403      */
54404     /**
54405      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54406      */
54407     /**
54408      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54409      */
54410     /**
54411      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54412      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54413      * default renderer uses the raw data value.
54414      */
54415        /**
54416      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54417      */
54418     /**
54419      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54420      */
54421
54422     /**
54423      * Returns the id of the column at the specified index.
54424      * @param {Number} index The column index
54425      * @return {String} the id
54426      */
54427     getColumnId : function(index){
54428         return this.config[index].id;
54429     },
54430
54431     /**
54432      * Returns the column for a specified id.
54433      * @param {String} id The column id
54434      * @return {Object} the column
54435      */
54436     getColumnById : function(id){
54437         return this.lookup[id];
54438     },
54439
54440     
54441     /**
54442      * Returns the column for a specified dataIndex.
54443      * @param {String} dataIndex The column dataIndex
54444      * @return {Object|Boolean} the column or false if not found
54445      */
54446     getColumnByDataIndex: function(dataIndex){
54447         var index = this.findColumnIndex(dataIndex);
54448         return index > -1 ? this.config[index] : false;
54449     },
54450     
54451     /**
54452      * Returns the index for a specified column id.
54453      * @param {String} id The column id
54454      * @return {Number} the index, or -1 if not found
54455      */
54456     getIndexById : function(id){
54457         for(var i = 0, len = this.config.length; i < len; i++){
54458             if(this.config[i].id == id){
54459                 return i;
54460             }
54461         }
54462         return -1;
54463     },
54464     
54465     /**
54466      * Returns the index for a specified column dataIndex.
54467      * @param {String} dataIndex The column dataIndex
54468      * @return {Number} the index, or -1 if not found
54469      */
54470     
54471     findColumnIndex : function(dataIndex){
54472         for(var i = 0, len = this.config.length; i < len; i++){
54473             if(this.config[i].dataIndex == dataIndex){
54474                 return i;
54475             }
54476         }
54477         return -1;
54478     },
54479     
54480     
54481     moveColumn : function(oldIndex, newIndex){
54482         var c = this.config[oldIndex];
54483         this.config.splice(oldIndex, 1);
54484         this.config.splice(newIndex, 0, c);
54485         this.dataMap = null;
54486         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54487     },
54488
54489     isLocked : function(colIndex){
54490         return this.config[colIndex].locked === true;
54491     },
54492
54493     setLocked : function(colIndex, value, suppressEvent){
54494         if(this.isLocked(colIndex) == value){
54495             return;
54496         }
54497         this.config[colIndex].locked = value;
54498         if(!suppressEvent){
54499             this.fireEvent("columnlockchange", this, colIndex, value);
54500         }
54501     },
54502
54503     getTotalLockedWidth : function(){
54504         var totalWidth = 0;
54505         for(var i = 0; i < this.config.length; i++){
54506             if(this.isLocked(i) && !this.isHidden(i)){
54507                 this.totalWidth += this.getColumnWidth(i);
54508             }
54509         }
54510         return totalWidth;
54511     },
54512
54513     getLockedCount : function(){
54514         for(var i = 0, len = this.config.length; i < len; i++){
54515             if(!this.isLocked(i)){
54516                 return i;
54517             }
54518         }
54519     },
54520
54521     /**
54522      * Returns the number of columns.
54523      * @return {Number}
54524      */
54525     getColumnCount : function(visibleOnly){
54526         if(visibleOnly === true){
54527             var c = 0;
54528             for(var i = 0, len = this.config.length; i < len; i++){
54529                 if(!this.isHidden(i)){
54530                     c++;
54531                 }
54532             }
54533             return c;
54534         }
54535         return this.config.length;
54536     },
54537
54538     /**
54539      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54540      * @param {Function} fn
54541      * @param {Object} scope (optional)
54542      * @return {Array} result
54543      */
54544     getColumnsBy : function(fn, scope){
54545         var r = [];
54546         for(var i = 0, len = this.config.length; i < len; i++){
54547             var c = this.config[i];
54548             if(fn.call(scope||this, c, i) === true){
54549                 r[r.length] = c;
54550             }
54551         }
54552         return r;
54553     },
54554
54555     /**
54556      * Returns true if the specified column is sortable.
54557      * @param {Number} col The column index
54558      * @return {Boolean}
54559      */
54560     isSortable : function(col){
54561         if(typeof this.config[col].sortable == "undefined"){
54562             return this.defaultSortable;
54563         }
54564         return this.config[col].sortable;
54565     },
54566
54567     /**
54568      * Returns the rendering (formatting) function defined for the column.
54569      * @param {Number} col The column index.
54570      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54571      */
54572     getRenderer : function(col){
54573         if(!this.config[col].renderer){
54574             return Roo.grid.ColumnModel.defaultRenderer;
54575         }
54576         return this.config[col].renderer;
54577     },
54578
54579     /**
54580      * Sets the rendering (formatting) function for a column.
54581      * @param {Number} col The column index
54582      * @param {Function} fn The function to use to process the cell's raw data
54583      * to return HTML markup for the grid view. The render function is called with
54584      * the following parameters:<ul>
54585      * <li>Data value.</li>
54586      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54587      * <li>css A CSS style string to apply to the table cell.</li>
54588      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54589      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54590      * <li>Row index</li>
54591      * <li>Column index</li>
54592      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54593      */
54594     setRenderer : function(col, fn){
54595         this.config[col].renderer = fn;
54596     },
54597
54598     /**
54599      * Returns the width for the specified column.
54600      * @param {Number} col The column index
54601      * @return {Number}
54602      */
54603     getColumnWidth : function(col){
54604         return this.config[col].width * 1 || this.defaultWidth;
54605     },
54606
54607     /**
54608      * Sets the width for a column.
54609      * @param {Number} col The column index
54610      * @param {Number} width The new width
54611      */
54612     setColumnWidth : function(col, width, suppressEvent){
54613         this.config[col].width = width;
54614         this.totalWidth = null;
54615         if(!suppressEvent){
54616              this.fireEvent("widthchange", this, col, width);
54617         }
54618     },
54619
54620     /**
54621      * Returns the total width of all columns.
54622      * @param {Boolean} includeHidden True to include hidden column widths
54623      * @return {Number}
54624      */
54625     getTotalWidth : function(includeHidden){
54626         if(!this.totalWidth){
54627             this.totalWidth = 0;
54628             for(var i = 0, len = this.config.length; i < len; i++){
54629                 if(includeHidden || !this.isHidden(i)){
54630                     this.totalWidth += this.getColumnWidth(i);
54631                 }
54632             }
54633         }
54634         return this.totalWidth;
54635     },
54636
54637     /**
54638      * Returns the header for the specified column.
54639      * @param {Number} col The column index
54640      * @return {String}
54641      */
54642     getColumnHeader : function(col){
54643         return this.config[col].header;
54644     },
54645
54646     /**
54647      * Sets the header for a column.
54648      * @param {Number} col The column index
54649      * @param {String} header The new header
54650      */
54651     setColumnHeader : function(col, header){
54652         this.config[col].header = header;
54653         this.fireEvent("headerchange", this, col, header);
54654     },
54655
54656     /**
54657      * Returns the tooltip for the specified column.
54658      * @param {Number} col The column index
54659      * @return {String}
54660      */
54661     getColumnTooltip : function(col){
54662             return this.config[col].tooltip;
54663     },
54664     /**
54665      * Sets the tooltip for a column.
54666      * @param {Number} col The column index
54667      * @param {String} tooltip The new tooltip
54668      */
54669     setColumnTooltip : function(col, tooltip){
54670             this.config[col].tooltip = tooltip;
54671     },
54672
54673     /**
54674      * Returns the dataIndex for the specified column.
54675      * @param {Number} col The column index
54676      * @return {Number}
54677      */
54678     getDataIndex : function(col){
54679         return this.config[col].dataIndex;
54680     },
54681
54682     /**
54683      * Sets the dataIndex for a column.
54684      * @param {Number} col The column index
54685      * @param {Number} dataIndex The new dataIndex
54686      */
54687     setDataIndex : function(col, dataIndex){
54688         this.config[col].dataIndex = dataIndex;
54689     },
54690
54691     
54692     
54693     /**
54694      * Returns true if the cell is editable.
54695      * @param {Number} colIndex The column index
54696      * @param {Number} rowIndex The row index
54697      * @return {Boolean}
54698      */
54699     isCellEditable : function(colIndex, rowIndex){
54700         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54701     },
54702
54703     /**
54704      * Returns the editor defined for the cell/column.
54705      * return false or null to disable editing.
54706      * @param {Number} colIndex The column index
54707      * @param {Number} rowIndex The row index
54708      * @return {Object}
54709      */
54710     getCellEditor : function(colIndex, rowIndex){
54711         return this.config[colIndex].editor;
54712     },
54713
54714     /**
54715      * Sets if a column is editable.
54716      * @param {Number} col The column index
54717      * @param {Boolean} editable True if the column is editable
54718      */
54719     setEditable : function(col, editable){
54720         this.config[col].editable = editable;
54721     },
54722
54723
54724     /**
54725      * Returns true if the column is hidden.
54726      * @param {Number} colIndex The column index
54727      * @return {Boolean}
54728      */
54729     isHidden : function(colIndex){
54730         return this.config[colIndex].hidden;
54731     },
54732
54733
54734     /**
54735      * Returns true if the column width cannot be changed
54736      */
54737     isFixed : function(colIndex){
54738         return this.config[colIndex].fixed;
54739     },
54740
54741     /**
54742      * Returns true if the column can be resized
54743      * @return {Boolean}
54744      */
54745     isResizable : function(colIndex){
54746         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54747     },
54748     /**
54749      * Sets if a column is hidden.
54750      * @param {Number} colIndex The column index
54751      * @param {Boolean} hidden True if the column is hidden
54752      */
54753     setHidden : function(colIndex, hidden){
54754         this.config[colIndex].hidden = hidden;
54755         this.totalWidth = null;
54756         this.fireEvent("hiddenchange", this, colIndex, hidden);
54757     },
54758
54759     /**
54760      * Sets the editor for a column.
54761      * @param {Number} col The column index
54762      * @param {Object} editor The editor object
54763      */
54764     setEditor : function(col, editor){
54765         this.config[col].editor = editor;
54766     }
54767 });
54768
54769 Roo.grid.ColumnModel.defaultRenderer = function(value){
54770         if(typeof value == "string" && value.length < 1){
54771             return "&#160;";
54772         }
54773         return value;
54774 };
54775
54776 // Alias for backwards compatibility
54777 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54778 /*
54779  * Based on:
54780  * Ext JS Library 1.1.1
54781  * Copyright(c) 2006-2007, Ext JS, LLC.
54782  *
54783  * Originally Released Under LGPL - original licence link has changed is not relivant.
54784  *
54785  * Fork - LGPL
54786  * <script type="text/javascript">
54787  */
54788
54789 /**
54790  * @class Roo.grid.AbstractSelectionModel
54791  * @extends Roo.util.Observable
54792  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54793  * implemented by descendant classes.  This class should not be directly instantiated.
54794  * @constructor
54795  */
54796 Roo.grid.AbstractSelectionModel = function(){
54797     this.locked = false;
54798     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54799 };
54800
54801 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54802     /** @ignore Called by the grid automatically. Do not call directly. */
54803     init : function(grid){
54804         this.grid = grid;
54805         this.initEvents();
54806     },
54807
54808     /**
54809      * Locks the selections.
54810      */
54811     lock : function(){
54812         this.locked = true;
54813     },
54814
54815     /**
54816      * Unlocks the selections.
54817      */
54818     unlock : function(){
54819         this.locked = false;
54820     },
54821
54822     /**
54823      * Returns true if the selections are locked.
54824      * @return {Boolean}
54825      */
54826     isLocked : function(){
54827         return this.locked;
54828     }
54829 });/*
54830  * Based on:
54831  * Ext JS Library 1.1.1
54832  * Copyright(c) 2006-2007, Ext JS, LLC.
54833  *
54834  * Originally Released Under LGPL - original licence link has changed is not relivant.
54835  *
54836  * Fork - LGPL
54837  * <script type="text/javascript">
54838  */
54839 /**
54840  * @extends Roo.grid.AbstractSelectionModel
54841  * @class Roo.grid.RowSelectionModel
54842  * The default SelectionModel used by {@link Roo.grid.Grid}.
54843  * It supports multiple selections and keyboard selection/navigation. 
54844  * @constructor
54845  * @param {Object} config
54846  */
54847 Roo.grid.RowSelectionModel = function(config){
54848     Roo.apply(this, config);
54849     this.selections = new Roo.util.MixedCollection(false, function(o){
54850         return o.id;
54851     });
54852
54853     this.last = false;
54854     this.lastActive = false;
54855
54856     this.addEvents({
54857         /**
54858              * @event selectionchange
54859              * Fires when the selection changes
54860              * @param {SelectionModel} this
54861              */
54862             "selectionchange" : true,
54863         /**
54864              * @event afterselectionchange
54865              * Fires after the selection changes (eg. by key press or clicking)
54866              * @param {SelectionModel} this
54867              */
54868             "afterselectionchange" : true,
54869         /**
54870              * @event beforerowselect
54871              * Fires when a row is selected being selected, return false to cancel.
54872              * @param {SelectionModel} this
54873              * @param {Number} rowIndex The selected index
54874              * @param {Boolean} keepExisting False if other selections will be cleared
54875              */
54876             "beforerowselect" : true,
54877         /**
54878              * @event rowselect
54879              * Fires when a row is selected.
54880              * @param {SelectionModel} this
54881              * @param {Number} rowIndex The selected index
54882              * @param {Roo.data.Record} r The record
54883              */
54884             "rowselect" : true,
54885         /**
54886              * @event rowdeselect
54887              * Fires when a row is deselected.
54888              * @param {SelectionModel} this
54889              * @param {Number} rowIndex The selected index
54890              */
54891         "rowdeselect" : true
54892     });
54893     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54894     this.locked = false;
54895 };
54896
54897 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54898     /**
54899      * @cfg {Boolean} singleSelect
54900      * True to allow selection of only one row at a time (defaults to false)
54901      */
54902     singleSelect : false,
54903
54904     // private
54905     initEvents : function(){
54906
54907         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54908             this.grid.on("mousedown", this.handleMouseDown, this);
54909         }else{ // allow click to work like normal
54910             this.grid.on("rowclick", this.handleDragableRowClick, this);
54911         }
54912
54913         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54914             "up" : function(e){
54915                 if(!e.shiftKey){
54916                     this.selectPrevious(e.shiftKey);
54917                 }else if(this.last !== false && this.lastActive !== false){
54918                     var last = this.last;
54919                     this.selectRange(this.last,  this.lastActive-1);
54920                     this.grid.getView().focusRow(this.lastActive);
54921                     if(last !== false){
54922                         this.last = last;
54923                     }
54924                 }else{
54925                     this.selectFirstRow();
54926                 }
54927                 this.fireEvent("afterselectionchange", this);
54928             },
54929             "down" : function(e){
54930                 if(!e.shiftKey){
54931                     this.selectNext(e.shiftKey);
54932                 }else if(this.last !== false && this.lastActive !== false){
54933                     var last = this.last;
54934                     this.selectRange(this.last,  this.lastActive+1);
54935                     this.grid.getView().focusRow(this.lastActive);
54936                     if(last !== false){
54937                         this.last = last;
54938                     }
54939                 }else{
54940                     this.selectFirstRow();
54941                 }
54942                 this.fireEvent("afterselectionchange", this);
54943             },
54944             scope: this
54945         });
54946
54947         var view = this.grid.view;
54948         view.on("refresh", this.onRefresh, this);
54949         view.on("rowupdated", this.onRowUpdated, this);
54950         view.on("rowremoved", this.onRemove, this);
54951     },
54952
54953     // private
54954     onRefresh : function(){
54955         var ds = this.grid.dataSource, i, v = this.grid.view;
54956         var s = this.selections;
54957         s.each(function(r){
54958             if((i = ds.indexOfId(r.id)) != -1){
54959                 v.onRowSelect(i);
54960             }else{
54961                 s.remove(r);
54962             }
54963         });
54964     },
54965
54966     // private
54967     onRemove : function(v, index, r){
54968         this.selections.remove(r);
54969     },
54970
54971     // private
54972     onRowUpdated : function(v, index, r){
54973         if(this.isSelected(r)){
54974             v.onRowSelect(index);
54975         }
54976     },
54977
54978     /**
54979      * Select records.
54980      * @param {Array} records The records to select
54981      * @param {Boolean} keepExisting (optional) True to keep existing selections
54982      */
54983     selectRecords : function(records, keepExisting){
54984         if(!keepExisting){
54985             this.clearSelections();
54986         }
54987         var ds = this.grid.dataSource;
54988         for(var i = 0, len = records.length; i < len; i++){
54989             this.selectRow(ds.indexOf(records[i]), true);
54990         }
54991     },
54992
54993     /**
54994      * Gets the number of selected rows.
54995      * @return {Number}
54996      */
54997     getCount : function(){
54998         return this.selections.length;
54999     },
55000
55001     /**
55002      * Selects the first row in the grid.
55003      */
55004     selectFirstRow : function(){
55005         this.selectRow(0);
55006     },
55007
55008     /**
55009      * Select the last row.
55010      * @param {Boolean} keepExisting (optional) True to keep existing selections
55011      */
55012     selectLastRow : function(keepExisting){
55013         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55014     },
55015
55016     /**
55017      * Selects the row immediately following the last selected row.
55018      * @param {Boolean} keepExisting (optional) True to keep existing selections
55019      */
55020     selectNext : function(keepExisting){
55021         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55022             this.selectRow(this.last+1, keepExisting);
55023             this.grid.getView().focusRow(this.last);
55024         }
55025     },
55026
55027     /**
55028      * Selects the row that precedes the last selected row.
55029      * @param {Boolean} keepExisting (optional) True to keep existing selections
55030      */
55031     selectPrevious : function(keepExisting){
55032         if(this.last){
55033             this.selectRow(this.last-1, keepExisting);
55034             this.grid.getView().focusRow(this.last);
55035         }
55036     },
55037
55038     /**
55039      * Returns the selected records
55040      * @return {Array} Array of selected records
55041      */
55042     getSelections : function(){
55043         return [].concat(this.selections.items);
55044     },
55045
55046     /**
55047      * Returns the first selected record.
55048      * @return {Record}
55049      */
55050     getSelected : function(){
55051         return this.selections.itemAt(0);
55052     },
55053
55054
55055     /**
55056      * Clears all selections.
55057      */
55058     clearSelections : function(fast){
55059         if(this.locked) return;
55060         if(fast !== true){
55061             var ds = this.grid.dataSource;
55062             var s = this.selections;
55063             s.each(function(r){
55064                 this.deselectRow(ds.indexOfId(r.id));
55065             }, this);
55066             s.clear();
55067         }else{
55068             this.selections.clear();
55069         }
55070         this.last = false;
55071     },
55072
55073
55074     /**
55075      * Selects all rows.
55076      */
55077     selectAll : function(){
55078         if(this.locked) return;
55079         this.selections.clear();
55080         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55081             this.selectRow(i, true);
55082         }
55083     },
55084
55085     /**
55086      * Returns True if there is a selection.
55087      * @return {Boolean}
55088      */
55089     hasSelection : function(){
55090         return this.selections.length > 0;
55091     },
55092
55093     /**
55094      * Returns True if the specified row is selected.
55095      * @param {Number/Record} record The record or index of the record to check
55096      * @return {Boolean}
55097      */
55098     isSelected : function(index){
55099         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55100         return (r && this.selections.key(r.id) ? true : false);
55101     },
55102
55103     /**
55104      * Returns True if the specified record id is selected.
55105      * @param {String} id The id of record to check
55106      * @return {Boolean}
55107      */
55108     isIdSelected : function(id){
55109         return (this.selections.key(id) ? true : false);
55110     },
55111
55112     // private
55113     handleMouseDown : function(e, t){
55114         var view = this.grid.getView(), rowIndex;
55115         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55116             return;
55117         };
55118         if(e.shiftKey && this.last !== false){
55119             var last = this.last;
55120             this.selectRange(last, rowIndex, e.ctrlKey);
55121             this.last = last; // reset the last
55122             view.focusRow(rowIndex);
55123         }else{
55124             var isSelected = this.isSelected(rowIndex);
55125             if(e.button !== 0 && isSelected){
55126                 view.focusRow(rowIndex);
55127             }else if(e.ctrlKey && isSelected){
55128                 this.deselectRow(rowIndex);
55129             }else if(!isSelected){
55130                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55131                 view.focusRow(rowIndex);
55132             }
55133         }
55134         this.fireEvent("afterselectionchange", this);
55135     },
55136     // private
55137     handleDragableRowClick :  function(grid, rowIndex, e) 
55138     {
55139         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55140             this.selectRow(rowIndex, false);
55141             grid.view.focusRow(rowIndex);
55142              this.fireEvent("afterselectionchange", this);
55143         }
55144     },
55145     
55146     /**
55147      * Selects multiple rows.
55148      * @param {Array} rows Array of the indexes of the row to select
55149      * @param {Boolean} keepExisting (optional) True to keep existing selections
55150      */
55151     selectRows : function(rows, keepExisting){
55152         if(!keepExisting){
55153             this.clearSelections();
55154         }
55155         for(var i = 0, len = rows.length; i < len; i++){
55156             this.selectRow(rows[i], true);
55157         }
55158     },
55159
55160     /**
55161      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55162      * @param {Number} startRow The index of the first row in the range
55163      * @param {Number} endRow The index of the last row in the range
55164      * @param {Boolean} keepExisting (optional) True to retain existing selections
55165      */
55166     selectRange : function(startRow, endRow, keepExisting){
55167         if(this.locked) return;
55168         if(!keepExisting){
55169             this.clearSelections();
55170         }
55171         if(startRow <= endRow){
55172             for(var i = startRow; i <= endRow; i++){
55173                 this.selectRow(i, true);
55174             }
55175         }else{
55176             for(var i = startRow; i >= endRow; i--){
55177                 this.selectRow(i, true);
55178             }
55179         }
55180     },
55181
55182     /**
55183      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55184      * @param {Number} startRow The index of the first row in the range
55185      * @param {Number} endRow The index of the last row in the range
55186      */
55187     deselectRange : function(startRow, endRow, preventViewNotify){
55188         if(this.locked) return;
55189         for(var i = startRow; i <= endRow; i++){
55190             this.deselectRow(i, preventViewNotify);
55191         }
55192     },
55193
55194     /**
55195      * Selects a row.
55196      * @param {Number} row The index of the row to select
55197      * @param {Boolean} keepExisting (optional) True to keep existing selections
55198      */
55199     selectRow : function(index, keepExisting, preventViewNotify){
55200         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55201         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55202             if(!keepExisting || this.singleSelect){
55203                 this.clearSelections();
55204             }
55205             var r = this.grid.dataSource.getAt(index);
55206             this.selections.add(r);
55207             this.last = this.lastActive = index;
55208             if(!preventViewNotify){
55209                 this.grid.getView().onRowSelect(index);
55210             }
55211             this.fireEvent("rowselect", this, index, r);
55212             this.fireEvent("selectionchange", this);
55213         }
55214     },
55215
55216     /**
55217      * Deselects a row.
55218      * @param {Number} row The index of the row to deselect
55219      */
55220     deselectRow : function(index, preventViewNotify){
55221         if(this.locked) return;
55222         if(this.last == index){
55223             this.last = false;
55224         }
55225         if(this.lastActive == index){
55226             this.lastActive = false;
55227         }
55228         var r = this.grid.dataSource.getAt(index);
55229         this.selections.remove(r);
55230         if(!preventViewNotify){
55231             this.grid.getView().onRowDeselect(index);
55232         }
55233         this.fireEvent("rowdeselect", this, index);
55234         this.fireEvent("selectionchange", this);
55235     },
55236
55237     // private
55238     restoreLast : function(){
55239         if(this._last){
55240             this.last = this._last;
55241         }
55242     },
55243
55244     // private
55245     acceptsNav : function(row, col, cm){
55246         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55247     },
55248
55249     // private
55250     onEditorKey : function(field, e){
55251         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55252         if(k == e.TAB){
55253             e.stopEvent();
55254             ed.completeEdit();
55255             if(e.shiftKey){
55256                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55257             }else{
55258                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55259             }
55260         }else if(k == e.ENTER && !e.ctrlKey){
55261             e.stopEvent();
55262             ed.completeEdit();
55263             if(e.shiftKey){
55264                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55265             }else{
55266                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55267             }
55268         }else if(k == e.ESC){
55269             ed.cancelEdit();
55270         }
55271         if(newCell){
55272             g.startEditing(newCell[0], newCell[1]);
55273         }
55274     }
55275 });/*
55276  * Based on:
55277  * Ext JS Library 1.1.1
55278  * Copyright(c) 2006-2007, Ext JS, LLC.
55279  *
55280  * Originally Released Under LGPL - original licence link has changed is not relivant.
55281  *
55282  * Fork - LGPL
55283  * <script type="text/javascript">
55284  */
55285 /**
55286  * @class Roo.grid.CellSelectionModel
55287  * @extends Roo.grid.AbstractSelectionModel
55288  * This class provides the basic implementation for cell selection in a grid.
55289  * @constructor
55290  * @param {Object} config The object containing the configuration of this model.
55291  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55292  */
55293 Roo.grid.CellSelectionModel = function(config){
55294     Roo.apply(this, config);
55295
55296     this.selection = null;
55297
55298     this.addEvents({
55299         /**
55300              * @event beforerowselect
55301              * Fires before a cell is selected.
55302              * @param {SelectionModel} this
55303              * @param {Number} rowIndex The selected row index
55304              * @param {Number} colIndex The selected cell index
55305              */
55306             "beforecellselect" : true,
55307         /**
55308              * @event cellselect
55309              * Fires when a cell is selected.
55310              * @param {SelectionModel} this
55311              * @param {Number} rowIndex The selected row index
55312              * @param {Number} colIndex The selected cell index
55313              */
55314             "cellselect" : true,
55315         /**
55316              * @event selectionchange
55317              * Fires when the active selection changes.
55318              * @param {SelectionModel} this
55319              * @param {Object} selection null for no selection or an object (o) with two properties
55320                 <ul>
55321                 <li>o.record: the record object for the row the selection is in</li>
55322                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55323                 </ul>
55324              */
55325             "selectionchange" : true,
55326         /**
55327              * @event tabend
55328              * Fires when the tab (or enter) was pressed on the last editable cell
55329              * You can use this to trigger add new row.
55330              * @param {SelectionModel} this
55331              */
55332             "tabend" : true,
55333          /**
55334              * @event beforeeditnext
55335              * Fires before the next editable sell is made active
55336              * You can use this to skip to another cell or fire the tabend
55337              *    if you set cell to false
55338              * @param {Object} eventdata object : { cell : [ row, col ] } 
55339              */
55340             "beforeeditnext" : true
55341     });
55342     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55343 };
55344
55345 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55346     
55347     enter_is_tab: false,
55348
55349     /** @ignore */
55350     initEvents : function(){
55351         this.grid.on("mousedown", this.handleMouseDown, this);
55352         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55353         var view = this.grid.view;
55354         view.on("refresh", this.onViewChange, this);
55355         view.on("rowupdated", this.onRowUpdated, this);
55356         view.on("beforerowremoved", this.clearSelections, this);
55357         view.on("beforerowsinserted", this.clearSelections, this);
55358         if(this.grid.isEditor){
55359             this.grid.on("beforeedit", this.beforeEdit,  this);
55360         }
55361     },
55362
55363         //private
55364     beforeEdit : function(e){
55365         this.select(e.row, e.column, false, true, e.record);
55366     },
55367
55368         //private
55369     onRowUpdated : function(v, index, r){
55370         if(this.selection && this.selection.record == r){
55371             v.onCellSelect(index, this.selection.cell[1]);
55372         }
55373     },
55374
55375         //private
55376     onViewChange : function(){
55377         this.clearSelections(true);
55378     },
55379
55380         /**
55381          * Returns the currently selected cell,.
55382          * @return {Array} The selected cell (row, column) or null if none selected.
55383          */
55384     getSelectedCell : function(){
55385         return this.selection ? this.selection.cell : null;
55386     },
55387
55388     /**
55389      * Clears all selections.
55390      * @param {Boolean} true to prevent the gridview from being notified about the change.
55391      */
55392     clearSelections : function(preventNotify){
55393         var s = this.selection;
55394         if(s){
55395             if(preventNotify !== true){
55396                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55397             }
55398             this.selection = null;
55399             this.fireEvent("selectionchange", this, null);
55400         }
55401     },
55402
55403     /**
55404      * Returns true if there is a selection.
55405      * @return {Boolean}
55406      */
55407     hasSelection : function(){
55408         return this.selection ? true : false;
55409     },
55410
55411     /** @ignore */
55412     handleMouseDown : function(e, t){
55413         var v = this.grid.getView();
55414         if(this.isLocked()){
55415             return;
55416         };
55417         var row = v.findRowIndex(t);
55418         var cell = v.findCellIndex(t);
55419         if(row !== false && cell !== false){
55420             this.select(row, cell);
55421         }
55422     },
55423
55424     /**
55425      * Selects a cell.
55426      * @param {Number} rowIndex
55427      * @param {Number} collIndex
55428      */
55429     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55430         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55431             this.clearSelections();
55432             r = r || this.grid.dataSource.getAt(rowIndex);
55433             this.selection = {
55434                 record : r,
55435                 cell : [rowIndex, colIndex]
55436             };
55437             if(!preventViewNotify){
55438                 var v = this.grid.getView();
55439                 v.onCellSelect(rowIndex, colIndex);
55440                 if(preventFocus !== true){
55441                     v.focusCell(rowIndex, colIndex);
55442                 }
55443             }
55444             this.fireEvent("cellselect", this, rowIndex, colIndex);
55445             this.fireEvent("selectionchange", this, this.selection);
55446         }
55447     },
55448
55449         //private
55450     isSelectable : function(rowIndex, colIndex, cm){
55451         return !cm.isHidden(colIndex);
55452     },
55453
55454     /** @ignore */
55455     handleKeyDown : function(e){
55456         //Roo.log('Cell Sel Model handleKeyDown');
55457         if(!e.isNavKeyPress()){
55458             return;
55459         }
55460         var g = this.grid, s = this.selection;
55461         if(!s){
55462             e.stopEvent();
55463             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55464             if(cell){
55465                 this.select(cell[0], cell[1]);
55466             }
55467             return;
55468         }
55469         var sm = this;
55470         var walk = function(row, col, step){
55471             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55472         };
55473         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55474         var newCell;
55475
55476       
55477
55478         switch(k){
55479             case e.TAB:
55480                 // handled by onEditorKey
55481                 if (g.isEditor && g.editing) {
55482                     return;
55483                 }
55484                 if(e.shiftKey) {
55485                     newCell = walk(r, c-1, -1);
55486                 } else {
55487                     newCell = walk(r, c+1, 1);
55488                 }
55489                 break;
55490             
55491             case e.DOWN:
55492                newCell = walk(r+1, c, 1);
55493                 break;
55494             
55495             case e.UP:
55496                 newCell = walk(r-1, c, -1);
55497                 break;
55498             
55499             case e.RIGHT:
55500                 newCell = walk(r, c+1, 1);
55501                 break;
55502             
55503             case e.LEFT:
55504                 newCell = walk(r, c-1, -1);
55505                 break;
55506             
55507             case e.ENTER:
55508                 
55509                 if(g.isEditor && !g.editing){
55510                    g.startEditing(r, c);
55511                    e.stopEvent();
55512                    return;
55513                 }
55514                 
55515                 
55516              break;
55517         };
55518         if(newCell){
55519             this.select(newCell[0], newCell[1]);
55520             e.stopEvent();
55521             
55522         }
55523     },
55524
55525     acceptsNav : function(row, col, cm){
55526         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55527     },
55528     /**
55529      * Selects a cell.
55530      * @param {Number} field (not used) - as it's normally used as a listener
55531      * @param {Number} e - event - fake it by using
55532      *
55533      * var e = Roo.EventObjectImpl.prototype;
55534      * e.keyCode = e.TAB
55535      *
55536      * 
55537      */
55538     onEditorKey : function(field, e){
55539         
55540         var k = e.getKey(),
55541             newCell,
55542             g = this.grid,
55543             ed = g.activeEditor,
55544             forward = false;
55545         ///Roo.log('onEditorKey' + k);
55546         
55547         
55548         if (this.enter_is_tab && k == e.ENTER) {
55549             k = e.TAB;
55550         }
55551         
55552         if(k == e.TAB){
55553             if(e.shiftKey){
55554                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55555             }else{
55556                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55557                 forward = true;
55558             }
55559             
55560             e.stopEvent();
55561             
55562         } else if(k == e.ENTER &&  !e.ctrlKey){
55563             ed.completeEdit();
55564             e.stopEvent();
55565             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55566         
55567                 } else if(k == e.ESC){
55568             ed.cancelEdit();
55569         }
55570                 
55571         if (newCell) {
55572             var ecall = { cell : newCell, forward : forward };
55573             this.fireEvent('beforeeditnext', ecall );
55574             newCell = ecall.cell;
55575                         forward = ecall.forward;
55576         }
55577                 
55578         if(newCell){
55579             //Roo.log('next cell after edit');
55580             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55581         } else if (forward) {
55582             // tabbed past last
55583             this.fireEvent.defer(100, this, ['tabend',this]);
55584         }
55585     }
55586 });/*
55587  * Based on:
55588  * Ext JS Library 1.1.1
55589  * Copyright(c) 2006-2007, Ext JS, LLC.
55590  *
55591  * Originally Released Under LGPL - original licence link has changed is not relivant.
55592  *
55593  * Fork - LGPL
55594  * <script type="text/javascript">
55595  */
55596  
55597 /**
55598  * @class Roo.grid.EditorGrid
55599  * @extends Roo.grid.Grid
55600  * Class for creating and editable grid.
55601  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55602  * The container MUST have some type of size defined for the grid to fill. The container will be 
55603  * automatically set to position relative if it isn't already.
55604  * @param {Object} dataSource The data model to bind to
55605  * @param {Object} colModel The column model with info about this grid's columns
55606  */
55607 Roo.grid.EditorGrid = function(container, config){
55608     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55609     this.getGridEl().addClass("xedit-grid");
55610
55611     if(!this.selModel){
55612         this.selModel = new Roo.grid.CellSelectionModel();
55613     }
55614
55615     this.activeEditor = null;
55616
55617         this.addEvents({
55618             /**
55619              * @event beforeedit
55620              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55621              * <ul style="padding:5px;padding-left:16px;">
55622              * <li>grid - This grid</li>
55623              * <li>record - The record being edited</li>
55624              * <li>field - The field name being edited</li>
55625              * <li>value - The value for the field being edited.</li>
55626              * <li>row - The grid row index</li>
55627              * <li>column - The grid column index</li>
55628              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55629              * </ul>
55630              * @param {Object} e An edit event (see above for description)
55631              */
55632             "beforeedit" : true,
55633             /**
55634              * @event afteredit
55635              * Fires after a cell is edited. <br />
55636              * <ul style="padding:5px;padding-left:16px;">
55637              * <li>grid - This grid</li>
55638              * <li>record - The record being edited</li>
55639              * <li>field - The field name being edited</li>
55640              * <li>value - The value being set</li>
55641              * <li>originalValue - The original value for the field, before the edit.</li>
55642              * <li>row - The grid row index</li>
55643              * <li>column - The grid column index</li>
55644              * </ul>
55645              * @param {Object} e An edit event (see above for description)
55646              */
55647             "afteredit" : true,
55648             /**
55649              * @event validateedit
55650              * Fires after a cell is edited, but before the value is set in the record. 
55651          * You can use this to modify the value being set in the field, Return false
55652              * to cancel the change. The edit event object has the following properties <br />
55653              * <ul style="padding:5px;padding-left:16px;">
55654          * <li>editor - This editor</li>
55655              * <li>grid - This grid</li>
55656              * <li>record - The record being edited</li>
55657              * <li>field - The field name being edited</li>
55658              * <li>value - The value being set</li>
55659              * <li>originalValue - The original value for the field, before the edit.</li>
55660              * <li>row - The grid row index</li>
55661              * <li>column - The grid column index</li>
55662              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55663              * </ul>
55664              * @param {Object} e An edit event (see above for description)
55665              */
55666             "validateedit" : true
55667         });
55668     this.on("bodyscroll", this.stopEditing,  this);
55669     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55670 };
55671
55672 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55673     /**
55674      * @cfg {Number} clicksToEdit
55675      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55676      */
55677     clicksToEdit: 2,
55678
55679     // private
55680     isEditor : true,
55681     // private
55682     trackMouseOver: false, // causes very odd FF errors
55683
55684     onCellDblClick : function(g, row, col){
55685         this.startEditing(row, col);
55686     },
55687
55688     onEditComplete : function(ed, value, startValue){
55689         this.editing = false;
55690         this.activeEditor = null;
55691         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55692         var r = ed.record;
55693         var field = this.colModel.getDataIndex(ed.col);
55694         var e = {
55695             grid: this,
55696             record: r,
55697             field: field,
55698             originalValue: startValue,
55699             value: value,
55700             row: ed.row,
55701             column: ed.col,
55702             cancel:false,
55703             editor: ed
55704         };
55705         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55706         cell.show();
55707           
55708         if(String(value) !== String(startValue)){
55709             
55710             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55711                 r.set(field, e.value);
55712                 // if we are dealing with a combo box..
55713                 // then we also set the 'name' colum to be the displayField
55714                 if (ed.field.displayField && ed.field.name) {
55715                     r.set(ed.field.name, ed.field.el.dom.value);
55716                 }
55717                 
55718                 delete e.cancel; //?? why!!!
55719                 this.fireEvent("afteredit", e);
55720             }
55721         } else {
55722             this.fireEvent("afteredit", e); // always fire it!
55723         }
55724         this.view.focusCell(ed.row, ed.col);
55725     },
55726
55727     /**
55728      * Starts editing the specified for the specified row/column
55729      * @param {Number} rowIndex
55730      * @param {Number} colIndex
55731      */
55732     startEditing : function(row, col){
55733         this.stopEditing();
55734         if(this.colModel.isCellEditable(col, row)){
55735             this.view.ensureVisible(row, col, true);
55736           
55737             var r = this.dataSource.getAt(row);
55738             var field = this.colModel.getDataIndex(col);
55739             var cell = Roo.get(this.view.getCell(row,col));
55740             var e = {
55741                 grid: this,
55742                 record: r,
55743                 field: field,
55744                 value: r.data[field],
55745                 row: row,
55746                 column: col,
55747                 cancel:false 
55748             };
55749             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55750                 this.editing = true;
55751                 var ed = this.colModel.getCellEditor(col, row);
55752                 
55753                 if (!ed) {
55754                     return;
55755                 }
55756                 if(!ed.rendered){
55757                     ed.render(ed.parentEl || document.body);
55758                 }
55759                 ed.field.reset();
55760                
55761                 cell.hide();
55762                 
55763                 (function(){ // complex but required for focus issues in safari, ie and opera
55764                     ed.row = row;
55765                     ed.col = col;
55766                     ed.record = r;
55767                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55768                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55769                     this.activeEditor = ed;
55770                     var v = r.data[field];
55771                     ed.startEdit(this.view.getCell(row, col), v);
55772                     // combo's with 'displayField and name set
55773                     if (ed.field.displayField && ed.field.name) {
55774                         ed.field.el.dom.value = r.data[ed.field.name];
55775                     }
55776                     
55777                     
55778                 }).defer(50, this);
55779             }
55780         }
55781     },
55782         
55783     /**
55784      * Stops any active editing
55785      */
55786     stopEditing : function(){
55787         if(this.activeEditor){
55788             this.activeEditor.completeEdit();
55789         }
55790         this.activeEditor = null;
55791     },
55792         
55793          /**
55794      * Called to get grid's drag proxy text, by default returns this.ddText.
55795      * @return {String}
55796      */
55797     getDragDropText : function(){
55798         var count = this.selModel.getSelectedCell() ? 1 : 0;
55799         return String.format(this.ddText, count, count == 1 ? '' : 's');
55800     }
55801         
55802 });/*
55803  * Based on:
55804  * Ext JS Library 1.1.1
55805  * Copyright(c) 2006-2007, Ext JS, LLC.
55806  *
55807  * Originally Released Under LGPL - original licence link has changed is not relivant.
55808  *
55809  * Fork - LGPL
55810  * <script type="text/javascript">
55811  */
55812
55813 // private - not really -- you end up using it !
55814 // This is a support class used internally by the Grid components
55815
55816 /**
55817  * @class Roo.grid.GridEditor
55818  * @extends Roo.Editor
55819  * Class for creating and editable grid elements.
55820  * @param {Object} config any settings (must include field)
55821  */
55822 Roo.grid.GridEditor = function(field, config){
55823     if (!config && field.field) {
55824         config = field;
55825         field = Roo.factory(config.field, Roo.form);
55826     }
55827     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55828     field.monitorTab = false;
55829 };
55830
55831 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55832     
55833     /**
55834      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55835      */
55836     
55837     alignment: "tl-tl",
55838     autoSize: "width",
55839     hideEl : false,
55840     cls: "x-small-editor x-grid-editor",
55841     shim:false,
55842     shadow:"frame"
55843 });/*
55844  * Based on:
55845  * Ext JS Library 1.1.1
55846  * Copyright(c) 2006-2007, Ext JS, LLC.
55847  *
55848  * Originally Released Under LGPL - original licence link has changed is not relivant.
55849  *
55850  * Fork - LGPL
55851  * <script type="text/javascript">
55852  */
55853   
55854
55855   
55856 Roo.grid.PropertyRecord = Roo.data.Record.create([
55857     {name:'name',type:'string'},  'value'
55858 ]);
55859
55860
55861 Roo.grid.PropertyStore = function(grid, source){
55862     this.grid = grid;
55863     this.store = new Roo.data.Store({
55864         recordType : Roo.grid.PropertyRecord
55865     });
55866     this.store.on('update', this.onUpdate,  this);
55867     if(source){
55868         this.setSource(source);
55869     }
55870     Roo.grid.PropertyStore.superclass.constructor.call(this);
55871 };
55872
55873
55874
55875 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55876     setSource : function(o){
55877         this.source = o;
55878         this.store.removeAll();
55879         var data = [];
55880         for(var k in o){
55881             if(this.isEditableValue(o[k])){
55882                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55883             }
55884         }
55885         this.store.loadRecords({records: data}, {}, true);
55886     },
55887
55888     onUpdate : function(ds, record, type){
55889         if(type == Roo.data.Record.EDIT){
55890             var v = record.data['value'];
55891             var oldValue = record.modified['value'];
55892             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55893                 this.source[record.id] = v;
55894                 record.commit();
55895                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55896             }else{
55897                 record.reject();
55898             }
55899         }
55900     },
55901
55902     getProperty : function(row){
55903        return this.store.getAt(row);
55904     },
55905
55906     isEditableValue: function(val){
55907         if(val && val instanceof Date){
55908             return true;
55909         }else if(typeof val == 'object' || typeof val == 'function'){
55910             return false;
55911         }
55912         return true;
55913     },
55914
55915     setValue : function(prop, value){
55916         this.source[prop] = value;
55917         this.store.getById(prop).set('value', value);
55918     },
55919
55920     getSource : function(){
55921         return this.source;
55922     }
55923 });
55924
55925 Roo.grid.PropertyColumnModel = function(grid, store){
55926     this.grid = grid;
55927     var g = Roo.grid;
55928     g.PropertyColumnModel.superclass.constructor.call(this, [
55929         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55930         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55931     ]);
55932     this.store = store;
55933     this.bselect = Roo.DomHelper.append(document.body, {
55934         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55935             {tag: 'option', value: 'true', html: 'true'},
55936             {tag: 'option', value: 'false', html: 'false'}
55937         ]
55938     });
55939     Roo.id(this.bselect);
55940     var f = Roo.form;
55941     this.editors = {
55942         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55943         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55944         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55945         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55946         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55947     };
55948     this.renderCellDelegate = this.renderCell.createDelegate(this);
55949     this.renderPropDelegate = this.renderProp.createDelegate(this);
55950 };
55951
55952 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55953     
55954     
55955     nameText : 'Name',
55956     valueText : 'Value',
55957     
55958     dateFormat : 'm/j/Y',
55959     
55960     
55961     renderDate : function(dateVal){
55962         return dateVal.dateFormat(this.dateFormat);
55963     },
55964
55965     renderBool : function(bVal){
55966         return bVal ? 'true' : 'false';
55967     },
55968
55969     isCellEditable : function(colIndex, rowIndex){
55970         return colIndex == 1;
55971     },
55972
55973     getRenderer : function(col){
55974         return col == 1 ?
55975             this.renderCellDelegate : this.renderPropDelegate;
55976     },
55977
55978     renderProp : function(v){
55979         return this.getPropertyName(v);
55980     },
55981
55982     renderCell : function(val){
55983         var rv = val;
55984         if(val instanceof Date){
55985             rv = this.renderDate(val);
55986         }else if(typeof val == 'boolean'){
55987             rv = this.renderBool(val);
55988         }
55989         return Roo.util.Format.htmlEncode(rv);
55990     },
55991
55992     getPropertyName : function(name){
55993         var pn = this.grid.propertyNames;
55994         return pn && pn[name] ? pn[name] : name;
55995     },
55996
55997     getCellEditor : function(colIndex, rowIndex){
55998         var p = this.store.getProperty(rowIndex);
55999         var n = p.data['name'], val = p.data['value'];
56000         
56001         if(typeof(this.grid.customEditors[n]) == 'string'){
56002             return this.editors[this.grid.customEditors[n]];
56003         }
56004         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56005             return this.grid.customEditors[n];
56006         }
56007         if(val instanceof Date){
56008             return this.editors['date'];
56009         }else if(typeof val == 'number'){
56010             return this.editors['number'];
56011         }else if(typeof val == 'boolean'){
56012             return this.editors['boolean'];
56013         }else{
56014             return this.editors['string'];
56015         }
56016     }
56017 });
56018
56019 /**
56020  * @class Roo.grid.PropertyGrid
56021  * @extends Roo.grid.EditorGrid
56022  * This class represents the  interface of a component based property grid control.
56023  * <br><br>Usage:<pre><code>
56024  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56025       
56026  });
56027  // set any options
56028  grid.render();
56029  * </code></pre>
56030   
56031  * @constructor
56032  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56033  * The container MUST have some type of size defined for the grid to fill. The container will be
56034  * automatically set to position relative if it isn't already.
56035  * @param {Object} config A config object that sets properties on this grid.
56036  */
56037 Roo.grid.PropertyGrid = function(container, config){
56038     config = config || {};
56039     var store = new Roo.grid.PropertyStore(this);
56040     this.store = store;
56041     var cm = new Roo.grid.PropertyColumnModel(this, store);
56042     store.store.sort('name', 'ASC');
56043     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56044         ds: store.store,
56045         cm: cm,
56046         enableColLock:false,
56047         enableColumnMove:false,
56048         stripeRows:false,
56049         trackMouseOver: false,
56050         clicksToEdit:1
56051     }, config));
56052     this.getGridEl().addClass('x-props-grid');
56053     this.lastEditRow = null;
56054     this.on('columnresize', this.onColumnResize, this);
56055     this.addEvents({
56056          /**
56057              * @event beforepropertychange
56058              * Fires before a property changes (return false to stop?)
56059              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56060              * @param {String} id Record Id
56061              * @param {String} newval New Value
56062          * @param {String} oldval Old Value
56063              */
56064         "beforepropertychange": true,
56065         /**
56066              * @event propertychange
56067              * Fires after a property changes
56068              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56069              * @param {String} id Record Id
56070              * @param {String} newval New Value
56071          * @param {String} oldval Old Value
56072              */
56073         "propertychange": true
56074     });
56075     this.customEditors = this.customEditors || {};
56076 };
56077 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56078     
56079      /**
56080      * @cfg {Object} customEditors map of colnames=> custom editors.
56081      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56082      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56083      * false disables editing of the field.
56084          */
56085     
56086       /**
56087      * @cfg {Object} propertyNames map of property Names to their displayed value
56088          */
56089     
56090     render : function(){
56091         Roo.grid.PropertyGrid.superclass.render.call(this);
56092         this.autoSize.defer(100, this);
56093     },
56094
56095     autoSize : function(){
56096         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56097         if(this.view){
56098             this.view.fitColumns();
56099         }
56100     },
56101
56102     onColumnResize : function(){
56103         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56104         this.autoSize();
56105     },
56106     /**
56107      * Sets the data for the Grid
56108      * accepts a Key => Value object of all the elements avaiable.
56109      * @param {Object} data  to appear in grid.
56110      */
56111     setSource : function(source){
56112         this.store.setSource(source);
56113         //this.autoSize();
56114     },
56115     /**
56116      * Gets all the data from the grid.
56117      * @return {Object} data  data stored in grid
56118      */
56119     getSource : function(){
56120         return this.store.getSource();
56121     }
56122 });/*
56123   
56124  * Licence LGPL
56125  
56126  */
56127  
56128 /**
56129  * @class Roo.grid.Calendar
56130  * @extends Roo.util.Grid
56131  * This class extends the Grid to provide a calendar widget
56132  * <br><br>Usage:<pre><code>
56133  var grid = new Roo.grid.Calendar("my-container-id", {
56134      ds: myDataStore,
56135      cm: myColModel,
56136      selModel: mySelectionModel,
56137      autoSizeColumns: true,
56138      monitorWindowResize: false,
56139      trackMouseOver: true
56140      eventstore : real data store..
56141  });
56142  // set any options
56143  grid.render();
56144   
56145   * @constructor
56146  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56147  * The container MUST have some type of size defined for the grid to fill. The container will be
56148  * automatically set to position relative if it isn't already.
56149  * @param {Object} config A config object that sets properties on this grid.
56150  */
56151 Roo.grid.Calendar = function(container, config){
56152         // initialize the container
56153         this.container = Roo.get(container);
56154         this.container.update("");
56155         this.container.setStyle("overflow", "hidden");
56156     this.container.addClass('x-grid-container');
56157
56158     this.id = this.container.id;
56159
56160     Roo.apply(this, config);
56161     // check and correct shorthanded configs
56162     
56163     var rows = [];
56164     var d =1;
56165     for (var r = 0;r < 6;r++) {
56166         
56167         rows[r]=[];
56168         for (var c =0;c < 7;c++) {
56169             rows[r][c]= '';
56170         }
56171     }
56172     if (this.eventStore) {
56173         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56174         this.eventStore.on('load',this.onLoad, this);
56175         this.eventStore.on('beforeload',this.clearEvents, this);
56176          
56177     }
56178     
56179     this.dataSource = new Roo.data.Store({
56180             proxy: new Roo.data.MemoryProxy(rows),
56181             reader: new Roo.data.ArrayReader({}, [
56182                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56183     });
56184
56185     this.dataSource.load();
56186     this.ds = this.dataSource;
56187     this.ds.xmodule = this.xmodule || false;
56188     
56189     
56190     var cellRender = function(v,x,r)
56191     {
56192         return String.format(
56193             '<div class="fc-day  fc-widget-content"><div>' +
56194                 '<div class="fc-event-container"></div>' +
56195                 '<div class="fc-day-number">{0}</div>'+
56196                 
56197                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56198             '</div></div>', v);
56199     
56200     }
56201     
56202     
56203     this.colModel = new Roo.grid.ColumnModel( [
56204         {
56205             xtype: 'ColumnModel',
56206             xns: Roo.grid,
56207             dataIndex : 'weekday0',
56208             header : 'Sunday',
56209             renderer : cellRender
56210         },
56211         {
56212             xtype: 'ColumnModel',
56213             xns: Roo.grid,
56214             dataIndex : 'weekday1',
56215             header : 'Monday',
56216             renderer : cellRender
56217         },
56218         {
56219             xtype: 'ColumnModel',
56220             xns: Roo.grid,
56221             dataIndex : 'weekday2',
56222             header : 'Tuesday',
56223             renderer : cellRender
56224         },
56225         {
56226             xtype: 'ColumnModel',
56227             xns: Roo.grid,
56228             dataIndex : 'weekday3',
56229             header : 'Wednesday',
56230             renderer : cellRender
56231         },
56232         {
56233             xtype: 'ColumnModel',
56234             xns: Roo.grid,
56235             dataIndex : 'weekday4',
56236             header : 'Thursday',
56237             renderer : cellRender
56238         },
56239         {
56240             xtype: 'ColumnModel',
56241             xns: Roo.grid,
56242             dataIndex : 'weekday5',
56243             header : 'Friday',
56244             renderer : cellRender
56245         },
56246         {
56247             xtype: 'ColumnModel',
56248             xns: Roo.grid,
56249             dataIndex : 'weekday6',
56250             header : 'Saturday',
56251             renderer : cellRender
56252         }
56253     ]);
56254     this.cm = this.colModel;
56255     this.cm.xmodule = this.xmodule || false;
56256  
56257         
56258           
56259     //this.selModel = new Roo.grid.CellSelectionModel();
56260     //this.sm = this.selModel;
56261     //this.selModel.init(this);
56262     
56263     
56264     if(this.width){
56265         this.container.setWidth(this.width);
56266     }
56267
56268     if(this.height){
56269         this.container.setHeight(this.height);
56270     }
56271     /** @private */
56272         this.addEvents({
56273         // raw events
56274         /**
56275          * @event click
56276          * The raw click event for the entire grid.
56277          * @param {Roo.EventObject} e
56278          */
56279         "click" : true,
56280         /**
56281          * @event dblclick
56282          * The raw dblclick event for the entire grid.
56283          * @param {Roo.EventObject} e
56284          */
56285         "dblclick" : true,
56286         /**
56287          * @event contextmenu
56288          * The raw contextmenu event for the entire grid.
56289          * @param {Roo.EventObject} e
56290          */
56291         "contextmenu" : true,
56292         /**
56293          * @event mousedown
56294          * The raw mousedown event for the entire grid.
56295          * @param {Roo.EventObject} e
56296          */
56297         "mousedown" : true,
56298         /**
56299          * @event mouseup
56300          * The raw mouseup event for the entire grid.
56301          * @param {Roo.EventObject} e
56302          */
56303         "mouseup" : true,
56304         /**
56305          * @event mouseover
56306          * The raw mouseover event for the entire grid.
56307          * @param {Roo.EventObject} e
56308          */
56309         "mouseover" : true,
56310         /**
56311          * @event mouseout
56312          * The raw mouseout event for the entire grid.
56313          * @param {Roo.EventObject} e
56314          */
56315         "mouseout" : true,
56316         /**
56317          * @event keypress
56318          * The raw keypress event for the entire grid.
56319          * @param {Roo.EventObject} e
56320          */
56321         "keypress" : true,
56322         /**
56323          * @event keydown
56324          * The raw keydown event for the entire grid.
56325          * @param {Roo.EventObject} e
56326          */
56327         "keydown" : true,
56328
56329         // custom events
56330
56331         /**
56332          * @event cellclick
56333          * Fires when a cell is clicked
56334          * @param {Grid} this
56335          * @param {Number} rowIndex
56336          * @param {Number} columnIndex
56337          * @param {Roo.EventObject} e
56338          */
56339         "cellclick" : true,
56340         /**
56341          * @event celldblclick
56342          * Fires when a cell is double clicked
56343          * @param {Grid} this
56344          * @param {Number} rowIndex
56345          * @param {Number} columnIndex
56346          * @param {Roo.EventObject} e
56347          */
56348         "celldblclick" : true,
56349         /**
56350          * @event rowclick
56351          * Fires when a row is clicked
56352          * @param {Grid} this
56353          * @param {Number} rowIndex
56354          * @param {Roo.EventObject} e
56355          */
56356         "rowclick" : true,
56357         /**
56358          * @event rowdblclick
56359          * Fires when a row is double clicked
56360          * @param {Grid} this
56361          * @param {Number} rowIndex
56362          * @param {Roo.EventObject} e
56363          */
56364         "rowdblclick" : true,
56365         /**
56366          * @event headerclick
56367          * Fires when a header is clicked
56368          * @param {Grid} this
56369          * @param {Number} columnIndex
56370          * @param {Roo.EventObject} e
56371          */
56372         "headerclick" : true,
56373         /**
56374          * @event headerdblclick
56375          * Fires when a header cell is double clicked
56376          * @param {Grid} this
56377          * @param {Number} columnIndex
56378          * @param {Roo.EventObject} e
56379          */
56380         "headerdblclick" : true,
56381         /**
56382          * @event rowcontextmenu
56383          * Fires when a row is right clicked
56384          * @param {Grid} this
56385          * @param {Number} rowIndex
56386          * @param {Roo.EventObject} e
56387          */
56388         "rowcontextmenu" : true,
56389         /**
56390          * @event cellcontextmenu
56391          * Fires when a cell is right clicked
56392          * @param {Grid} this
56393          * @param {Number} rowIndex
56394          * @param {Number} cellIndex
56395          * @param {Roo.EventObject} e
56396          */
56397          "cellcontextmenu" : true,
56398         /**
56399          * @event headercontextmenu
56400          * Fires when a header is right clicked
56401          * @param {Grid} this
56402          * @param {Number} columnIndex
56403          * @param {Roo.EventObject} e
56404          */
56405         "headercontextmenu" : true,
56406         /**
56407          * @event bodyscroll
56408          * Fires when the body element is scrolled
56409          * @param {Number} scrollLeft
56410          * @param {Number} scrollTop
56411          */
56412         "bodyscroll" : true,
56413         /**
56414          * @event columnresize
56415          * Fires when the user resizes a column
56416          * @param {Number} columnIndex
56417          * @param {Number} newSize
56418          */
56419         "columnresize" : true,
56420         /**
56421          * @event columnmove
56422          * Fires when the user moves a column
56423          * @param {Number} oldIndex
56424          * @param {Number} newIndex
56425          */
56426         "columnmove" : true,
56427         /**
56428          * @event startdrag
56429          * Fires when row(s) start being dragged
56430          * @param {Grid} this
56431          * @param {Roo.GridDD} dd The drag drop object
56432          * @param {event} e The raw browser event
56433          */
56434         "startdrag" : true,
56435         /**
56436          * @event enddrag
56437          * Fires when a drag operation is complete
56438          * @param {Grid} this
56439          * @param {Roo.GridDD} dd The drag drop object
56440          * @param {event} e The raw browser event
56441          */
56442         "enddrag" : true,
56443         /**
56444          * @event dragdrop
56445          * Fires when dragged row(s) are dropped on a valid DD target
56446          * @param {Grid} this
56447          * @param {Roo.GridDD} dd The drag drop object
56448          * @param {String} targetId The target drag drop object
56449          * @param {event} e The raw browser event
56450          */
56451         "dragdrop" : true,
56452         /**
56453          * @event dragover
56454          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56455          * @param {Grid} this
56456          * @param {Roo.GridDD} dd The drag drop object
56457          * @param {String} targetId The target drag drop object
56458          * @param {event} e The raw browser event
56459          */
56460         "dragover" : true,
56461         /**
56462          * @event dragenter
56463          *  Fires when the dragged row(s) first cross another DD target while being dragged
56464          * @param {Grid} this
56465          * @param {Roo.GridDD} dd The drag drop object
56466          * @param {String} targetId The target drag drop object
56467          * @param {event} e The raw browser event
56468          */
56469         "dragenter" : true,
56470         /**
56471          * @event dragout
56472          * Fires when the dragged row(s) leave another DD target while being dragged
56473          * @param {Grid} this
56474          * @param {Roo.GridDD} dd The drag drop object
56475          * @param {String} targetId The target drag drop object
56476          * @param {event} e The raw browser event
56477          */
56478         "dragout" : true,
56479         /**
56480          * @event rowclass
56481          * Fires when a row is rendered, so you can change add a style to it.
56482          * @param {GridView} gridview   The grid view
56483          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56484          */
56485         'rowclass' : true,
56486
56487         /**
56488          * @event render
56489          * Fires when the grid is rendered
56490          * @param {Grid} grid
56491          */
56492         'render' : true,
56493             /**
56494              * @event select
56495              * Fires when a date is selected
56496              * @param {DatePicker} this
56497              * @param {Date} date The selected date
56498              */
56499         'select': true,
56500         /**
56501              * @event monthchange
56502              * Fires when the displayed month changes 
56503              * @param {DatePicker} this
56504              * @param {Date} date The selected month
56505              */
56506         'monthchange': true,
56507         /**
56508              * @event evententer
56509              * Fires when mouse over an event
56510              * @param {Calendar} this
56511              * @param {event} Event
56512              */
56513         'evententer': true,
56514         /**
56515              * @event eventleave
56516              * Fires when the mouse leaves an
56517              * @param {Calendar} this
56518              * @param {event}
56519              */
56520         'eventleave': true,
56521         /**
56522              * @event eventclick
56523              * Fires when the mouse click an
56524              * @param {Calendar} this
56525              * @param {event}
56526              */
56527         'eventclick': true,
56528         /**
56529              * @event eventrender
56530              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56531              * @param {Calendar} this
56532              * @param {data} data to be modified
56533              */
56534         'eventrender': true
56535         
56536     });
56537
56538     Roo.grid.Grid.superclass.constructor.call(this);
56539     this.on('render', function() {
56540         this.view.el.addClass('x-grid-cal'); 
56541         
56542         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56543
56544     },this);
56545     
56546     if (!Roo.grid.Calendar.style) {
56547         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56548             
56549             
56550             '.x-grid-cal .x-grid-col' :  {
56551                 height: 'auto !important',
56552                 'vertical-align': 'top'
56553             },
56554             '.x-grid-cal  .fc-event-hori' : {
56555                 height: '14px'
56556             }
56557              
56558             
56559         }, Roo.id());
56560     }
56561
56562     
56563     
56564 };
56565 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56566     /**
56567      * @cfg {Store} eventStore The store that loads events.
56568      */
56569     eventStore : 25,
56570
56571      
56572     activeDate : false,
56573     startDay : 0,
56574     autoWidth : true,
56575     monitorWindowResize : false,
56576
56577     
56578     resizeColumns : function() {
56579         var col = (this.view.el.getWidth() / 7) - 3;
56580         // loop through cols, and setWidth
56581         for(var i =0 ; i < 7 ; i++){
56582             this.cm.setColumnWidth(i, col);
56583         }
56584     },
56585      setDate :function(date) {
56586         
56587         Roo.log('setDate?');
56588         
56589         this.resizeColumns();
56590         var vd = this.activeDate;
56591         this.activeDate = date;
56592 //        if(vd && this.el){
56593 //            var t = date.getTime();
56594 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56595 //                Roo.log('using add remove');
56596 //                
56597 //                this.fireEvent('monthchange', this, date);
56598 //                
56599 //                this.cells.removeClass("fc-state-highlight");
56600 //                this.cells.each(function(c){
56601 //                   if(c.dateValue == t){
56602 //                       c.addClass("fc-state-highlight");
56603 //                       setTimeout(function(){
56604 //                            try{c.dom.firstChild.focus();}catch(e){}
56605 //                       }, 50);
56606 //                       return false;
56607 //                   }
56608 //                   return true;
56609 //                });
56610 //                return;
56611 //            }
56612 //        }
56613         
56614         var days = date.getDaysInMonth();
56615         
56616         var firstOfMonth = date.getFirstDateOfMonth();
56617         var startingPos = firstOfMonth.getDay()-this.startDay;
56618         
56619         if(startingPos < this.startDay){
56620             startingPos += 7;
56621         }
56622         
56623         var pm = date.add(Date.MONTH, -1);
56624         var prevStart = pm.getDaysInMonth()-startingPos;
56625 //        
56626         
56627         
56628         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56629         
56630         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
56631         //this.cells.addClassOnOver('fc-state-hover');
56632         
56633         var cells = this.cells.elements;
56634         var textEls = this.textNodes;
56635         
56636         //Roo.each(cells, function(cell){
56637         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
56638         //});
56639         
56640         days += startingPos;
56641
56642         // convert everything to numbers so it's fast
56643         var day = 86400000;
56644         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
56645         //Roo.log(d);
56646         //Roo.log(pm);
56647         //Roo.log(prevStart);
56648         
56649         var today = new Date().clearTime().getTime();
56650         var sel = date.clearTime().getTime();
56651         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
56652         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
56653         var ddMatch = this.disabledDatesRE;
56654         var ddText = this.disabledDatesText;
56655         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
56656         var ddaysText = this.disabledDaysText;
56657         var format = this.format;
56658         
56659         var setCellClass = function(cal, cell){
56660             
56661             //Roo.log('set Cell Class');
56662             cell.title = "";
56663             var t = d.getTime();
56664             
56665             //Roo.log(d);
56666             
56667             
56668             cell.dateValue = t;
56669             if(t == today){
56670                 cell.className += " fc-today";
56671                 cell.className += " fc-state-highlight";
56672                 cell.title = cal.todayText;
56673             }
56674             if(t == sel){
56675                 // disable highlight in other month..
56676                 cell.className += " fc-state-highlight";
56677                 
56678             }
56679             // disabling
56680             if(t < min) {
56681                 //cell.className = " fc-state-disabled";
56682                 cell.title = cal.minText;
56683                 return;
56684             }
56685             if(t > max) {
56686                 //cell.className = " fc-state-disabled";
56687                 cell.title = cal.maxText;
56688                 return;
56689             }
56690             if(ddays){
56691                 if(ddays.indexOf(d.getDay()) != -1){
56692                     // cell.title = ddaysText;
56693                    // cell.className = " fc-state-disabled";
56694                 }
56695             }
56696             if(ddMatch && format){
56697                 var fvalue = d.dateFormat(format);
56698                 if(ddMatch.test(fvalue)){
56699                     cell.title = ddText.replace("%0", fvalue);
56700                    cell.className = " fc-state-disabled";
56701                 }
56702             }
56703             
56704             if (!cell.initialClassName) {
56705                 cell.initialClassName = cell.dom.className;
56706             }
56707             
56708             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
56709         };
56710
56711         var i = 0;
56712         
56713         for(; i < startingPos; i++) {
56714             cells[i].dayName =  (++prevStart);
56715             Roo.log(textEls[i]);
56716             d.setDate(d.getDate()+1);
56717             
56718             //cells[i].className = "fc-past fc-other-month";
56719             setCellClass(this, cells[i]);
56720         }
56721         
56722         var intDay = 0;
56723         
56724         for(; i < days; i++){
56725             intDay = i - startingPos + 1;
56726             cells[i].dayName =  (intDay);
56727             d.setDate(d.getDate()+1);
56728             
56729             cells[i].className = ''; // "x-date-active";
56730             setCellClass(this, cells[i]);
56731         }
56732         var extraDays = 0;
56733         
56734         for(; i < 42; i++) {
56735             //textEls[i].innerHTML = (++extraDays);
56736             
56737             d.setDate(d.getDate()+1);
56738             cells[i].dayName = (++extraDays);
56739             cells[i].className = "fc-future fc-other-month";
56740             setCellClass(this, cells[i]);
56741         }
56742         
56743         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
56744         
56745         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
56746         
56747         // this will cause all the cells to mis
56748         var rows= [];
56749         var i =0;
56750         for (var r = 0;r < 6;r++) {
56751             for (var c =0;c < 7;c++) {
56752                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
56753             }    
56754         }
56755         
56756         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56757         for(i=0;i<cells.length;i++) {
56758             
56759             this.cells.elements[i].dayName = cells[i].dayName ;
56760             this.cells.elements[i].className = cells[i].className;
56761             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
56762             this.cells.elements[i].title = cells[i].title ;
56763             this.cells.elements[i].dateValue = cells[i].dateValue ;
56764         }
56765         
56766         
56767         
56768         
56769         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
56770         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
56771         
56772         ////if(totalRows != 6){
56773             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
56774            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
56775        // }
56776         
56777         this.fireEvent('monthchange', this, date);
56778         
56779         
56780     },
56781  /**
56782      * Returns the grid's SelectionModel.
56783      * @return {SelectionModel}
56784      */
56785     getSelectionModel : function(){
56786         if(!this.selModel){
56787             this.selModel = new Roo.grid.CellSelectionModel();
56788         }
56789         return this.selModel;
56790     },
56791
56792     load: function() {
56793         this.eventStore.load()
56794         
56795         
56796         
56797     },
56798     
56799     findCell : function(dt) {
56800         dt = dt.clearTime().getTime();
56801         var ret = false;
56802         this.cells.each(function(c){
56803             //Roo.log("check " +c.dateValue + '?=' + dt);
56804             if(c.dateValue == dt){
56805                 ret = c;
56806                 return false;
56807             }
56808             return true;
56809         });
56810         
56811         return ret;
56812     },
56813     
56814     findCells : function(rec) {
56815         var s = rec.data.start_dt.clone().clearTime().getTime();
56816        // Roo.log(s);
56817         var e= rec.data.end_dt.clone().clearTime().getTime();
56818        // Roo.log(e);
56819         var ret = [];
56820         this.cells.each(function(c){
56821              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
56822             
56823             if(c.dateValue > e){
56824                 return ;
56825             }
56826             if(c.dateValue < s){
56827                 return ;
56828             }
56829             ret.push(c);
56830         });
56831         
56832         return ret;    
56833     },
56834     
56835     findBestRow: function(cells)
56836     {
56837         var ret = 0;
56838         
56839         for (var i =0 ; i < cells.length;i++) {
56840             ret  = Math.max(cells[i].rows || 0,ret);
56841         }
56842         return ret;
56843         
56844     },
56845     
56846     
56847     addItem : function(rec)
56848     {
56849         // look for vertical location slot in
56850         var cells = this.findCells(rec);
56851         
56852         rec.row = this.findBestRow(cells);
56853         
56854         // work out the location.
56855         
56856         var crow = false;
56857         var rows = [];
56858         for(var i =0; i < cells.length; i++) {
56859             if (!crow) {
56860                 crow = {
56861                     start : cells[i],
56862                     end :  cells[i]
56863                 };
56864                 continue;
56865             }
56866             if (crow.start.getY() == cells[i].getY()) {
56867                 // on same row.
56868                 crow.end = cells[i];
56869                 continue;
56870             }
56871             // different row.
56872             rows.push(crow);
56873             crow = {
56874                 start: cells[i],
56875                 end : cells[i]
56876             };
56877             
56878         }
56879         
56880         rows.push(crow);
56881         rec.els = [];
56882         rec.rows = rows;
56883         rec.cells = cells;
56884         for (var i = 0; i < cells.length;i++) {
56885             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
56886             
56887         }
56888         
56889         
56890     },
56891     
56892     clearEvents: function() {
56893         
56894         if (!this.eventStore.getCount()) {
56895             return;
56896         }
56897         // reset number of rows in cells.
56898         Roo.each(this.cells.elements, function(c){
56899             c.rows = 0;
56900         });
56901         
56902         this.eventStore.each(function(e) {
56903             this.clearEvent(e);
56904         },this);
56905         
56906     },
56907     
56908     clearEvent : function(ev)
56909     {
56910         if (ev.els) {
56911             Roo.each(ev.els, function(el) {
56912                 el.un('mouseenter' ,this.onEventEnter, this);
56913                 el.un('mouseleave' ,this.onEventLeave, this);
56914                 el.remove();
56915             },this);
56916             ev.els = [];
56917         }
56918     },
56919     
56920     
56921     renderEvent : function(ev,ctr) {
56922         if (!ctr) {
56923              ctr = this.view.el.select('.fc-event-container',true).first();
56924         }
56925         
56926          
56927         this.clearEvent(ev);
56928             //code
56929        
56930         
56931         
56932         ev.els = [];
56933         var cells = ev.cells;
56934         var rows = ev.rows;
56935         this.fireEvent('eventrender', this, ev);
56936         
56937         for(var i =0; i < rows.length; i++) {
56938             
56939             cls = '';
56940             if (i == 0) {
56941                 cls += ' fc-event-start';
56942             }
56943             if ((i+1) == rows.length) {
56944                 cls += ' fc-event-end';
56945             }
56946             
56947             //Roo.log(ev.data);
56948             // how many rows should it span..
56949             var cg = this.eventTmpl.append(ctr,Roo.apply({
56950                 fccls : cls
56951                 
56952             }, ev.data) , true);
56953             
56954             
56955             cg.on('mouseenter' ,this.onEventEnter, this, ev);
56956             cg.on('mouseleave' ,this.onEventLeave, this, ev);
56957             cg.on('click', this.onEventClick, this, ev);
56958             
56959             ev.els.push(cg);
56960             
56961             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
56962             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
56963             //Roo.log(cg);
56964              
56965             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
56966             cg.setWidth(ebox.right - sbox.x -2);
56967         }
56968     },
56969     
56970     renderEvents: function()
56971     {   
56972         // first make sure there is enough space..
56973         
56974         if (!this.eventTmpl) {
56975             this.eventTmpl = new Roo.Template(
56976                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
56977                     '<div class="fc-event-inner">' +
56978                         '<span class="fc-event-time">{time}</span>' +
56979                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
56980                     '</div>' +
56981                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
56982                 '</div>'
56983             );
56984                 
56985         }
56986                
56987         
56988         
56989         this.cells.each(function(c) {
56990             //Roo.log(c.select('.fc-day-content div',true).first());
56991             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
56992         });
56993         
56994         var ctr = this.view.el.select('.fc-event-container',true).first();
56995         
56996         var cls;
56997         this.eventStore.each(function(ev){
56998             
56999             this.renderEvent(ev);
57000              
57001              
57002         }, this);
57003         this.view.layout();
57004         
57005     },
57006     
57007     onEventEnter: function (e, el,event,d) {
57008         this.fireEvent('evententer', this, el, event);
57009     },
57010     
57011     onEventLeave: function (e, el,event,d) {
57012         this.fireEvent('eventleave', this, el, event);
57013     },
57014     
57015     onEventClick: function (e, el,event,d) {
57016         this.fireEvent('eventclick', this, el, event);
57017     },
57018     
57019     onMonthChange: function () {
57020         this.store.load();
57021     },
57022     
57023     onLoad: function () {
57024         
57025         //Roo.log('calendar onload');
57026 //         
57027         if(this.eventStore.getCount() > 0){
57028             
57029            
57030             
57031             this.eventStore.each(function(d){
57032                 
57033                 
57034                 // FIXME..
57035                 var add =   d.data;
57036                 if (typeof(add.end_dt) == 'undefined')  {
57037                     Roo.log("Missing End time in calendar data: ");
57038                     Roo.log(d);
57039                     return;
57040                 }
57041                 if (typeof(add.start_dt) == 'undefined')  {
57042                     Roo.log("Missing Start time in calendar data: ");
57043                     Roo.log(d);
57044                     return;
57045                 }
57046                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57047                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57048                 add.id = add.id || d.id;
57049                 add.title = add.title || '??';
57050                 
57051                 this.addItem(d);
57052                 
57053              
57054             },this);
57055         }
57056         
57057         this.renderEvents();
57058     }
57059     
57060
57061 });
57062 /*
57063  grid : {
57064                 xtype: 'Grid',
57065                 xns: Roo.grid,
57066                 listeners : {
57067                     render : function ()
57068                     {
57069                         _this.grid = this;
57070                         
57071                         if (!this.view.el.hasClass('course-timesheet')) {
57072                             this.view.el.addClass('course-timesheet');
57073                         }
57074                         if (this.tsStyle) {
57075                             this.ds.load({});
57076                             return; 
57077                         }
57078                         Roo.log('width');
57079                         Roo.log(_this.grid.view.el.getWidth());
57080                         
57081                         
57082                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57083                             '.course-timesheet .x-grid-row' : {
57084                                 height: '80px'
57085                             },
57086                             '.x-grid-row td' : {
57087                                 'vertical-align' : 0
57088                             },
57089                             '.course-edit-link' : {
57090                                 'color' : 'blue',
57091                                 'text-overflow' : 'ellipsis',
57092                                 'overflow' : 'hidden',
57093                                 'white-space' : 'nowrap',
57094                                 'cursor' : 'pointer'
57095                             },
57096                             '.sub-link' : {
57097                                 'color' : 'green'
57098                             },
57099                             '.de-act-sup-link' : {
57100                                 'color' : 'purple',
57101                                 'text-decoration' : 'line-through'
57102                             },
57103                             '.de-act-link' : {
57104                                 'color' : 'red',
57105                                 'text-decoration' : 'line-through'
57106                             },
57107                             '.course-timesheet .course-highlight' : {
57108                                 'border-top-style': 'dashed !important',
57109                                 'border-bottom-bottom': 'dashed !important'
57110                             },
57111                             '.course-timesheet .course-item' : {
57112                                 'font-family'   : 'tahoma, arial, helvetica',
57113                                 'font-size'     : '11px',
57114                                 'overflow'      : 'hidden',
57115                                 'padding-left'  : '10px',
57116                                 'padding-right' : '10px',
57117                                 'padding-top' : '10px' 
57118                             }
57119                             
57120                         }, Roo.id());
57121                                 this.ds.load({});
57122                     }
57123                 },
57124                 autoWidth : true,
57125                 monitorWindowResize : false,
57126                 cellrenderer : function(v,x,r)
57127                 {
57128                     return v;
57129                 },
57130                 sm : {
57131                     xtype: 'CellSelectionModel',
57132                     xns: Roo.grid
57133                 },
57134                 dataSource : {
57135                     xtype: 'Store',
57136                     xns: Roo.data,
57137                     listeners : {
57138                         beforeload : function (_self, options)
57139                         {
57140                             options.params = options.params || {};
57141                             options.params._month = _this.monthField.getValue();
57142                             options.params.limit = 9999;
57143                             options.params['sort'] = 'when_dt';    
57144                             options.params['dir'] = 'ASC';    
57145                             this.proxy.loadResponse = this.loadResponse;
57146                             Roo.log("load?");
57147                             //this.addColumns();
57148                         },
57149                         load : function (_self, records, options)
57150                         {
57151                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57152                                 // if you click on the translation.. you can edit it...
57153                                 var el = Roo.get(this);
57154                                 var id = el.dom.getAttribute('data-id');
57155                                 var d = el.dom.getAttribute('data-date');
57156                                 var t = el.dom.getAttribute('data-time');
57157                                 //var id = this.child('span').dom.textContent;
57158                                 
57159                                 //Roo.log(this);
57160                                 Pman.Dialog.CourseCalendar.show({
57161                                     id : id,
57162                                     when_d : d,
57163                                     when_t : t,
57164                                     productitem_active : id ? 1 : 0
57165                                 }, function() {
57166                                     _this.grid.ds.load({});
57167                                 });
57168                            
57169                            });
57170                            
57171                            _this.panel.fireEvent('resize', [ '', '' ]);
57172                         }
57173                     },
57174                     loadResponse : function(o, success, response){
57175                             // this is overridden on before load..
57176                             
57177                             Roo.log("our code?");       
57178                             //Roo.log(success);
57179                             //Roo.log(response)
57180                             delete this.activeRequest;
57181                             if(!success){
57182                                 this.fireEvent("loadexception", this, o, response);
57183                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57184                                 return;
57185                             }
57186                             var result;
57187                             try {
57188                                 result = o.reader.read(response);
57189                             }catch(e){
57190                                 Roo.log("load exception?");
57191                                 this.fireEvent("loadexception", this, o, response, e);
57192                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57193                                 return;
57194                             }
57195                             Roo.log("ready...");        
57196                             // loop through result.records;
57197                             // and set this.tdate[date] = [] << array of records..
57198                             _this.tdata  = {};
57199                             Roo.each(result.records, function(r){
57200                                 //Roo.log(r.data);
57201                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57202                                     _this.tdata[r.data.when_dt.format('j')] = [];
57203                                 }
57204                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57205                             });
57206                             
57207                             //Roo.log(_this.tdata);
57208                             
57209                             result.records = [];
57210                             result.totalRecords = 6;
57211                     
57212                             // let's generate some duumy records for the rows.
57213                             //var st = _this.dateField.getValue();
57214                             
57215                             // work out monday..
57216                             //st = st.add(Date.DAY, -1 * st.format('w'));
57217                             
57218                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57219                             
57220                             var firstOfMonth = date.getFirstDayOfMonth();
57221                             var days = date.getDaysInMonth();
57222                             var d = 1;
57223                             var firstAdded = false;
57224                             for (var i = 0; i < result.totalRecords ; i++) {
57225                                 //var d= st.add(Date.DAY, i);
57226                                 var row = {};
57227                                 var added = 0;
57228                                 for(var w = 0 ; w < 7 ; w++){
57229                                     if(!firstAdded && firstOfMonth != w){
57230                                         continue;
57231                                     }
57232                                     if(d > days){
57233                                         continue;
57234                                     }
57235                                     firstAdded = true;
57236                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57237                                     row['weekday'+w] = String.format(
57238                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57239                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57240                                                     d,
57241                                                     date.format('Y-m-')+dd
57242                                                 );
57243                                     added++;
57244                                     if(typeof(_this.tdata[d]) != 'undefined'){
57245                                         Roo.each(_this.tdata[d], function(r){
57246                                             var is_sub = '';
57247                                             var deactive = '';
57248                                             var id = r.id;
57249                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57250                                             if(r.parent_id*1>0){
57251                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57252                                                 id = r.parent_id;
57253                                             }
57254                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57255                                                 deactive = 'de-act-link';
57256                                             }
57257                                             
57258                                             row['weekday'+w] += String.format(
57259                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57260                                                     id, //0
57261                                                     r.product_id_name, //1
57262                                                     r.when_dt.format('h:ia'), //2
57263                                                     is_sub, //3
57264                                                     deactive, //4
57265                                                     desc // 5
57266                                             );
57267                                         });
57268                                     }
57269                                     d++;
57270                                 }
57271                                 
57272                                 // only do this if something added..
57273                                 if(added > 0){ 
57274                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57275                                 }
57276                                 
57277                                 
57278                                 // push it twice. (second one with an hour..
57279                                 
57280                             }
57281                             //Roo.log(result);
57282                             this.fireEvent("load", this, o, o.request.arg);
57283                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57284                         },
57285                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57286                     proxy : {
57287                         xtype: 'HttpProxy',
57288                         xns: Roo.data,
57289                         method : 'GET',
57290                         url : baseURL + '/Roo/Shop_course.php'
57291                     },
57292                     reader : {
57293                         xtype: 'JsonReader',
57294                         xns: Roo.data,
57295                         id : 'id',
57296                         fields : [
57297                             {
57298                                 'name': 'id',
57299                                 'type': 'int'
57300                             },
57301                             {
57302                                 'name': 'when_dt',
57303                                 'type': 'string'
57304                             },
57305                             {
57306                                 'name': 'end_dt',
57307                                 'type': 'string'
57308                             },
57309                             {
57310                                 'name': 'parent_id',
57311                                 'type': 'int'
57312                             },
57313                             {
57314                                 'name': 'product_id',
57315                                 'type': 'int'
57316                             },
57317                             {
57318                                 'name': 'productitem_id',
57319                                 'type': 'int'
57320                             },
57321                             {
57322                                 'name': 'guid',
57323                                 'type': 'int'
57324                             }
57325                         ]
57326                     }
57327                 },
57328                 toolbar : {
57329                     xtype: 'Toolbar',
57330                     xns: Roo,
57331                     items : [
57332                         {
57333                             xtype: 'Button',
57334                             xns: Roo.Toolbar,
57335                             listeners : {
57336                                 click : function (_self, e)
57337                                 {
57338                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57339                                     sd.setMonth(sd.getMonth()-1);
57340                                     _this.monthField.setValue(sd.format('Y-m-d'));
57341                                     _this.grid.ds.load({});
57342                                 }
57343                             },
57344                             text : "Back"
57345                         },
57346                         {
57347                             xtype: 'Separator',
57348                             xns: Roo.Toolbar
57349                         },
57350                         {
57351                             xtype: 'MonthField',
57352                             xns: Roo.form,
57353                             listeners : {
57354                                 render : function (_self)
57355                                 {
57356                                     _this.monthField = _self;
57357                                    // _this.monthField.set  today
57358                                 },
57359                                 select : function (combo, date)
57360                                 {
57361                                     _this.grid.ds.load({});
57362                                 }
57363                             },
57364                             value : (function() { return new Date(); })()
57365                         },
57366                         {
57367                             xtype: 'Separator',
57368                             xns: Roo.Toolbar
57369                         },
57370                         {
57371                             xtype: 'TextItem',
57372                             xns: Roo.Toolbar,
57373                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57374                         },
57375                         {
57376                             xtype: 'Fill',
57377                             xns: Roo.Toolbar
57378                         },
57379                         {
57380                             xtype: 'Button',
57381                             xns: Roo.Toolbar,
57382                             listeners : {
57383                                 click : function (_self, e)
57384                                 {
57385                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57386                                     sd.setMonth(sd.getMonth()+1);
57387                                     _this.monthField.setValue(sd.format('Y-m-d'));
57388                                     _this.grid.ds.load({});
57389                                 }
57390                             },
57391                             text : "Next"
57392                         }
57393                     ]
57394                 },
57395                  
57396             }
57397         };
57398         
57399         *//*
57400  * Based on:
57401  * Ext JS Library 1.1.1
57402  * Copyright(c) 2006-2007, Ext JS, LLC.
57403  *
57404  * Originally Released Under LGPL - original licence link has changed is not relivant.
57405  *
57406  * Fork - LGPL
57407  * <script type="text/javascript">
57408  */
57409  
57410 /**
57411  * @class Roo.LoadMask
57412  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57413  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57414  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57415  * element's UpdateManager load indicator and will be destroyed after the initial load.
57416  * @constructor
57417  * Create a new LoadMask
57418  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57419  * @param {Object} config The config object
57420  */
57421 Roo.LoadMask = function(el, config){
57422     this.el = Roo.get(el);
57423     Roo.apply(this, config);
57424     if(this.store){
57425         this.store.on('beforeload', this.onBeforeLoad, this);
57426         this.store.on('load', this.onLoad, this);
57427         this.store.on('loadexception', this.onLoadException, this);
57428         this.removeMask = false;
57429     }else{
57430         var um = this.el.getUpdateManager();
57431         um.showLoadIndicator = false; // disable the default indicator
57432         um.on('beforeupdate', this.onBeforeLoad, this);
57433         um.on('update', this.onLoad, this);
57434         um.on('failure', this.onLoad, this);
57435         this.removeMask = true;
57436     }
57437 };
57438
57439 Roo.LoadMask.prototype = {
57440     /**
57441      * @cfg {Boolean} removeMask
57442      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57443      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57444      */
57445     /**
57446      * @cfg {String} msg
57447      * The text to display in a centered loading message box (defaults to 'Loading...')
57448      */
57449     msg : 'Loading...',
57450     /**
57451      * @cfg {String} msgCls
57452      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57453      */
57454     msgCls : 'x-mask-loading',
57455
57456     /**
57457      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57458      * @type Boolean
57459      */
57460     disabled: false,
57461
57462     /**
57463      * Disables the mask to prevent it from being displayed
57464      */
57465     disable : function(){
57466        this.disabled = true;
57467     },
57468
57469     /**
57470      * Enables the mask so that it can be displayed
57471      */
57472     enable : function(){
57473         this.disabled = false;
57474     },
57475     
57476     onLoadException : function()
57477     {
57478         Roo.log(arguments);
57479         
57480         if (typeof(arguments[3]) != 'undefined') {
57481             Roo.MessageBox.alert("Error loading",arguments[3]);
57482         } 
57483         /*
57484         try {
57485             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57486                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57487             }   
57488         } catch(e) {
57489             
57490         }
57491         */
57492     
57493         
57494         
57495         this.el.unmask(this.removeMask);
57496     },
57497     // private
57498     onLoad : function()
57499     {
57500         this.el.unmask(this.removeMask);
57501     },
57502
57503     // private
57504     onBeforeLoad : function(){
57505         if(!this.disabled){
57506             this.el.mask(this.msg, this.msgCls);
57507         }
57508     },
57509
57510     // private
57511     destroy : function(){
57512         if(this.store){
57513             this.store.un('beforeload', this.onBeforeLoad, this);
57514             this.store.un('load', this.onLoad, this);
57515             this.store.un('loadexception', this.onLoadException, this);
57516         }else{
57517             var um = this.el.getUpdateManager();
57518             um.un('beforeupdate', this.onBeforeLoad, this);
57519             um.un('update', this.onLoad, this);
57520             um.un('failure', this.onLoad, this);
57521         }
57522     }
57523 };/*
57524  * Based on:
57525  * Ext JS Library 1.1.1
57526  * Copyright(c) 2006-2007, Ext JS, LLC.
57527  *
57528  * Originally Released Under LGPL - original licence link has changed is not relivant.
57529  *
57530  * Fork - LGPL
57531  * <script type="text/javascript">
57532  */
57533
57534
57535 /**
57536  * @class Roo.XTemplate
57537  * @extends Roo.Template
57538  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57539 <pre><code>
57540 var t = new Roo.XTemplate(
57541         '&lt;select name="{name}"&gt;',
57542                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57543         '&lt;/select&gt;'
57544 );
57545  
57546 // then append, applying the master template values
57547  </code></pre>
57548  *
57549  * Supported features:
57550  *
57551  *  Tags:
57552
57553 <pre><code>
57554       {a_variable} - output encoded.
57555       {a_variable.format:("Y-m-d")} - call a method on the variable
57556       {a_variable:raw} - unencoded output
57557       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57558       {a_variable:this.method_on_template(...)} - call a method on the template object.
57559  
57560 </code></pre>
57561  *  The tpl tag:
57562 <pre><code>
57563         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57564         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57565         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57566         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57567   
57568         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57569         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57570 </code></pre>
57571  *      
57572  */
57573 Roo.XTemplate = function()
57574 {
57575     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57576     if (this.html) {
57577         this.compile();
57578     }
57579 };
57580
57581
57582 Roo.extend(Roo.XTemplate, Roo.Template, {
57583
57584     /**
57585      * The various sub templates
57586      */
57587     tpls : false,
57588     /**
57589      *
57590      * basic tag replacing syntax
57591      * WORD:WORD()
57592      *
57593      * // you can fake an object call by doing this
57594      *  x.t:(test,tesT) 
57595      * 
57596      */
57597     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57598
57599     /**
57600      * compile the template
57601      *
57602      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57603      *
57604      */
57605     compile: function()
57606     {
57607         var s = this.html;
57608      
57609         s = ['<tpl>', s, '</tpl>'].join('');
57610     
57611         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57612             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57613             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57614             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57615             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57616             m,
57617             id     = 0,
57618             tpls   = [];
57619     
57620         while(true == !!(m = s.match(re))){
57621             var forMatch   = m[0].match(nameRe),
57622                 ifMatch   = m[0].match(ifRe),
57623                 execMatch   = m[0].match(execRe),
57624                 namedMatch   = m[0].match(namedRe),
57625                 
57626                 exp  = null, 
57627                 fn   = null,
57628                 exec = null,
57629                 name = forMatch && forMatch[1] ? forMatch[1] : '';
57630                 
57631             if (ifMatch) {
57632                 // if - puts fn into test..
57633                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
57634                 if(exp){
57635                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
57636                 }
57637             }
57638             
57639             if (execMatch) {
57640                 // exec - calls a function... returns empty if true is  returned.
57641                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
57642                 if(exp){
57643                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
57644                 }
57645             }
57646             
57647             
57648             if (name) {
57649                 // for = 
57650                 switch(name){
57651                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
57652                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
57653                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
57654                 }
57655             }
57656             var uid = namedMatch ? namedMatch[1] : id;
57657             
57658             
57659             tpls.push({
57660                 id:     namedMatch ? namedMatch[1] : id,
57661                 target: name,
57662                 exec:   exec,
57663                 test:   fn,
57664                 body:   m[1] || ''
57665             });
57666             if (namedMatch) {
57667                 s = s.replace(m[0], '');
57668             } else { 
57669                 s = s.replace(m[0], '{xtpl'+ id + '}');
57670             }
57671             ++id;
57672         }
57673         this.tpls = [];
57674         for(var i = tpls.length-1; i >= 0; --i){
57675             this.compileTpl(tpls[i]);
57676             this.tpls[tpls[i].id] = tpls[i];
57677         }
57678         this.master = tpls[tpls.length-1];
57679         return this;
57680     },
57681     /**
57682      * same as applyTemplate, except it's done to one of the subTemplates
57683      * when using named templates, you can do:
57684      *
57685      * var str = pl.applySubTemplate('your-name', values);
57686      *
57687      * 
57688      * @param {Number} id of the template
57689      * @param {Object} values to apply to template
57690      * @param {Object} parent (normaly the instance of this object)
57691      */
57692     applySubTemplate : function(id, values, parent)
57693     {
57694         
57695         
57696         var t = this.tpls[id];
57697         
57698         
57699         try { 
57700             if(t.test && !t.test.call(this, values, parent)){
57701                 return '';
57702             }
57703         } catch(e) {
57704             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
57705             Roo.log(e.toString());
57706             Roo.log(t.test);
57707             return ''
57708         }
57709         try { 
57710             
57711             if(t.exec && t.exec.call(this, values, parent)){
57712                 return '';
57713             }
57714         } catch(e) {
57715             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
57716             Roo.log(e.toString());
57717             Roo.log(t.exec);
57718             return ''
57719         }
57720         try {
57721             var vs = t.target ? t.target.call(this, values, parent) : values;
57722             parent = t.target ? values : parent;
57723             if(t.target && vs instanceof Array){
57724                 var buf = [];
57725                 for(var i = 0, len = vs.length; i < len; i++){
57726                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
57727                 }
57728                 return buf.join('');
57729             }
57730             return t.compiled.call(this, vs, parent);
57731         } catch (e) {
57732             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
57733             Roo.log(e.toString());
57734             Roo.log(t.compiled);
57735             return '';
57736         }
57737     },
57738
57739     compileTpl : function(tpl)
57740     {
57741         var fm = Roo.util.Format;
57742         var useF = this.disableFormats !== true;
57743         var sep = Roo.isGecko ? "+" : ",";
57744         var undef = function(str) {
57745             Roo.log("Property not found :"  + str);
57746             return '';
57747         };
57748         
57749         var fn = function(m, name, format, args)
57750         {
57751             //Roo.log(arguments);
57752             args = args ? args.replace(/\\'/g,"'") : args;
57753             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
57754             if (typeof(format) == 'undefined') {
57755                 format= 'htmlEncode';
57756             }
57757             if (format == 'raw' ) {
57758                 format = false;
57759             }
57760             
57761             if(name.substr(0, 4) == 'xtpl'){
57762                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
57763             }
57764             
57765             // build an array of options to determine if value is undefined..
57766             
57767             // basically get 'xxxx.yyyy' then do
57768             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
57769             //    (function () { Roo.log("Property not found"); return ''; })() :
57770             //    ......
57771             
57772             var udef_ar = [];
57773             var lookfor = '';
57774             Roo.each(name.split('.'), function(st) {
57775                 lookfor += (lookfor.length ? '.': '') + st;
57776                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
57777             });
57778             
57779             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
57780             
57781             
57782             if(format && useF){
57783                 
57784                 args = args ? ',' + args : "";
57785                  
57786                 if(format.substr(0, 5) != "this."){
57787                     format = "fm." + format + '(';
57788                 }else{
57789                     format = 'this.call("'+ format.substr(5) + '", ';
57790                     args = ", values";
57791                 }
57792                 
57793                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
57794             }
57795              
57796             if (args.length) {
57797                 // called with xxyx.yuu:(test,test)
57798                 // change to ()
57799                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
57800             }
57801             // raw.. - :raw modifier..
57802             return "'"+ sep + udef_st  + name + ")"+sep+"'";
57803             
57804         };
57805         var body;
57806         // branched to use + in gecko and [].join() in others
57807         if(Roo.isGecko){
57808             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
57809                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
57810                     "';};};";
57811         }else{
57812             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
57813             body.push(tpl.body.replace(/(\r\n|\n)/g,
57814                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
57815             body.push("'].join('');};};");
57816             body = body.join('');
57817         }
57818         
57819         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
57820        
57821         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
57822         eval(body);
57823         
57824         return this;
57825     },
57826
57827     applyTemplate : function(values){
57828         return this.master.compiled.call(this, values, {});
57829         //var s = this.subs;
57830     },
57831
57832     apply : function(){
57833         return this.applyTemplate.apply(this, arguments);
57834     }
57835
57836  });
57837
57838 Roo.XTemplate.from = function(el){
57839     el = Roo.getDom(el);
57840     return new Roo.XTemplate(el.value || el.innerHTML);
57841 };